|  | /* | 
|  | * Copyright (C) 2015-2018 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. | 
|  | */ | 
|  |  | 
|  | #pragma once | 
|  |  | 
|  | #if ENABLE(B3_JIT) | 
|  |  | 
|  | #include "FPRInfo.h" | 
|  | #include "GPRInfo.h" | 
|  | #include "JSCJSValue.h" | 
|  | #include "Reg.h" | 
|  | #include "RegisterSet.h" | 
|  | #include "ValueRecovery.h" | 
|  | #include <wtf/PrintStream.h> | 
|  | #if ENABLE(WEBASSEMBLY) | 
|  | #include "WasmValueLocation.h" | 
|  | #endif | 
|  |  | 
|  | namespace JSC { | 
|  |  | 
|  | class AssemblyHelpers; | 
|  |  | 
|  | namespace B3 { | 
|  |  | 
|  | // We use this class to describe value representations at stackmaps. It's used both to force a | 
|  | // representation and to get the representation. When the B3 client forces a representation, we say | 
|  | // that it's an input. When B3 tells the client what representation it picked, we say that it's an | 
|  | // output. | 
|  |  | 
|  | class ValueRep { | 
|  | WTF_MAKE_FAST_ALLOCATED; | 
|  | public: | 
|  | enum Kind : uint8_t { | 
|  | // As an input representation, this means that B3 can pick any representation. As an output | 
|  | // representation, this means that we don't know. This will only arise as an output | 
|  | // representation for the active arguments of Check/CheckAdd/CheckSub/CheckMul. | 
|  | WarmAny, | 
|  |  | 
|  | // Same as WarmAny, but implies that the use is cold. A cold use is not counted as a use for | 
|  | // computing the priority of the used temporary. | 
|  | ColdAny, | 
|  |  | 
|  | // Same as ColdAny, but also implies that the use occurs after all other effects of the stackmap | 
|  | // value. | 
|  | LateColdAny, | 
|  |  | 
|  | // As an input representation, this means that B3 should pick some register. It could be a | 
|  | // register that this claims to clobber! | 
|  | SomeRegister, | 
|  |  | 
|  | // As an input representation, this means that B3 should pick some register but that this | 
|  | // register is then cobbered with garbage. This only works for patchpoints. | 
|  | SomeRegisterWithClobber, | 
|  |  | 
|  | // As an input representation, this tells us that B3 should pick some register, but implies | 
|  | // that the def happens before any of the effects of the stackmap. This is only valid for | 
|  | // the result constraint of a Patchpoint. | 
|  | SomeEarlyRegister, | 
|  |  | 
|  | // As an input representation, this tells us that B3 should pick some register, but implies | 
|  | // the use happens after any defs. This is only works for patchpoints. | 
|  | SomeLateRegister, | 
|  |  | 
|  | // As an input representation, this forces a particular register. As an output | 
|  | // representation, this tells us what register B3 picked. | 
|  | Register, | 
|  |  | 
|  | // As an input representation, this forces a particular register and states that | 
|  | // the register is used late. This means that the register is used after the result | 
|  | // is defined (i.e, the result will interfere with this as an input). | 
|  | // It's not a valid output representation. | 
|  | LateRegister, | 
|  |  | 
|  | // As an output representation, this tells us what stack slot B3 picked. It's not a valid | 
|  | // input representation. | 
|  | Stack, | 
|  |  | 
|  | // As an input representation, this forces the value to end up in the argument area at some | 
|  | // offset. As an output representation this tells us what offset from SP B3 picked. | 
|  | StackArgument, | 
|  |  | 
|  | // As an output representation, this tells us that B3 constant-folded the value. | 
|  | Constant | 
|  | }; | 
|  |  | 
|  | ValueRep() | 
|  | : m_kind(WarmAny) | 
|  | { | 
|  | } | 
|  |  | 
|  | explicit ValueRep(Reg reg) | 
|  | : m_kind(Register) | 
|  | { | 
|  | u.reg = reg; | 
|  | } | 
|  |  | 
|  | ValueRep(Kind kind) | 
|  | : m_kind(kind) | 
|  | { | 
|  | ASSERT(kind == WarmAny || kind == ColdAny || kind == LateColdAny || kind == SomeRegister || kind == SomeRegisterWithClobber || kind == SomeEarlyRegister || kind == SomeLateRegister); | 
|  | } | 
|  |  | 
|  | #if ENABLE(WEBASSEMBLY) | 
|  | ValueRep(Wasm::ValueLocation location) | 
|  | { | 
|  | switch (location.kind()) { | 
|  | case Wasm::ValueLocation::Kind::Register: | 
|  | m_kind = Register; | 
|  | u.reg = location.reg(); | 
|  | break; | 
|  | case Wasm::ValueLocation::Kind::Stack: | 
|  | m_kind = Stack; | 
|  | u.offsetFromFP = location.offsetFromFP(); | 
|  | break; | 
|  | case Wasm::ValueLocation::Kind::StackArgument: | 
|  | m_kind = StackArgument; | 
|  | u.offsetFromSP = location.offsetFromSP(); | 
|  | break; | 
|  | default: | 
|  | ASSERT_NOT_REACHED(); | 
|  | } | 
|  | } | 
|  | #endif | 
|  |  | 
|  | static ValueRep reg(Reg reg) | 
|  | { | 
|  | return ValueRep(reg); | 
|  | } | 
|  |  | 
|  | static ValueRep lateReg(Reg reg) | 
|  | { | 
|  | ValueRep result(reg); | 
|  | result.m_kind = LateRegister; | 
|  | return result; | 
|  | } | 
|  |  | 
|  | static ValueRep stack(intptr_t offsetFromFP) | 
|  | { | 
|  | ValueRep result; | 
|  | result.m_kind = Stack; | 
|  | result.u.offsetFromFP = offsetFromFP; | 
|  | return result; | 
|  | } | 
|  |  | 
|  | static ValueRep stackArgument(intptr_t offsetFromSP) | 
|  | { | 
|  | ValueRep result; | 
|  | result.m_kind = StackArgument; | 
|  | result.u.offsetFromSP = offsetFromSP; | 
|  | return result; | 
|  | } | 
|  |  | 
|  | static ValueRep constant(int64_t value) | 
|  | { | 
|  | ValueRep result; | 
|  | result.m_kind = Constant; | 
|  | result.u.value = value; | 
|  | return result; | 
|  | } | 
|  |  | 
|  | static ValueRep constantDouble(double value) | 
|  | { | 
|  | return ValueRep::constant(bitwise_cast<int64_t>(value)); | 
|  | } | 
|  |  | 
|  | static ValueRep constantFloat(float value) | 
|  | { | 
|  | return ValueRep::constant(static_cast<uint64_t>(bitwise_cast<uint32_t>(value))); | 
|  | } | 
|  |  | 
|  | Kind kind() const { return m_kind; } | 
|  |  | 
|  | bool operator==(const ValueRep& other) const | 
|  | { | 
|  | if (kind() != other.kind()) | 
|  | return false; | 
|  | switch (kind()) { | 
|  | case LateRegister: | 
|  | case Register: | 
|  | return u.reg == other.u.reg; | 
|  | case Stack: | 
|  | return u.offsetFromFP == other.u.offsetFromFP; | 
|  | case StackArgument: | 
|  | return u.offsetFromSP == other.u.offsetFromSP; | 
|  | case Constant: | 
|  | return u.value == other.u.value; | 
|  | default: | 
|  | return true; | 
|  | } | 
|  | } | 
|  |  | 
|  | bool operator!=(const ValueRep& other) const | 
|  | { | 
|  | return !(*this == other); | 
|  | } | 
|  |  | 
|  | explicit operator bool() const { return kind() != WarmAny; } | 
|  |  | 
|  | bool isAny() const { return kind() == WarmAny || kind() == ColdAny || kind() == LateColdAny; } | 
|  |  | 
|  | bool isReg() const { return kind() == Register || kind() == LateRegister || kind() == SomeLateRegister; } | 
|  |  | 
|  | Reg reg() const | 
|  | { | 
|  | ASSERT(isReg()); | 
|  | return u.reg; | 
|  | } | 
|  |  | 
|  | bool isGPR() const { return isReg() && reg().isGPR(); } | 
|  | bool isFPR() const { return isReg() && reg().isFPR(); } | 
|  |  | 
|  | GPRReg gpr() const { return reg().gpr(); } | 
|  | FPRReg fpr() const { return reg().fpr(); } | 
|  |  | 
|  | bool isStack() const { return kind() == Stack; } | 
|  |  | 
|  | intptr_t offsetFromFP() const | 
|  | { | 
|  | ASSERT(isStack()); | 
|  | return u.offsetFromFP; | 
|  | } | 
|  |  | 
|  | bool isStackArgument() const { return kind() == StackArgument; } | 
|  |  | 
|  | intptr_t offsetFromSP() const | 
|  | { | 
|  | ASSERT(isStackArgument()); | 
|  | return u.offsetFromSP; | 
|  | } | 
|  |  | 
|  | bool isConstant() const { return kind() == Constant; } | 
|  |  | 
|  | int64_t value() const | 
|  | { | 
|  | ASSERT(isConstant()); | 
|  | return u.value; | 
|  | } | 
|  |  | 
|  | double doubleValue() const | 
|  | { | 
|  | return bitwise_cast<double>(value()); | 
|  | } | 
|  |  | 
|  | float floatValue() const | 
|  | { | 
|  | return bitwise_cast<float>(static_cast<uint32_t>(static_cast<uint64_t>(value()))); | 
|  | } | 
|  |  | 
|  | ValueRep withOffset(intptr_t offset) const | 
|  | { | 
|  | switch (kind()) { | 
|  | case Stack: | 
|  | return stack(offsetFromFP() + offset); | 
|  | case StackArgument: | 
|  | return stackArgument(offsetFromSP() + offset); | 
|  | default: | 
|  | return *this; | 
|  | } | 
|  | } | 
|  |  | 
|  | void addUsedRegistersTo(RegisterSet&) const; | 
|  |  | 
|  | RegisterSet usedRegisters() const; | 
|  |  | 
|  | // Get the used registers for a vector of ValueReps. | 
|  | template<typename VectorType> | 
|  | static RegisterSet usedRegisters(const VectorType& vector) | 
|  | { | 
|  | RegisterSet result; | 
|  | for (const ValueRep& value : vector) | 
|  | value.addUsedRegistersTo(result); | 
|  | return result; | 
|  | } | 
|  |  | 
|  | JS_EXPORT_PRIVATE void dump(PrintStream&) const; | 
|  |  | 
|  | // This has a simple contract: it emits code to restore the value into the given register. This | 
|  | // will work even if it requires moving between bits a GPR and a FPR. | 
|  | void emitRestore(AssemblyHelpers&, Reg) const; | 
|  |  | 
|  | // Computes the ValueRecovery assuming that the Value* was for a JSValue (i.e. Int64). | 
|  | // NOTE: We should avoid putting JSValue-related methods in B3, but this was hard to avoid | 
|  | // because some parts of JSC use ValueRecovery like a general "where my bits at" object, almost | 
|  | // exactly like ValueRep. | 
|  | ValueRecovery recoveryForJSValue() const; | 
|  |  | 
|  | private: | 
|  | union U { | 
|  | Reg reg; | 
|  | intptr_t offsetFromFP; | 
|  | intptr_t offsetFromSP; | 
|  | int64_t value; | 
|  |  | 
|  | U() | 
|  | { | 
|  | memset(static_cast<void*>(this), 0, sizeof(*this)); | 
|  | } | 
|  | } u; | 
|  | Kind m_kind; | 
|  | }; | 
|  |  | 
|  | } } // namespace JSC::B3 | 
|  |  | 
|  | namespace WTF { | 
|  |  | 
|  | void printInternal(PrintStream&, JSC::B3::ValueRep::Kind); | 
|  |  | 
|  | } // namespace WTF | 
|  |  | 
|  | #endif // ENABLE(B3_JIT) |