| /* | 
 |  * Copyright (C) 2015-2017 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 = "@"; | 
 | 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) |