/* * Copyright (C) 2011-2019 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 #include "DFGMinifiedID.h" #include "DataFormat.h" #if ENABLE(JIT) #include "GPRInfo.h" #include "FPRInfo.h" #include "Reg.h" #endif #include "JSCJSValue.h" #include "MacroAssembler.h" #include "VirtualRegister.h" namespace JSC { struct DumpContext; struct InlineCallFrame; // Describes how to recover a given bytecode virtual register at a given // code point. enum ValueRecoveryTechnique : uint8_t { // It's in a register. InGPR, UnboxedInt32InGPR, UnboxedInt52InGPR, UnboxedStrictInt52InGPR, UnboxedBooleanInGPR, UnboxedCellInGPR, #if USE(JSVALUE32_64) InPair, #endif InFPR, UnboxedDoubleInFPR, // It's in the stack, but at a different location. DisplacedInJSStack, // It's in the stack, at a different location, and it's unboxed. Int32DisplacedInJSStack, Int52DisplacedInJSStack, StrictInt52DisplacedInJSStack, DoubleDisplacedInJSStack, CellDisplacedInJSStack, BooleanDisplacedInJSStack, // It's an Arguments object. This arises because of the simplified arguments simplification done by the DFG. DirectArgumentsThatWereNotCreated, ClonedArgumentsThatWereNotCreated, // It's a constant. Constant, // Don't know how to recover it. DontKnow }; class ValueRecovery { public: ValueRecovery() : m_technique(DontKnow) { } bool isSet() const { return m_technique != DontKnow; } bool operator!() const { return !isSet(); } #if ENABLE(JIT) static ValueRecovery inRegister(Reg reg, DataFormat dataFormat) { if (reg.isGPR()) return inGPR(reg.gpr(), dataFormat); ASSERT(reg.isFPR()); return inFPR(reg.fpr(), dataFormat); } #endif explicit operator bool() const { return isSet(); } static ValueRecovery inGPR(MacroAssembler::RegisterID gpr, DataFormat dataFormat) { ASSERT(dataFormat != DataFormatNone); #if USE(JSVALUE32_64) ASSERT(dataFormat == DataFormatInt32 || dataFormat == DataFormatCell || dataFormat == DataFormatBoolean); #endif ValueRecovery result; if (dataFormat == DataFormatInt32) result.m_technique = UnboxedInt32InGPR; else if (dataFormat == DataFormatInt52) result.m_technique = UnboxedInt52InGPR; else if (dataFormat == DataFormatStrictInt52) result.m_technique = UnboxedStrictInt52InGPR; else if (dataFormat == DataFormatBoolean) result.m_technique = UnboxedBooleanInGPR; else if (dataFormat == DataFormatCell) result.m_technique = UnboxedCellInGPR; else result.m_technique = InGPR; UnionType u; u.gpr = gpr; result.m_source = WTFMove(u); return result; } #if USE(JSVALUE32_64) static ValueRecovery inPair(MacroAssembler::RegisterID tagGPR, MacroAssembler::RegisterID payloadGPR) { ValueRecovery result; result.m_technique = InPair; UnionType u; u.pair.tagGPR = tagGPR; u.pair.payloadGPR = payloadGPR; result.m_source = WTFMove(u); return result; } #endif static ValueRecovery inFPR(MacroAssembler::FPRegisterID fpr, DataFormat dataFormat) { ASSERT(dataFormat == DataFormatDouble || dataFormat & DataFormatJS); ValueRecovery result; if (dataFormat == DataFormatDouble) result.m_technique = UnboxedDoubleInFPR; else result.m_technique = InFPR; UnionType u; u.fpr = fpr; result.m_source = WTFMove(u); return result; } static ValueRecovery displacedInJSStack(VirtualRegister virtualReg, DataFormat dataFormat) { ValueRecovery result; switch (dataFormat) { case DataFormatInt32: result.m_technique = Int32DisplacedInJSStack; break; case DataFormatInt52: result.m_technique = Int52DisplacedInJSStack; break; case DataFormatStrictInt52: result.m_technique = StrictInt52DisplacedInJSStack; break; case DataFormatDouble: result.m_technique = DoubleDisplacedInJSStack; break; case DataFormatCell: result.m_technique = CellDisplacedInJSStack; break; case DataFormatBoolean: result.m_technique = BooleanDisplacedInJSStack; break; default: ASSERT(dataFormat != DataFormatNone && dataFormat != DataFormatStorage); result.m_technique = DisplacedInJSStack; break; } UnionType u; u.virtualReg = virtualReg.offset(); result.m_source = WTFMove(u); return result; } static ValueRecovery constant(JSValue value) { ValueRecovery result; result.m_technique = Constant; UnionType u; u.constant = JSValue::encode(value); result.m_source = WTFMove(u); return result; } static ValueRecovery directArgumentsThatWereNotCreated(DFG::MinifiedID id) { ValueRecovery result; result.m_technique = DirectArgumentsThatWereNotCreated; UnionType u; u.nodeID = id.bits(); result.m_source = WTFMove(u); return result; } static ValueRecovery clonedArgumentsThatWereNotCreated(DFG::MinifiedID id) { ValueRecovery result; result.m_technique = ClonedArgumentsThatWereNotCreated; UnionType u; u.nodeID = id.bits(); result.m_source = WTFMove(u); return result; } ValueRecoveryTechnique technique() const { return m_technique; } bool isConstant() const { return m_technique == Constant; } bool isInGPR() const { switch (m_technique) { case InGPR: case UnboxedInt32InGPR: case UnboxedBooleanInGPR: case UnboxedCellInGPR: case UnboxedInt52InGPR: case UnboxedStrictInt52InGPR: return true; default: return false; } } bool isInFPR() const { switch (m_technique) { case InFPR: case UnboxedDoubleInFPR: return true; default: return false; } } bool isInRegisters() const { return isInJSValueRegs() || isInGPR() || isInFPR(); } bool isInJSStack() const { switch (m_technique) { case DisplacedInJSStack: case Int32DisplacedInJSStack: case Int52DisplacedInJSStack: case StrictInt52DisplacedInJSStack: case DoubleDisplacedInJSStack: case CellDisplacedInJSStack: case BooleanDisplacedInJSStack: return true; default: return false; } } DataFormat dataFormat() const { switch (m_technique) { case InGPR: case InFPR: case DisplacedInJSStack: case Constant: #if USE(JSVALUE32_64) case InPair: #endif return DataFormatJS; case UnboxedInt32InGPR: case Int32DisplacedInJSStack: return DataFormatInt32; case UnboxedInt52InGPR: case Int52DisplacedInJSStack: return DataFormatInt52; case UnboxedStrictInt52InGPR: case StrictInt52DisplacedInJSStack: return DataFormatStrictInt52; case UnboxedBooleanInGPR: case BooleanDisplacedInJSStack: return DataFormatBoolean; case UnboxedCellInGPR: case CellDisplacedInJSStack: return DataFormatCell; case UnboxedDoubleInFPR: case DoubleDisplacedInJSStack: return DataFormatDouble; default: return DataFormatNone; } } MacroAssembler::RegisterID gpr() const { ASSERT(isInGPR()); return m_source.get().gpr; } #if USE(JSVALUE32_64) MacroAssembler::RegisterID tagGPR() const { ASSERT(m_technique == InPair); return m_source.get().pair.tagGPR; } MacroAssembler::RegisterID payloadGPR() const { ASSERT(m_technique == InPair); return m_source.get().pair.payloadGPR; } bool isInJSValueRegs() const { return m_technique == InPair; } #if ENABLE(JIT) JSValueRegs jsValueRegs() const { ASSERT(isInJSValueRegs()); return JSValueRegs(tagGPR(), payloadGPR()); } #endif // ENABLE(JIT) #else bool isInJSValueRegs() const { return isInGPR(); } #endif // USE(JSVALUE32_64) MacroAssembler::FPRegisterID fpr() const { ASSERT(isInFPR()); return m_source.get().fpr; } VirtualRegister virtualRegister() const { ASSERT(isInJSStack()); return VirtualRegister(m_source.get().virtualReg); } ValueRecovery withLocalsOffset(int offset) const { switch (m_technique) { case DisplacedInJSStack: case Int32DisplacedInJSStack: case DoubleDisplacedInJSStack: case CellDisplacedInJSStack: case BooleanDisplacedInJSStack: case Int52DisplacedInJSStack: case StrictInt52DisplacedInJSStack: { ValueRecovery result; result.m_technique = m_technique; UnionType u; u.virtualReg = m_source.get().virtualReg + offset; result.m_source = WTFMove(u); return result; } default: return *this; } } JSValue constant() const { ASSERT(isConstant()); return JSValue::decode(m_source.get().constant); } DFG::MinifiedID nodeID() const { ASSERT(m_technique == DirectArgumentsThatWereNotCreated || m_technique == ClonedArgumentsThatWereNotCreated); return DFG::MinifiedID::fromBits(m_source.get().nodeID); } JSValue recover(CallFrame*) const; #if ENABLE(JIT) template void forEachReg(const Func& func) { switch (m_technique) { case InGPR: case UnboxedInt32InGPR: case UnboxedBooleanInGPR: case UnboxedCellInGPR: case UnboxedInt52InGPR: case UnboxedStrictInt52InGPR: func(gpr()); return; case InFPR: case UnboxedDoubleInFPR: func(fpr()); return; #if USE(JSVALUE32_64) case InPair: func(jsValueRegs().payloadGPR()); func(jsValueRegs().tagGPR()); return; #endif default: return; } } void dumpInContext(PrintStream& out, DumpContext* context) const; void dump(PrintStream& out) const; #endif private: ValueRecoveryTechnique m_technique; union UnionType { MacroAssembler::RegisterID gpr; MacroAssembler::FPRegisterID fpr; #if USE(JSVALUE32_64) struct { MacroAssembler::RegisterID tagGPR; MacroAssembler::RegisterID payloadGPR; } pair; #endif int virtualReg; EncodedJSValue constant; unsigned nodeID; }; Packed m_source; }; static_assert(alignof(ValueRecovery) == 1); } // namespace JSC