blob: 4dd5cb866ad73e21ba2da437f3d01c7de5ba4c4d [file] [log] [blame]
/*
* Copyright (C) 2015-2020 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "B3Value.h"
#if ENABLE(B3_JIT)
#include "B3ArgumentRegValue.h"
#include "B3AtomicValue.h"
#include "B3BasicBlockInlines.h"
#include "B3BottomProvider.h"
#include "B3CCallValue.h"
#include "B3FenceValue.h"
#include "B3MemoryValue.h"
#include "B3OriginDump.h"
#include "B3ProcedureInlines.h"
#include "B3SlotBaseValue.h"
#include "B3StackSlot.h"
#include "B3UpsilonValue.h"
#include "B3ValueInlines.h"
#include "B3ValueKeyInlines.h"
#include "B3VariableValue.h"
#include "B3WasmBoundsCheckValue.h"
#include <wtf/CommaPrinter.h>
#include <wtf/ListDump.h>
#include <wtf/StringPrintStream.h>
#include <wtf/Vector.h>
namespace JSC { namespace B3 {
const char* const Value::dumpPrefix = "b@";
void DeepValueDump::dump(PrintStream& out) const
{
if (m_value)
m_value->deepDump(m_proc, out);
else
out.print("<null>");
}
Value::~Value()
{
if (m_numChildren == VarArgs)
bitwise_cast<Vector<Value*, 3> *>(childrenAlloc())->Vector<Value*, 3>::~Vector();
}
void Value::replaceWithIdentity(Value* value)
{
// This is a bit crazy. It does an in-place replacement of whatever Value subclass this is with
// a plain Identity Value. We first collect all of the information we need, then we destruct the
// previous value in place, and then we construct the Identity Value in place.
RELEASE_ASSERT(m_type == value->m_type);
ASSERT(value != this);
if (m_type == Void)
replaceWithNopIgnoringType();
else
replaceWith(Identity, m_type, this->owner, value);
}
void Value::replaceWithBottom(InsertionSet& insertionSet, size_t index)
{
replaceWithBottom(BottomProvider(insertionSet, index));
}
void Value::replaceWithNop()
{
RELEASE_ASSERT(m_type == Void);
replaceWithNopIgnoringType();
}
void Value::replaceWithNopIgnoringType()
{
replaceWith(Nop, Void, this->owner);
}
void Value::replaceWithPhi()
{
if (m_type == Void) {
replaceWithNop();
return;
}
replaceWith(Phi, m_type, this->owner);
}
void Value::replaceWithJump(BasicBlock* owner, FrequentedBlock target)
{
RELEASE_ASSERT(owner->last() == this);
replaceWith(Jump, Void, this->owner);
owner->setSuccessors(target);
}
void Value::replaceWithOops(BasicBlock* owner)
{
RELEASE_ASSERT(owner->last() == this);
replaceWith(Oops, Void, this->owner);
owner->clearSuccessors();
}
void Value::replaceWithJump(FrequentedBlock target)
{
replaceWithJump(owner, target);
}
void Value::replaceWithOops()
{
replaceWithOops(owner);
}
void Value::replaceWith(Kind kind, Type type, BasicBlock* owner)
{
unsigned index = m_index;
this->~Value();
new (this) Value(kind, type, m_origin);
this->m_index = index;
this->owner = owner;
}
void Value::replaceWith(Kind kind, Type type, BasicBlock* owner, Value* value)
{
unsigned index = m_index;
this->~Value();
new (this) Value(kind, type, m_origin, value);
this->m_index = index;
this->owner = owner;
}
void Value::dump(PrintStream& out) const
{
bool isConstant = false;
switch (opcode()) {
case Const32:
out.print("$", asInt32(), "(");
isConstant = true;
break;
case Const64:
out.print("$", asInt64(), "(");
isConstant = true;
break;
case ConstFloat:
out.print("$", asFloat(), "(");
isConstant = true;
break;
case ConstDouble:
out.print("$", asDouble(), "(");
isConstant = true;
break;
default:
break;
}
out.print(dumpPrefix, m_index);
if (isConstant)
out.print(")");
}
void Value::dumpChildren(CommaPrinter& comma, PrintStream& out) const
{
for (Value* child : children())
out.print(comma, pointerDump(child));
}
void Value::deepDump(const Procedure* proc, PrintStream& out) const
{
out.print(m_type, " ", dumpPrefix, m_index, " = ", m_kind);
out.print("(");
CommaPrinter comma;
dumpChildren(comma, out);
dumpMeta(comma, out);
{
CString string = toCString(effects());
if (string.length())
out.print(comma, string);
}
if (m_origin)
out.print(comma, OriginDump(proc, m_origin));
out.print(")");
}
void Value::dumpSuccessors(const BasicBlock* block, PrintStream& out) const
{
// Note that this must not crash if we have the wrong number of successors, since someone
// debugging a number-of-successors bug will probably want to dump IR!
if (opcode() == Branch && block->numSuccessors() == 2) {
out.print("Then:", block->taken(), ", Else:", block->notTaken());
return;
}
out.print(listDump(block->successors()));
}
Value* Value::negConstant(Procedure&) const
{
return nullptr;
}
Value* Value::addConstant(Procedure&, int32_t) const
{
return nullptr;
}
Value* Value::addConstant(Procedure&, const Value*) const
{
return nullptr;
}
Value* Value::subConstant(Procedure&, const Value*) const
{
return nullptr;
}
Value* Value::mulConstant(Procedure&, const Value*) const
{
return nullptr;
}
Value* Value::checkAddConstant(Procedure&, const Value*) const
{
return nullptr;
}
Value* Value::checkSubConstant(Procedure&, const Value*) const
{
return nullptr;
}
Value* Value::checkMulConstant(Procedure&, const Value*) const
{
return nullptr;
}
Value* Value::checkNegConstant(Procedure&) const
{
return nullptr;
}
Value* Value::divConstant(Procedure&, const Value*) const
{
return nullptr;
}
Value* Value::uDivConstant(Procedure&, const Value*) const
{
return nullptr;
}
Value* Value::modConstant(Procedure&, const Value*) const
{
return nullptr;
}
Value* Value::uModConstant(Procedure&, const Value*) const
{
return nullptr;
}
Value* Value::bitAndConstant(Procedure&, const Value*) const
{
return nullptr;
}
Value* Value::bitOrConstant(Procedure&, const Value*) const
{
return nullptr;
}
Value* Value::bitXorConstant(Procedure&, const Value*) const
{
return nullptr;
}
Value* Value::shlConstant(Procedure&, const Value*) const
{
return nullptr;
}
Value* Value::sShrConstant(Procedure&, const Value*) const
{
return nullptr;
}
Value* Value::zShrConstant(Procedure&, const Value*) const
{
return nullptr;
}
Value* Value::rotRConstant(Procedure&, const Value*) const
{
return nullptr;
}
Value* Value::rotLConstant(Procedure&, const Value*) const
{
return nullptr;
}
Value* Value::bitwiseCastConstant(Procedure&) const
{
return nullptr;
}
Value* Value::iToDConstant(Procedure&) const
{
return nullptr;
}
Value* Value::iToFConstant(Procedure&) const
{
return nullptr;
}
Value* Value::doubleToFloatConstant(Procedure&) const
{
return nullptr;
}
Value* Value::floatToDoubleConstant(Procedure&) const
{
return nullptr;
}
Value* Value::absConstant(Procedure&) const
{
return nullptr;
}
Value* Value::ceilConstant(Procedure&) const
{
return nullptr;
}
Value* Value::floorConstant(Procedure&) const
{
return nullptr;
}
Value* Value::sqrtConstant(Procedure&) const
{
return nullptr;
}
TriState Value::equalConstant(const Value*) const
{
return MixedTriState;
}
TriState Value::notEqualConstant(const Value*) const
{
return MixedTriState;
}
TriState Value::lessThanConstant(const Value*) const
{
return MixedTriState;
}
TriState Value::greaterThanConstant(const Value*) const
{
return MixedTriState;
}
TriState Value::lessEqualConstant(const Value*) const
{
return MixedTriState;
}
TriState Value::greaterEqualConstant(const Value*) const
{
return MixedTriState;
}
TriState Value::aboveConstant(const Value*) const
{
return MixedTriState;
}
TriState Value::belowConstant(const Value*) const
{
return MixedTriState;
}
TriState Value::aboveEqualConstant(const Value*) const
{
return MixedTriState;
}
TriState Value::belowEqualConstant(const Value*) const
{
return MixedTriState;
}
TriState Value::equalOrUnorderedConstant(const Value*) const
{
return MixedTriState;
}
Value* Value::invertedCompare(Procedure& proc) const
{
if (numChildren() != 2)
return nullptr;
if (Optional<Opcode> invertedOpcode = B3::invertedCompare(opcode(), child(0)->type())) {
ASSERT(!kind().hasExtraBits());
return proc.add<Value>(*invertedOpcode, type(), origin(), child(0), child(1));
}
return nullptr;
}
bool Value::isRounded() const
{
ASSERT(type().isFloat());
switch (opcode()) {
case Floor:
case Ceil:
case IToD:
case IToF:
return true;
case ConstDouble: {
double value = asDouble();
return std::isfinite(value) && value == ceil(value);
}
case ConstFloat: {
float value = asFloat();
return std::isfinite(value) && value == ceilf(value);
}
default:
return false;
}
}
bool Value::returnsBool() const
{
if (type() != Int32)
return false;
switch (opcode()) {
case Const32:
return asInt32() == 0 || asInt32() == 1;
case BitAnd:
return child(0)->returnsBool() || child(1)->returnsBool();
case BitOr:
case BitXor:
return child(0)->returnsBool() && child(1)->returnsBool();
case Select:
return child(1)->returnsBool() && child(2)->returnsBool();
case Identity:
return child(0)->returnsBool();
case Equal:
case NotEqual:
case LessThan:
case GreaterThan:
case LessEqual:
case GreaterEqual:
case Above:
case Below:
case AboveEqual:
case BelowEqual:
case EqualOrUnordered:
case AtomicWeakCAS:
return true;
case Phi:
// FIXME: We should have a story here.
// https://bugs.webkit.org/show_bug.cgi?id=150725
return false;
default:
return false;
}
}
TriState Value::asTriState() const
{
switch (opcode()) {
case Const32:
return triState(!!asInt32());
case Const64:
return triState(!!asInt64());
case ConstDouble:
// Use "!= 0" to really emphasize what this mean with respect to NaN and such.
return triState(asDouble() != 0);
case ConstFloat:
return triState(asFloat() != 0.);
default:
return MixedTriState;
}
}
Effects Value::effects() const
{
Effects result;
switch (opcode()) {
case Nop:
case Identity:
case Opaque:
case Const32:
case Const64:
case ConstDouble:
case ConstFloat:
case SlotBase:
case ArgumentReg:
case FramePointer:
case Add:
case Sub:
case Mul:
case Neg:
case BitAnd:
case BitOr:
case BitXor:
case Shl:
case SShr:
case ZShr:
case RotR:
case RotL:
case Clz:
case Abs:
case Ceil:
case Floor:
case Sqrt:
case BitwiseCast:
case SExt8:
case SExt16:
case SExt32:
case ZExt32:
case Trunc:
case IToD:
case IToF:
case FloatToDouble:
case DoubleToFloat:
case Equal:
case NotEqual:
case LessThan:
case GreaterThan:
case LessEqual:
case GreaterEqual:
case Above:
case Below:
case AboveEqual:
case BelowEqual:
case EqualOrUnordered:
case Select:
case Depend:
case Extract:
break;
case Div:
case UDiv:
case Mod:
case UMod:
result.controlDependent = true;
break;
case Load8Z:
case Load8S:
case Load16Z:
case Load16S:
case Load: {
const MemoryValue* memory = as<MemoryValue>();
result.reads = memory->range();
if (memory->hasFence()) {
result.writes = memory->fenceRange();
result.fence = true;
}
result.controlDependent = true;
break;
}
case Store8:
case Store16:
case Store: {
const MemoryValue* memory = as<MemoryValue>();
result.writes = memory->range();
if (memory->hasFence()) {
result.reads = memory->fenceRange();
result.fence = true;
}
result.controlDependent = true;
break;
}
case AtomicWeakCAS:
case AtomicStrongCAS:
case AtomicXchgAdd:
case AtomicXchgAnd:
case AtomicXchgOr:
case AtomicXchgSub:
case AtomicXchgXor:
case AtomicXchg: {
const AtomicValue* atomic = as<AtomicValue>();
result.reads = atomic->range() | atomic->fenceRange();
result.writes = atomic->range() | atomic->fenceRange();
if (atomic->hasFence())
result.fence = true;
result.controlDependent = true;
break;
}
case WasmAddress:
result.readsPinned = true;
break;
case Fence: {
const FenceValue* fence = as<FenceValue>();
result.reads = fence->read;
result.writes = fence->write;
result.fence = true;
break;
}
case CCall:
result = as<CCallValue>()->effects;
break;
case Patchpoint:
result = as<PatchpointValue>()->effects;
break;
case CheckAdd:
case CheckSub:
case CheckMul:
case Check:
result = Effects::forCheck();
break;
case WasmBoundsCheck:
switch (as<WasmBoundsCheckValue>()->boundsType()) {
case WasmBoundsCheckValue::Type::Pinned:
result.readsPinned = true;
break;
case WasmBoundsCheckValue::Type::Maximum:
break;
}
result.exitsSideways = true;
break;
case Upsilon:
case Set:
result.writesLocalState = true;
break;
case Phi:
case Get:
result.readsLocalState = true;
break;
case Jump:
case Branch:
case Switch:
case Return:
case Oops:
case EntrySwitch:
result.terminal = true;
break;
}
if (traps()) {
result.exitsSideways = true;
result.reads = HeapRange::top();
}
return result;
}
ValueKey Value::key() const
{
// NOTE: Except for exotic things like CheckAdd and friends, we want every case here to have a
// corresponding case in ValueKey::materialize().
switch (opcode()) {
case FramePointer:
return ValueKey(kind(), type());
case Identity:
case Opaque:
case Abs:
case Ceil:
case Floor:
case Sqrt:
case SExt8:
case SExt16:
case SExt32:
case ZExt32:
case Clz:
case Trunc:
case IToD:
case IToF:
case FloatToDouble:
case DoubleToFloat:
case Check:
case BitwiseCast:
case Neg:
case Depend:
return ValueKey(kind(), type(), child(0));
case Add:
case Sub:
case Mul:
case Div:
case UDiv:
case Mod:
case UMod:
case BitAnd:
case BitOr:
case BitXor:
case Shl:
case SShr:
case ZShr:
case RotR:
case RotL:
case Equal:
case NotEqual:
case LessThan:
case GreaterThan:
case Above:
case Below:
case AboveEqual:
case BelowEqual:
case EqualOrUnordered:
case CheckAdd:
case CheckSub:
case CheckMul:
return ValueKey(kind(), type(), child(0), child(1));
case Select:
return ValueKey(kind(), type(), child(0), child(1), child(2));
case Const32:
return ValueKey(Const32, type(), static_cast<int64_t>(asInt32()));
case Const64:
return ValueKey(Const64, type(), asInt64());
case ConstDouble:
return ValueKey(ConstDouble, type(), asDouble());
case ConstFloat:
return ValueKey(ConstFloat, type(), asFloat());
case ArgumentReg:
return ValueKey(
ArgumentReg, type(),
static_cast<int64_t>(as<ArgumentRegValue>()->argumentReg().index()));
case SlotBase:
return ValueKey(
SlotBase, type(),
static_cast<int64_t>(as<SlotBaseValue>()->slot()->index()));
default:
return ValueKey();
}
}
Value* Value::foldIdentity() const
{
Value* current = const_cast<Value*>(this);
while (current->opcode() == Identity)
current = current->child(0);
return current;
}
bool Value::performSubstitution()
{
bool result = false;
for (Value*& child : children()) {
if (child->opcode() == Identity) {
result = true;
child = child->foldIdentity();
}
}
return result;
}
bool Value::isFree() const
{
switch (opcode()) {
case Const32:
case Const64:
case ConstDouble:
case ConstFloat:
case Identity:
case Opaque:
case Nop:
return true;
default:
return false;
}
}
void Value::dumpMeta(CommaPrinter&, PrintStream&) const
{
}
Type Value::typeFor(Kind kind, Value* firstChild, Value* secondChild)
{
switch (kind.opcode()) {
case Identity:
case Opaque:
case Add:
case Sub:
case Mul:
case Div:
case UDiv:
case Mod:
case UMod:
case Neg:
case BitAnd:
case BitOr:
case BitXor:
case Shl:
case SShr:
case ZShr:
case RotR:
case RotL:
case Clz:
case Abs:
case Ceil:
case Floor:
case Sqrt:
case CheckAdd:
case CheckSub:
case CheckMul:
case Depend:
return firstChild->type();
case FramePointer:
return pointerType();
case SExt8:
case SExt16:
case Equal:
case NotEqual:
case LessThan:
case GreaterThan:
case LessEqual:
case GreaterEqual:
case Above:
case Below:
case AboveEqual:
case BelowEqual:
case EqualOrUnordered:
return Int32;
case Trunc:
return firstChild->type() == Int64 ? Int32 : Float;
case SExt32:
case ZExt32:
return Int64;
case FloatToDouble:
case IToD:
return Double;
case DoubleToFloat:
case IToF:
return Float;
case BitwiseCast:
switch (firstChild->type().kind()) {
case Int64:
return Double;
case Double:
return Int64;
case Int32:
return Float;
case Float:
return Int32;
case Void:
case Tuple:
ASSERT_NOT_REACHED();
}
return Void;
case Nop:
case Jump:
case Branch:
case Return:
case Oops:
case EntrySwitch:
case WasmBoundsCheck:
return Void;
case Select:
ASSERT(secondChild);
return secondChild->type();
default:
RELEASE_ASSERT_NOT_REACHED();
}
}
void Value::badKind(Kind kind, unsigned numArgs)
{
dataLog("Bad kind ", kind, " with ", numArgs, " args.\n");
RELEASE_ASSERT_NOT_REACHED();
}
} } // namespace JSC::B3
#endif // ENABLE(B3_JIT)