| /* |
| * Copyright (C) 2016-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. |
| */ |
| |
| #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 }; |
| }; |
| |
| class ObservedResults { |
| public: |
| enum Tags : uint8_t { |
| NonNegZeroDouble = 1 << 0, |
| NegZeroDouble = 1 << 1, |
| NonNumeric = 1 << 2, |
| Int32Overflow = 1 << 3, |
| Int52Overflow = 1 << 4, |
| HeapBigInt = 1 << 5, |
| BigInt32 = 1 << 6, |
| }; |
| static constexpr uint32_t numBitsNeeded = 7; |
| |
| ObservedResults() = default; |
| explicit ObservedResults(uint8_t bits) |
| : m_bits(bits) |
| { } |
| |
| bool didObserveNonInt32() { return m_bits & (NonNegZeroDouble | NegZeroDouble | NonNumeric | HeapBigInt | BigInt32); } |
| bool didObserveDouble() { return m_bits & (NonNegZeroDouble | NegZeroDouble); } |
| bool didObserveNonNegZeroDouble() { return m_bits & NonNegZeroDouble; } |
| bool didObserveNegZeroDouble() { return m_bits & NegZeroDouble; } |
| bool didObserveNonNumeric() { return m_bits & NonNumeric; } |
| bool didObserveBigInt() { return m_bits & (HeapBigInt | BigInt32); } |
| bool didObserveHeapBigInt() { return m_bits & HeapBigInt; } |
| bool didObserveBigInt32() { return m_bits & BigInt32; } |
| bool didObserveInt32Overflow() { return m_bits & Int32Overflow; } |
| bool didObserveInt52Overflow() { return m_bits & Int52Overflow; } |
| |
| private: |
| uint8_t m_bits { 0 }; |
| }; |
| |
| template <typename BitfieldType> |
| class ArithProfile { |
| public: |
| ObservedResults observedResults() const |
| { |
| return ObservedResults(m_bits & ((1 << ObservedResults::numBitsNeeded) - 1)); |
| } |
| bool didObserveNonInt32() const { return observedResults().didObserveNonInt32();} |
| bool didObserveDouble() const { return observedResults().didObserveDouble(); } |
| bool didObserveNonNegZeroDouble() const { return observedResults().didObserveNonNegZeroDouble(); } |
| bool didObserveNegZeroDouble() const { return observedResults().didObserveNegZeroDouble(); } |
| bool didObserveNonNumeric() const { return observedResults().didObserveNonNumeric(); } |
| bool didObserveBigInt() const { return observedResults().didObserveBigInt(); } |
| bool didObserveHeapBigInt() const { return observedResults().didObserveHeapBigInt(); } |
| bool didObserveBigInt32() const { return observedResults().didObserveBigInt32(); } |
| bool didObserveInt32Overflow() const { return observedResults().didObserveInt32Overflow(); } |
| bool didObserveInt52Overflow() const { return observedResults().didObserveInt52Overflow(); } |
| |
| void setObservedNonNegZeroDouble() { setBit(ObservedResults::NonNegZeroDouble); } |
| void setObservedNegZeroDouble() { setBit(ObservedResults::NegZeroDouble); } |
| void setObservedNonNumeric() { setBit(ObservedResults::NonNumeric); } |
| void setObservedHeapBigInt() { setBit(ObservedResults::HeapBigInt); } |
| void setObservedBigInt32() { setBit(ObservedResults::BigInt32); } |
| void setObservedInt32Overflow() { setBit(ObservedResults::Int32Overflow); } |
| void setObservedInt52Overflow() { setBit(ObservedResults::Int52Overflow); } |
| |
| void observeResult(JSValue value) |
| { |
| if (value.isInt32()) |
| return; |
| if (value.isNumber()) { |
| m_bits |= ObservedResults::Int32Overflow | ObservedResults::Int52Overflow | ObservedResults::NonNegZeroDouble | ObservedResults::NegZeroDouble; |
| return; |
| } |
| if (value.isBigInt32()) { |
| m_bits |= ObservedResults::BigInt32; |
| return; |
| } |
| if (value && value.isHeapBigInt()) { |
| m_bits |= ObservedResults::HeapBigInt; |
| return; |
| } |
| m_bits |= ObservedResults::NonNumeric; |
| } |
| |
| const void* addressOfBits() const { return &m_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, GPRReg tempGPR, TagRegistersMode = HaveTagRegisters); |
| |
| // Sets (Int32Overflow | Int52Overflow | NonNegZeroDouble | NegZeroDouble). |
| bool shouldEmitSetDouble() const; |
| void emitSetDouble(CCallHelpers&) const; |
| |
| void emitSetNonNumeric(CCallHelpers&) const; |
| bool shouldEmitSetNonNumeric() const; |
| |
| bool shouldEmitSetHeapBigInt() const; |
| void emitSetHeapBigInt(CCallHelpers&) const; |
| |
| bool shouldEmitSetBigInt32() const; |
| #if USE(BIGINT32) |
| void emitSetBigInt32(CCallHelpers&) const; |
| #endif |
| |
| void emitUnconditionalSet(CCallHelpers&, BitfieldType) const; |
| #endif // ENABLE(JIT) |
| |
| constexpr uint32_t bits() const { return m_bits; } |
| |
| protected: |
| ArithProfile() = default; |
| |
| bool hasBits(int mask) const { return m_bits & mask; } |
| void setBit(int mask) { m_bits |= mask; } |
| |
| BitfieldType 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. |
| }; |
| |
| /* This class stores the following components in 16 bits: |
| * - ObservedResults |
| * - ObservedType for the argument |
| */ |
| using UnaryArithProfileBase = uint16_t; |
| class UnaryArithProfile : public ArithProfile<UnaryArithProfileBase> { |
| static constexpr unsigned argObservedTypeShift = ObservedResults::numBitsNeeded; |
| |
| static_assert(argObservedTypeShift + ObservedType::numBitsNeeded <= sizeof(UnaryArithProfileBase) * 8, "Should fit in the type of the underlying bitfield."); |
| |
| static constexpr UnaryArithProfileBase clearArgObservedTypeBitMask = static_cast<UnaryArithProfileBase>(~(0b111 << argObservedTypeShift)); |
| |
| static constexpr UnaryArithProfileBase observedTypeMask = (1 << ObservedType::numBitsNeeded) - 1; |
| |
| public: |
| UnaryArithProfile() |
| : ArithProfile<UnaryArithProfileBase>() |
| { |
| ASSERT(argObservedType().isEmpty()); |
| ASSERT(argObservedType().isEmpty()); |
| } |
| |
| static constexpr UnaryArithProfileBase observedIntBits() |
| { |
| constexpr ObservedType observedInt32 { ObservedType().withInt32() }; |
| constexpr UnaryArithProfileBase bits = observedInt32.bits() << argObservedTypeShift; |
| return bits; |
| } |
| static constexpr UnaryArithProfileBase observedNumberBits() |
| { |
| constexpr ObservedType observedNumber { ObservedType().withNumber() }; |
| constexpr UnaryArithProfileBase bits = observedNumber.bits() << argObservedTypeShift; |
| return bits; |
| } |
| |
| constexpr ObservedType argObservedType() const { return ObservedType((m_bits >> argObservedTypeShift) & observedTypeMask); } |
| void setArgObservedType(ObservedType type) |
| { |
| UnaryArithProfileBase bits = m_bits; |
| bits &= clearArgObservedTypeBitMask; |
| bits |= type.bits() << argObservedTypeShift; |
| m_bits = bits; |
| ASSERT(argObservedType() == type); |
| } |
| |
| void argSawInt32() { setArgObservedType(argObservedType().withInt32()); } |
| void argSawNumber() { setArgObservedType(argObservedType().withNumber()); } |
| void argSawNonNumber() { setArgObservedType(argObservedType().withNonNumber()); } |
| |
| void observeArg(JSValue arg) |
| { |
| UnaryArithProfile newProfile = *this; |
| if (arg.isNumber()) { |
| if (arg.isInt32()) |
| newProfile.argSawInt32(); |
| else |
| newProfile.argSawNumber(); |
| } else |
| newProfile.argSawNonNumber(); |
| |
| m_bits = newProfile.bits(); |
| } |
| |
| bool isObservedTypeEmpty() |
| { |
| return argObservedType().isEmpty(); |
| } |
| |
| friend class JSC::LLIntOffsetsExtractor; |
| }; |
| |
| /* This class stores the following components in 16 bits: |
| * - ObservedResults |
| * - ObservedType for right-hand-side |
| * - ObservedType for left-hand-side |
| * - a bit used by division to indicate whether a special fast path was taken |
| */ |
| using BinaryArithProfileBase = uint16_t; |
| class BinaryArithProfile : public ArithProfile<BinaryArithProfileBase> { |
| static constexpr uint32_t rhsObservedTypeShift = ObservedResults::numBitsNeeded; |
| static constexpr uint32_t lhsObservedTypeShift = rhsObservedTypeShift + ObservedType::numBitsNeeded; |
| |
| static_assert(ObservedType::numBitsNeeded == 3, "We make a hard assumption about that here."); |
| static constexpr BinaryArithProfileBase clearRhsObservedTypeBitMask = static_cast<BinaryArithProfileBase>(~(0b111 << rhsObservedTypeShift)); |
| static constexpr BinaryArithProfileBase clearLhsObservedTypeBitMask = static_cast<BinaryArithProfileBase>(~(0b111 << lhsObservedTypeShift)); |
| |
| static constexpr BinaryArithProfileBase observedTypeMask = (1 << ObservedType::numBitsNeeded) - 1; |
| |
| public: |
| static constexpr BinaryArithProfileBase specialFastPathBit = 1 << (lhsObservedTypeShift + ObservedType::numBitsNeeded); |
| static_assert((lhsObservedTypeShift + ObservedType::numBitsNeeded + 1) <= sizeof(BinaryArithProfileBase) * 8, "Should fit in the underlying type."); |
| static_assert(!(specialFastPathBit & ~clearLhsObservedTypeBitMask), "These bits should not intersect."); |
| static_assert(specialFastPathBit & clearLhsObservedTypeBitMask, "These bits should intersect."); |
| static_assert(static_cast<unsigned>(specialFastPathBit) > static_cast<unsigned>(static_cast<BinaryArithProfileBase>(~clearLhsObservedTypeBitMask)), "These bits should not intersect and specialFastPathBit should be a higher bit."); |
| |
| BinaryArithProfile() |
| : ArithProfile<BinaryArithProfileBase> () |
| { |
| ASSERT(lhsObservedType().isEmpty()); |
| ASSERT(rhsObservedType().isEmpty()); |
| } |
| |
| static constexpr BinaryArithProfileBase observedIntIntBits() |
| { |
| constexpr ObservedType observedInt32 { ObservedType().withInt32() }; |
| constexpr BinaryArithProfileBase bits = (observedInt32.bits() << lhsObservedTypeShift) | (observedInt32.bits() << rhsObservedTypeShift); |
| return bits; |
| } |
| static constexpr BinaryArithProfileBase observedNumberIntBits() |
| { |
| constexpr ObservedType observedNumber { ObservedType().withNumber() }; |
| constexpr ObservedType observedInt32 { ObservedType().withInt32() }; |
| constexpr BinaryArithProfileBase bits = (observedNumber.bits() << lhsObservedTypeShift) | (observedInt32.bits() << rhsObservedTypeShift); |
| return bits; |
| } |
| static constexpr BinaryArithProfileBase observedIntNumberBits() |
| { |
| constexpr ObservedType observedNumber { ObservedType().withNumber() }; |
| constexpr ObservedType observedInt32 { ObservedType().withInt32() }; |
| constexpr BinaryArithProfileBase bits = (observedInt32.bits() << lhsObservedTypeShift) | (observedNumber.bits() << rhsObservedTypeShift); |
| return bits; |
| } |
| static constexpr BinaryArithProfileBase observedNumberNumberBits() |
| { |
| constexpr ObservedType observedNumber { ObservedType().withNumber() }; |
| constexpr BinaryArithProfileBase bits = (observedNumber.bits() << lhsObservedTypeShift) | (observedNumber.bits() << rhsObservedTypeShift); |
| return bits; |
| } |
| |
| constexpr ObservedType lhsObservedType() const { return ObservedType((m_bits >> lhsObservedTypeShift) & observedTypeMask); } |
| constexpr ObservedType rhsObservedType() const { return ObservedType((m_bits >> rhsObservedTypeShift) & observedTypeMask); } |
| void setLhsObservedType(ObservedType type) |
| { |
| BinaryArithProfileBase bits = m_bits; |
| bits &= clearLhsObservedTypeBitMask; |
| bits |= type.bits() << lhsObservedTypeShift; |
| m_bits = bits; |
| ASSERT(lhsObservedType() == type); |
| } |
| |
| void setRhsObservedType(ObservedType type) |
| { |
| BinaryArithProfileBase bits = m_bits; |
| bits &= clearRhsObservedTypeBitMask; |
| bits |= type.bits() << rhsObservedTypeShift; |
| m_bits = bits; |
| ASSERT(rhsObservedType() == type); |
| } |
| |
| bool tookSpecialFastPath() const { return m_bits & specialFastPathBit; } |
| |
| 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) |
| { |
| BinaryArithProfile 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); |
| |
| BinaryArithProfile newProfile = *this; |
| if (rhs.isNumber()) { |
| if (rhs.isInt32()) |
| newProfile.rhsSawInt32(); |
| else |
| newProfile.rhsSawNumber(); |
| } else |
| newProfile.rhsSawNonNumber(); |
| |
| m_bits = newProfile.bits(); |
| } |
| |
| bool isObservedTypeEmpty() |
| { |
| return lhsObservedType().isEmpty() && rhsObservedType().isEmpty(); |
| } |
| |
| friend class JSC::LLIntOffsetsExtractor; |
| }; |
| |
| } // namespace JSC |
| |
| namespace WTF { |
| |
| void printInternal(PrintStream&, const JSC::UnaryArithProfile&); |
| void printInternal(PrintStream&, const JSC::BinaryArithProfile&); |
| void printInternal(PrintStream&, const JSC::ObservedType&); |
| |
| } // namespace WTF |