| /* |
| * Copyright (C) 2016-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 "GPRInfo.h" |
| #include "JSCJSValue.h" |
| #include "ResultType.h" |
| #include "TagRegistersMode.h" |
| |
| namespace JSC { |
| |
| class CCallHelpers; |
| |
| struct ObservedType { |
| constexpr ObservedType(uint8_t bits = TypeEmpty) |
| : m_bits(bits) |
| { } |
| |
| constexpr bool sawInt32() const { return m_bits & TypeInt32; } |
| constexpr bool isOnlyInt32() const { return m_bits == TypeInt32; } |
| constexpr bool sawNumber() const { return m_bits & TypeNumber; } |
| constexpr bool isOnlyNumber() const { return m_bits == TypeNumber; } |
| constexpr bool sawNonNumber() const { return m_bits & TypeNonNumber; } |
| constexpr bool isOnlyNonNumber() const { return m_bits == TypeNonNumber; } |
| constexpr bool isEmpty() const { return !m_bits; } |
| constexpr uint8_t bits() const { return m_bits; } |
| |
| constexpr ObservedType withInt32() const { return ObservedType(m_bits | TypeInt32); } |
| constexpr ObservedType withNumber() const { return ObservedType(m_bits | TypeNumber); } |
| constexpr ObservedType withNonNumber() const { return ObservedType(m_bits | TypeNonNumber); } |
| constexpr ObservedType withoutNonNumber() const { return ObservedType(m_bits & ~TypeNonNumber); } |
| |
| constexpr bool operator==(const ObservedType& other) const { return m_bits == other.m_bits; } |
| |
| static constexpr uint8_t TypeEmpty = 0x0; |
| static constexpr uint8_t TypeInt32 = 0x1; |
| static constexpr uint8_t TypeNumber = 0x02; |
| static constexpr uint8_t TypeNonNumber = 0x04; |
| |
| static constexpr uint32_t numBitsNeeded = 3; |
| |
| private: |
| uint8_t m_bits { 0 }; |
| }; |
| |
| struct ArithProfile { |
| private: |
| static constexpr uint32_t numberOfFlagBits = 6; |
| static constexpr uint32_t rhsResultTypeShift = numberOfFlagBits; |
| static constexpr uint32_t lhsResultTypeShift = rhsResultTypeShift + ResultType::numBitsNeeded; |
| static constexpr uint32_t rhsObservedTypeShift = lhsResultTypeShift + ResultType::numBitsNeeded; |
| static constexpr uint32_t lhsObservedTypeShift = rhsObservedTypeShift + ObservedType::numBitsNeeded; |
| |
| static_assert(ObservedType::numBitsNeeded == 3, "We make a hard assumption about that here."); |
| static constexpr uint32_t clearRhsObservedTypeBitMask = static_cast<uint32_t>(~((1 << rhsObservedTypeShift) | (1 << (rhsObservedTypeShift + 1)) | (1 << (rhsObservedTypeShift + 2)))); |
| static constexpr uint32_t clearLhsObservedTypeBitMask = static_cast<uint32_t>(~((1 << lhsObservedTypeShift) | (1 << (lhsObservedTypeShift + 1)) | (1 << (lhsObservedTypeShift + 2)))); |
| |
| static constexpr uint32_t resultTypeMask = (1 << ResultType::numBitsNeeded) - 1; |
| static constexpr uint32_t observedTypeMask = (1 << ObservedType::numBitsNeeded) - 1; |
| |
| enum class ConstantTag { Constant }; |
| |
| public: |
| static constexpr uint32_t specialFastPathBit = 1 << (lhsObservedTypeShift + ObservedType::numBitsNeeded); |
| static_assert((lhsObservedTypeShift + ObservedType::numBitsNeeded) <= (sizeof(uint32_t) * 8) - 1, "Should fit in a uint32_t."); |
| static_assert(!(specialFastPathBit & ~clearLhsObservedTypeBitMask), "These bits should not intersect."); |
| static_assert(specialFastPathBit & clearLhsObservedTypeBitMask, "These bits should intersect."); |
| static_assert(specialFastPathBit > ~clearLhsObservedTypeBitMask, "These bits should not intersect and specialFastPathBit should be a higher bit."); |
| |
| ArithProfile(ResultType arg) |
| : ArithProfile(ConstantTag::Constant, arg) |
| { |
| ASSERT(lhsResultType().bits() == arg.bits()); |
| ASSERT(lhsObservedType().isEmpty()); |
| ASSERT(rhsObservedType().isEmpty()); |
| } |
| |
| ArithProfile(ResultType lhs, ResultType rhs) |
| : ArithProfile(ConstantTag::Constant, lhs, rhs) |
| { |
| ASSERT(lhsResultType().bits() == lhs.bits() && rhsResultType().bits() == rhs.bits()); |
| ASSERT(lhsObservedType().isEmpty()); |
| ASSERT(rhsObservedType().isEmpty()); |
| } |
| |
| ArithProfile(OperandTypes types) |
| : ArithProfile(types.first(), types.second()) |
| { } |
| |
| ArithProfile() = default; |
| |
| static constexpr ArithProfile fromInt(uint32_t bits) |
| { |
| return ArithProfile { ConstantTag::Constant, bits }; |
| } |
| |
| static constexpr ArithProfile observedUnaryInt() |
| { |
| constexpr ObservedType observedInt32 { ObservedType().withInt32() }; |
| constexpr uint32_t bits = observedInt32.bits() << lhsObservedTypeShift; |
| static_assert(bits == 0x800000, ""); |
| return fromInt(bits); |
| } |
| static constexpr ArithProfile observedUnaryNumber() |
| { |
| constexpr ObservedType observedNumber { ObservedType().withNumber() }; |
| constexpr uint32_t bits = observedNumber.bits() << lhsObservedTypeShift; |
| static_assert(bits == 0x1000000, ""); |
| return fromInt(bits); |
| } |
| static constexpr ArithProfile observedBinaryIntInt() |
| { |
| constexpr ObservedType observedInt32 { ObservedType().withInt32() }; |
| constexpr uint32_t bits = (observedInt32.bits() << lhsObservedTypeShift) | (observedInt32.bits() << rhsObservedTypeShift); |
| static_assert(bits == 0x900000, ""); |
| return fromInt(bits); |
| } |
| static constexpr ArithProfile observedBinaryNumberInt() |
| { |
| constexpr ObservedType observedNumber { ObservedType().withNumber() }; |
| constexpr ObservedType observedInt32 { ObservedType().withInt32() }; |
| constexpr uint32_t bits = (observedNumber.bits() << lhsObservedTypeShift) | (observedInt32.bits() << rhsObservedTypeShift); |
| static_assert(bits == 0x1100000, ""); |
| return fromInt(bits); |
| } |
| static constexpr ArithProfile observedBinaryIntNumber() |
| { |
| constexpr ObservedType observedNumber { ObservedType().withNumber() }; |
| constexpr ObservedType observedInt32 { ObservedType().withInt32() }; |
| constexpr uint32_t bits = (observedInt32.bits() << lhsObservedTypeShift) | (observedNumber.bits() << rhsObservedTypeShift); |
| static_assert(bits == 0xa00000, ""); |
| return fromInt(bits); |
| } |
| static constexpr ArithProfile observedBinaryNumberNumber() |
| { |
| constexpr ObservedType observedNumber { ObservedType().withNumber() }; |
| constexpr uint32_t bits = (observedNumber.bits() << lhsObservedTypeShift) | (observedNumber.bits() << rhsObservedTypeShift); |
| static_assert(bits == 0x1200000, ""); |
| return fromInt(bits); |
| } |
| |
| enum ObservedResults { |
| NonNegZeroDouble = 1 << 0, |
| NegZeroDouble = 1 << 1, |
| NonNumeric = 1 << 2, |
| Int32Overflow = 1 << 3, |
| Int52Overflow = 1 << 4, |
| BigInt = 1 << 5, |
| }; |
| |
| ResultType lhsResultType() const { return ResultType((m_bits >> lhsResultTypeShift) & resultTypeMask); } |
| ResultType rhsResultType() const { return ResultType((m_bits >> rhsResultTypeShift) & resultTypeMask); } |
| |
| constexpr ObservedType lhsObservedType() const { return ObservedType((m_bits >> lhsObservedTypeShift) & observedTypeMask); } |
| constexpr ObservedType rhsObservedType() const { return ObservedType((m_bits >> rhsObservedTypeShift) & observedTypeMask); } |
| void setLhsObservedType(ObservedType type) |
| { |
| uint32_t bits = m_bits; |
| bits &= clearLhsObservedTypeBitMask; |
| bits |= type.bits() << lhsObservedTypeShift; |
| m_bits = bits; |
| ASSERT(lhsObservedType() == type); |
| } |
| |
| void setRhsObservedType(ObservedType type) |
| { |
| uint32_t bits = m_bits; |
| bits &= clearRhsObservedTypeBitMask; |
| bits |= type.bits() << rhsObservedTypeShift; |
| m_bits = bits; |
| ASSERT(rhsObservedType() == type); |
| } |
| |
| bool tookSpecialFastPath() const { return m_bits & specialFastPathBit; } |
| |
| bool didObserveNonInt32() const { return hasBits(NonNegZeroDouble | NegZeroDouble | NonNumeric | BigInt); } |
| bool didObserveDouble() const { return hasBits(NonNegZeroDouble | NegZeroDouble); } |
| bool didObserveNonNegZeroDouble() const { return hasBits(NonNegZeroDouble); } |
| bool didObserveNegZeroDouble() const { return hasBits(NegZeroDouble); } |
| bool didObserveNonNumeric() const { return hasBits(NonNumeric); } |
| bool didObserveBigInt() const { return hasBits(BigInt); } |
| bool didObserveInt32Overflow() const { return hasBits(Int32Overflow); } |
| bool didObserveInt52Overflow() const { return hasBits(Int52Overflow); } |
| |
| void setObservedNonNegZeroDouble() { setBit(NonNegZeroDouble); } |
| void setObservedNegZeroDouble() { setBit(NegZeroDouble); } |
| void setObservedNonNumeric() { setBit(NonNumeric); } |
| void setObservedBigInt() { setBit(BigInt); } |
| void setObservedInt32Overflow() { setBit(Int32Overflow); } |
| void setObservedInt52Overflow() { setBit(Int52Overflow); } |
| |
| const void* addressOfBits() const { return &m_bits; } |
| |
| void observeResult(JSValue value) |
| { |
| if (value.isInt32()) |
| return; |
| if (value.isNumber()) { |
| m_bits |= Int32Overflow | Int52Overflow | NonNegZeroDouble | NegZeroDouble; |
| return; |
| } |
| if (value && value.isBigInt()) { |
| m_bits |= BigInt; |
| return; |
| } |
| m_bits |= NonNumeric; |
| } |
| |
| void lhsSawInt32() { setLhsObservedType(lhsObservedType().withInt32()); } |
| void lhsSawNumber() { setLhsObservedType(lhsObservedType().withNumber()); } |
| void lhsSawNonNumber() { setLhsObservedType(lhsObservedType().withNonNumber()); } |
| void rhsSawInt32() { setRhsObservedType(rhsObservedType().withInt32()); } |
| void rhsSawNumber() { setRhsObservedType(rhsObservedType().withNumber()); } |
| void rhsSawNonNumber() { setRhsObservedType(rhsObservedType().withNonNumber()); } |
| |
| void observeLHS(JSValue lhs) |
| { |
| ArithProfile newProfile = *this; |
| if (lhs.isNumber()) { |
| if (lhs.isInt32()) |
| newProfile.lhsSawInt32(); |
| else |
| newProfile.lhsSawNumber(); |
| } else |
| newProfile.lhsSawNonNumber(); |
| |
| m_bits = newProfile.bits(); |
| } |
| |
| void observeLHSAndRHS(JSValue lhs, JSValue rhs) |
| { |
| observeLHS(lhs); |
| |
| ArithProfile newProfile = *this; |
| if (rhs.isNumber()) { |
| if (rhs.isInt32()) |
| newProfile.rhsSawInt32(); |
| else |
| newProfile.rhsSawNumber(); |
| } else |
| newProfile.rhsSawNonNumber(); |
| |
| m_bits = newProfile.bits(); |
| } |
| |
| #if ENABLE(JIT) |
| // Sets (Int32Overflow | Int52Overflow | NonNegZeroDouble | NegZeroDouble) if it sees a |
| // double. Sets NonNumeric if it sees a non-numeric. |
| void emitObserveResult(CCallHelpers&, JSValueRegs, TagRegistersMode = HaveTagRegisters); |
| |
| // Sets (Int32Overflow | Int52Overflow | NonNegZeroDouble | NegZeroDouble). |
| bool shouldEmitSetDouble() const; |
| void emitSetDouble(CCallHelpers&) const; |
| |
| // Sets NonNumber. |
| void emitSetNonNumeric(CCallHelpers&) const; |
| bool shouldEmitSetNonNumeric() const; |
| |
| // Sets BigInt |
| void emitSetBigInt(CCallHelpers&) const; |
| bool shouldEmitSetBigInt() const; |
| #endif // ENABLE(JIT) |
| |
| constexpr uint32_t bits() const { return m_bits; } |
| |
| private: |
| constexpr explicit ArithProfile(ConstantTag, uint32_t bits) |
| : m_bits(bits) |
| { |
| } |
| |
| constexpr ArithProfile(ConstantTag, ResultType arg) |
| : m_bits(arg.bits() << lhsResultTypeShift) |
| { |
| } |
| |
| constexpr ArithProfile(ConstantTag, ResultType lhs, ResultType rhs) |
| : m_bits((lhs.bits() << lhsResultTypeShift) | (rhs.bits() << rhsResultTypeShift)) |
| { |
| } |
| |
| bool hasBits(int mask) const { return m_bits & mask; } |
| void setBit(int mask) { m_bits |= mask; } |
| |
| uint32_t m_bits { 0 }; // We take care to update m_bits only in a single operation. We don't ever store an inconsistent bit representation to it. |
| |
| friend class JSC::LLIntOffsetsExtractor; |
| }; |
| |
| } // namespace JSC |
| |
| namespace WTF { |
| |
| void printInternal(PrintStream&, const JSC::ArithProfile&); |
| void printInternal(PrintStream&, const JSC::ObservedType&); |
| |
| } // namespace WTF |