/* * 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. */ #include "config.h" #include "ArithProfile.h" #include "CCallHelpers.h" #include "JSCJSValueInlines.h" namespace JSC { #if ENABLE(JIT) template void ArithProfile::emitObserveResult(CCallHelpers& jit, GPRReg valueGPR, GPRReg tempGPR, TagRegistersMode mode) { if (!shouldEmitSetDouble() && !shouldEmitSetNonNumeric() && !shouldEmitSetHeapBigInt() && !shouldEmitSetBigInt32()) return; CCallHelpers::JumpList done; CCallHelpers::JumpList nonNumeric; done.append(jit.branchIfInt32(valueGPR, mode)); CCallHelpers::Jump notDouble = jit.branchIfNotDoubleKnownNotInt32(valueGPR, mode); if (shouldEmitSetDouble()) { constexpr BitfieldType doubleBits = ObservedResults::Int32Overflow | ObservedResults::NegZeroDouble | ObservedResults::NonNegZeroDouble; if ((m_bits & doubleBits) != doubleBits) emitUnconditionalSet(jit, doubleBits); if (!didObserveInt52Overflow()) { if (mode == DoNotHaveTagRegisters) { jit.move(CCallHelpers::TrustedImm64(JSValue::NumberTag), tempGPR); jit.add64(valueGPR, tempGPR); } else jit.add64(GPRInfo::numberTagRegister, valueGPR, tempGPR); emitSetInt52OverflowIfNeeded(jit, tempGPR); } } done.append(jit.jump()); notDouble.link(&jit); #if USE(BIGINT32) CCallHelpers::Jump notBigInt32 = jit.branchIfNotBigInt32(valueGPR, tempGPR, mode); emitSetBigInt32(jit); done.append(jit.jump()); notBigInt32.link(&jit); #else UNUSED_PARAM(tempGPR); #endif nonNumeric.append(jit.branchIfNotCell(valueGPR, mode)); nonNumeric.append(jit.branchIfNotHeapBigInt(valueGPR)); emitSetHeapBigInt(jit); done.append(jit.jump()); nonNumeric.link(&jit); emitSetNonNumeric(jit); done.link(&jit); } template bool ArithProfile::shouldEmitSetDouble() const { BitfieldType mask = ObservedResults::Int32Overflow | ObservedResults::Int52Overflow | ObservedResults::NegZeroDouble | ObservedResults::NonNegZeroDouble; return (m_bits & mask) != mask; } template void ArithProfile::emitSetDouble(CCallHelpers& jit, GPRReg scratchGPR) const { if (shouldEmitSetDouble()) { #if CPU(ARM64) jit.move(CCallHelpers::TrustedImm32(ObservedResults::Int32Overflow | ObservedResults::Int52Overflow | ObservedResults::NegZeroDouble | ObservedResults::NonNegZeroDouble), scratchGPR); emitUnconditionalSet(jit, scratchGPR); #else UNUSED_PARAM(scratchGPR); emitUnconditionalSet(jit, ObservedResults::Int32Overflow | ObservedResults::Int52Overflow | ObservedResults::NegZeroDouble | ObservedResults::NonNegZeroDouble); #endif } } template void ArithProfile::emitSetInt52OverflowIfNeeded(CCallHelpers& jit, GPRReg doubleBitsGPR) const { constexpr double overflowPoint = static_cast(ObservedResults::int52OverflowPoint); CCallHelpers::JumpList inRange; inRange.append(jit.branch64(CCallHelpers::Equal, doubleBitsGPR, CCallHelpers::TrustedImm64(std::bit_cast(-overflowPoint)))); // Shifting out the sign bit orders the remaining bits by magnitude; NaN and Infinity sort above every finite value. jit.lshift64(CCallHelpers::TrustedImm32(1), doubleBitsGPR); inRange.append(jit.branch64(CCallHelpers::Below, doubleBitsGPR, CCallHelpers::TrustedImm64(std::bit_cast(overflowPoint) << 1))); emitUnconditionalSet(jit, ObservedResults::Int52Overflow); inRange.link(&jit); } template bool ArithProfile::shouldEmitSetNonNumeric() const { BitfieldType mask = ObservedResults::NonNumeric; return (m_bits & mask) != mask; } template void ArithProfile::emitSetNonNumeric(CCallHelpers& jit) const { if (shouldEmitSetNonNumeric()) emitUnconditionalSet(jit, ObservedResults::NonNumeric); } template bool ArithProfile::shouldEmitSetBigInt32() const { #if USE(BIGINT32) BitfieldType mask = ObservedResults::BigInt32; return (m_bits & mask) != mask; #else return false; #endif } template bool ArithProfile::shouldEmitSetHeapBigInt() const { BitfieldType mask = ObservedResults::HeapBigInt; return (m_bits & mask) != mask; } template void ArithProfile::emitSetHeapBigInt(CCallHelpers& jit) const { if (shouldEmitSetHeapBigInt()) emitUnconditionalSet(jit, ObservedResults::HeapBigInt); } #if USE(BIGINT32) template void ArithProfile::emitSetBigInt32(CCallHelpers& jit) const { if (shouldEmitSetBigInt32()) emitUnconditionalSet(jit, ObservedResults::BigInt32); } #endif template void ArithProfile::emitUnconditionalSet(CCallHelpers& jit, BitfieldType mask) const { static_assert(std::same_as); jit.or16(CCallHelpers::TrustedImm32(mask), CCallHelpers::AbsoluteAddress(addressOfBits())); } template void ArithProfile::emitUnconditionalSet(CCallHelpers& jit, GPRReg mask) const { jit.or16(mask, CCallHelpers::AbsoluteAddress(addressOfBits())); } // Generate the implementations of the functions above for UnaryArithProfile/BinaryArithProfile // If changing the size of either, add the corresponding lines here. template class ArithProfile; #endif // ENABLE(JIT) } // namespace JSC namespace WTF { using namespace JSC; template void printInternal(PrintStream& out, const ArithProfile& profile) { const char* separator = ""; out.print("Result:<"); if (!profile.didObserveNonInt32()) { out.print("Int32"); separator = "|"; } else { if (profile.didObserveNegZeroDouble()) { out.print(separator, "NegZeroDouble"); separator = "|"; } if (profile.didObserveNonNegZeroDouble()) { out.print(separator, "NonNegZeroDouble"); separator = "|"; } if (profile.didObserveNonNumeric()) { out.print(separator, "NonNumeric"); separator = "|"; } if (profile.didObserveInt32Overflow()) { out.print(separator, "Int32Overflow"); separator = "|"; } if (profile.didObserveInt52Overflow()) { out.print(separator, "Int52Overflow"); separator = "|"; } if (profile.didObserveHeapBigInt()) { out.print(separator, "HeapBigInt"); separator = "|"; } if (profile.didObserveBigInt32()) { out.print(separator, "BigInt32"); separator = "|"; } } out.print(">"); } void printInternal(PrintStream& out, const UnaryArithProfile& profile) { printInternal(out, static_cast>(profile)); out.print(" Arg ObservedType:<"); out.print(profile.argObservedType()); out.print(">"); } void printInternal(PrintStream& out, const BinaryArithProfile& profile) { printInternal(out, static_cast>(profile)); if (profile.tookSpecialFastPath()) out.print(" Took special fast path."); out.print(" LHS ObservedType:<"); out.print(profile.lhsObservedType()); out.print("> RHS ObservedType:<"); out.print(profile.rhsObservedType()); out.print(">"); } void printInternal(PrintStream& out, const JSC::ObservedType& observedType) { const char* separator = ""; if (observedType.sawInt32()) { out.print(separator, "Int32"); separator = "|"; } if (observedType.sawNumber()) { out.print(separator, "Number"); separator = "|"; } if (observedType.sawNonNumber()) { out.print(separator, "NonNumber"); separator = "|"; } } } // namespace WTF