Support an inlined representation in JSValue of small BigInts ("BigInt32")
authorrmorisset@apple.com <rmorisset@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 19 Apr 2020 02:20:59 +0000 (02:20 +0000)
committerrmorisset@apple.com <rmorisset@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 19 Apr 2020 02:20:59 +0000 (02:20 +0000)
https://bugs.webkit.org/show_bug.cgi?id=206182

Reviewed by Yusuke Suzuki.

JSTests:

I improved several of the tests to give more informative error messages in the process of fixing them.

More interestingly I had to modify "missing-exception-check-in-string-compare" because it relied on "s1 == s1" resolving ropes, and we now just return true.

* stress/big-int-division.js:
(testDiv):
* stress/big-int-left-shift-wrapped-value.js:
(assert.sameValue):
* stress/big-int-logical-not.js:
(assert):
* stress/big-int-mod-jit.js:
* stress/big-int-right-shift-general.js:
(testRightShift):
* stress/big-int-type-of-proven-type.js:
(assert):
* stress/compare-strict-eq-on-various-types.js:
(testAllTypesCall):
* stress/ftl-string-strict-equality.js:
* stress/missing-exception-check-in-string-compare.js:

Source/JavaScriptCore:

This patch attempts to optimize the performance of BigInts, when they are small (32 bit or less).
It works by inlining them into JSValue on 64-bit platforms, avoiding the allocation of a JSBigInt.
The bit pattern we use is 0000:XXXX:XXXX:0012
This representation works because of the following things:
- It cannot be confused with a Double or Integer thanks to the top bits
- It cannot be confused with a pointer to a Cell, thanks to bit 1 which is set to true
- It cannot be confused with a pointer to wasm thanks to bit 0 which is set to false
- It cannot be confused with true/false because bit 2 is set to false
- It cannot be confused for null/undefined because bit 4 is set to true

This entire change is gated by USE(BIGINT32), to make it easier to disable if it turns out to have bugs.
It should also make it much easier to verify if a given bug comes from it or from something else.

Note that in this patch we create BigInt32s when parsing small BigInt constants, and most operations (e.g. Add or BitOr) produce a BigInt32 if both of their operands are BigInt32,
but we don't produce a BigInt32 from for example the substraction/division of two large heap-allocated JSBigInts, even if the result fits in 32-bits.
As a result, small BigInts can now either be heap-allocated or inlined in the JSValue.

This patch includes a significant refactor of various slow paths, which are now grouped together in Operations.h
Because this increased the size of Operations.h significantly, I split the parts of Operations.h which are only used by the GC into Scribble.h, to avoid bloating compile times.

In the DFG and FTL we now have 3 UseKinds for BigInts: HeapBigIntUse, BigInt32Use and AnyBigIntUse.
The latter is useful when we know that we are receiving BigInts, but speculation indicates a mix of heap-allocated and small (inlined) big-ints.

Unfortunately, a naive implementation of this patch significantly regresses the performance of StrictEq (and its variants), as it is no longer true that a cell and a non-cell cannot be equal.
Before this patch, the code was jumping to a slow path if either:
- at least one operand is a double
- or both operands are cells
Now, it also needs to jump to the slow path if at least one is a cell.
To recover this performance cost, I significantly rewrote this code, from
  if (left is Cell && right is Cell) {
    if (left == right)
      return true;
    goto slowPath;
  }
  if (! left is Int32) {
    if (left is Number)
      goto slowPath
  }
  if (! right is Int32) {
    if (right is Number)
      goto slowPath
  }
  return left == right
To the following:
  if (left is Double || right is Double)
    goto slowPath
  if (left == right)
    return true;
  if (left is Cell || right is Cell)
    goto slowPath
  return false;
I believe this to be faster than just replacing (left is Cell && right is Cell) by an ||, because I found a bit-trick to check (left is Double || right is Double) which should help reduce the pressure on the branch predictor.
Early JetStream2 tests appear to confirm that this patch is roughly neutral while it was a 0.5% regression before I used this trick, but the numbers are still too noisy, I plan to do more measurements before landing this patch.

I don't yet have performance numbers for this patch on a BigInt benchmark, I will get such numbers before trying to land it, but I'd like some review in the meantime.

* JavaScriptCore.xcodeproj/project.pbxproj:
* assembler/X86Assembler.h:
(JSC::X86Assembler::X86InstructionFormatter::SingleInstructionBufferWriter::memoryModRM):
* bytecode/ArithProfile.cpp:
(JSC::ArithProfile<BitfieldType>::emitObserveResult):
(JSC::ArithProfile<BitfieldType>::shouldEmitSetBigInt32 const):
(JSC::ArithProfile<BitfieldType>::shouldEmitSetHeapBigInt const):
(JSC::ArithProfile<BitfieldType>::emitSetHeapBigInt const):
(JSC::ArithProfile<BitfieldType>::emitSetBigInt32 const):
(WTF::printInternal):
* bytecode/ArithProfile.h:
(JSC::ObservedResults::didObserveNonInt32):
(JSC::ObservedResults::didObserveBigInt):
(JSC::ObservedResults::didObserveHeapBigInt):
(JSC::ObservedResults::didObserveBigInt32):
(JSC::ArithProfile::didObserveHeapBigInt const):
(JSC::ArithProfile::didObserveBigInt32 const):
(JSC::ArithProfile::setObservedHeapBigInt):
(JSC::ArithProfile::setObservedBigInt32):
(JSC::ArithProfile::observeResult):
* bytecode/BytecodeList.rb:
* bytecode/BytecodeLivenessAnalysisInlines.h:
* bytecode/BytecodeUseDef.cpp:
(JSC::computeUsesForBytecodeIndexImpl):
(JSC::computeDefsForBytecodeIndexImpl):
* bytecode/CodeBlock.cpp:
* bytecode/DataFormat.h:
* bytecode/MethodOfGettingAValueProfile.cpp:
(JSC::MethodOfGettingAValueProfile::emitReportValue const):
* bytecode/MethodOfGettingAValueProfile.h:
* bytecode/SpeculatedType.cpp:
(JSC::dumpSpeculation):
(JSC::speculationFromClassInfo):
(JSC::speculationFromStructure):
(JSC::speculationFromValue):
(JSC::speculationFromJSType):
(JSC::leastUpperBoundOfStrictlyEquivalentSpeculations):
* bytecode/SpeculatedType.h:
(JSC::isBigInt32Speculation):
(JSC::isHeapBigIntSpeculation):
(JSC::isBigIntSpeculation):
* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::emitEqualityOpImpl):
(JSC::BytecodeGenerator::addBigIntConstant):
* bytecompiler/BytecodeGenerator.h:
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::isToThisAnIdentity):
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::parseBlock):
* dfg/DFGCapabilities.cpp:
(JSC::DFG::capabilityLevel):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGConstantFoldingPhase.cpp:
(JSC::DFG::ConstantFoldingPhase::foldConstants):
* dfg/DFGDoesGC.cpp:
(JSC::DFG::doesGC):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
(JSC::DFG::FixupPhase::fixupToThis):
(JSC::DFG::FixupPhase::fixupToNumeric):
(JSC::DFG::FixupPhase::observeUseKindOnNode):
(JSC::DFG::FixupPhase::fixupCompareStrictEqAndSameValue):
* dfg/DFGMayExit.cpp:
* dfg/DFGNode.h:
(JSC::DFG::Node::shouldSpeculateBigInt32):
(JSC::DFG::Node::shouldSpeculateHeapBigInt):
* dfg/DFGNodeType.h:
* dfg/DFGOSRExit.cpp:
(JSC::DFG::OSRExit::compileExit):
* dfg/DFGOSRExit.h:
* dfg/DFGOperations.cpp:
* dfg/DFGOperations.h:
* dfg/DFGPredictionPropagationPhase.cpp:
* dfg/DFGSafeToExecute.h:
(JSC::DFG::SafeToExecuteEdge::operator()):
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compilePeepHoleBranch):
(JSC::DFG::SpeculativeJIT::compileValueBitNot):
(JSC::DFG::SpeculativeJIT::emitUntypedOrAnyBigIntBitOp):
(JSC::DFG::SpeculativeJIT::compileValueBitwiseOp):
(JSC::DFG::SpeculativeJIT::emitUntypedOrBigIntRightShiftBitOp):
(JSC::DFG::SpeculativeJIT::compileValueLShiftOp):
(JSC::DFG::SpeculativeJIT::compileValueBitRShift):
(JSC::DFG::SpeculativeJIT::compileShiftOp):
(JSC::DFG::SpeculativeJIT::compileValueAdd):
(JSC::DFG::SpeculativeJIT::compileValueSub):
(JSC::DFG::SpeculativeJIT::compileIncOrDec):
(JSC::DFG::SpeculativeJIT::compileValueNegate):
(JSC::DFG::SpeculativeJIT::compileValueMul):
(JSC::DFG::SpeculativeJIT::compileValueDiv):
(JSC::DFG::SpeculativeJIT::compileValueMod):
(JSC::DFG::SpeculativeJIT::compileValuePow):
(JSC::DFG::SpeculativeJIT::compare):
(JSC::DFG::SpeculativeJIT::compileStrictEq):
(JSC::DFG::SpeculativeJIT::speculateHeapBigInt):
(JSC::DFG::SpeculativeJIT::speculate):
(JSC::DFG::SpeculativeJIT::compileToNumeric):
(JSC::DFG::SpeculativeJIT::compileHeapBigIntEquality):
* dfg/DFGSpeculativeJIT.h:
(JSC::DFG::SpeculateBigInt32Operand::SpeculateBigInt32Operand):
(JSC::DFG::SpeculateBigInt32Operand::~SpeculateBigInt32Operand):
(JSC::DFG::SpeculateBigInt32Operand::edge const):
(JSC::DFG::SpeculateBigInt32Operand::node const):
(JSC::DFG::SpeculateBigInt32Operand::gpr):
(JSC::DFG::SpeculateBigInt32Operand::use):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::fillJSValue):
(JSC::DFG::SpeculativeJIT::nonSpeculativePeepholeStrictEq):
(JSC::DFG::SpeculativeJIT::nonSpeculativeNonPeepholeStrictEq):
(JSC::DFG::SpeculativeJIT::fillSpeculateInt32Internal):
(JSC::DFG::SpeculativeJIT::fillSpeculateCell):
(JSC::DFG::SpeculativeJIT::fillSpeculateBoolean):
(JSC::DFG::SpeculativeJIT::speculateBigInt32):
(JSC::DFG::SpeculativeJIT::speculateAnyBigInt):
(JSC::DFG::SpeculativeJIT::fillSpeculateBigInt32):
(JSC::DFG::SpeculativeJIT::compileBigInt32Compare):
(JSC::DFG::SpeculativeJIT::compilePeepHoleBigInt32Branch):
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGStrengthReductionPhase.cpp:
(JSC::DFG::StrengthReductionPhase::handleNode):
* dfg/DFGUseKind.cpp:
(WTF::printInternal):
* dfg/DFGUseKind.h:
(JSC::DFG::typeFilterFor):
(JSC::DFG::isCell):
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLCommonValues.cpp:
(JSC::FTL::CommonValues::initializeConstants):
* ftl/FTLCommonValues.h:
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileNode):
(JSC::FTL::DFG::LowerDFGToB3::compileValueAdd):
(JSC::FTL::DFG::LowerDFGToB3::compileValueSub):
(JSC::FTL::DFG::LowerDFGToB3::compileValueMul):
(JSC::FTL::DFG::LowerDFGToB3::compileBinaryMathIC):
(JSC::FTL::DFG::LowerDFGToB3::compileValueDiv):
(JSC::FTL::DFG::LowerDFGToB3::compileValueMod):
(JSC::FTL::DFG::LowerDFGToB3::compileValuePow):
(JSC::FTL::DFG::LowerDFGToB3::compileValueBitNot):
(JSC::FTL::DFG::LowerDFGToB3::compileValueBitAnd):
(JSC::FTL::DFG::LowerDFGToB3::compileValueBitOr):
(JSC::FTL::DFG::LowerDFGToB3::compileValueBitXor):
(JSC::FTL::DFG::LowerDFGToB3::compileValueBitRShift):
(JSC::FTL::DFG::LowerDFGToB3::compileArithBitRShift):
(JSC::FTL::DFG::LowerDFGToB3::compileArithBitLShift):
(JSC::FTL::DFG::LowerDFGToB3::compileValueBitLShift):
(JSC::FTL::DFG::LowerDFGToB3::compileBitURShift):
(JSC::FTL::DFG::LowerDFGToB3::compileToNumeric):
(JSC::FTL::DFG::LowerDFGToB3::compileCompareEq):
(JSC::FTL::DFG::LowerDFGToB3::compileCompareStrictEq):
(JSC::FTL::DFG::LowerDFGToB3::compileIsBigInt):
(JSC::FTL::DFG::LowerDFGToB3::emitBinarySnippet):
(JSC::FTL::DFG::LowerDFGToB3::emitBinaryBitOpSnippet):
(JSC::FTL::DFG::LowerDFGToB3::boolify):
(JSC::FTL::DFG::LowerDFGToB3::buildTypeOf):
(JSC::FTL::DFG::LowerDFGToB3::lowHeapBigInt):
(JSC::FTL::DFG::LowerDFGToB3::lowBigInt32):
(JSC::FTL::DFG::LowerDFGToB3::isBigInt32):
(JSC::FTL::DFG::LowerDFGToB3::isNotBigInt32):
(JSC::FTL::DFG::LowerDFGToB3::unboxBigInt32):
(JSC::FTL::DFG::LowerDFGToB3::boxBigInt32):
(JSC::FTL::DFG::LowerDFGToB3::isNotAnyBigInt):
(JSC::FTL::DFG::LowerDFGToB3::speculate):
(JSC::FTL::DFG::LowerDFGToB3::isNotHeapBigIntUnknownWhetherCell):
(JSC::FTL::DFG::LowerDFGToB3::isNotHeapBigInt):
(JSC::FTL::DFG::LowerDFGToB3::isHeapBigInt):
(JSC::FTL::DFG::LowerDFGToB3::speculateHeapBigInt):
(JSC::FTL::DFG::LowerDFGToB3::speculateHeapBigIntUnknownWhetherCell):
(JSC::FTL::DFG::LowerDFGToB3::speculateBigInt32):
(JSC::FTL::DFG::LowerDFGToB3::speculateAnyBigInt):
* ftl/FTLOSRExitCompiler.cpp:
(JSC::FTL::compileStub):
* heap/HeapSnapshotBuilder.cpp:
(JSC::HeapSnapshotBuilder::json):
* heap/MarkedBlockInlines.h:
* heap/PreciseAllocation.cpp:
* inspector/agents/InspectorHeapAgent.cpp:
(Inspector::InspectorHeapAgent::getPreview):
* interpreter/Interpreter.cpp:
(JSC::sizeOfVarargs):
* jit/AssemblyHelpers.cpp:
(JSC::AssemblyHelpers::emitConvertValueToBoolean):
(JSC::AssemblyHelpers::branchIfValue):
* jit/AssemblyHelpers.h:
(JSC::AssemblyHelpers::branchIfBigInt32):
(JSC::AssemblyHelpers::branchIfBigInt32KnownNotNumber):
(JSC::AssemblyHelpers::branchIfNotBigInt32KnownNotNumber):
(JSC::AssemblyHelpers::branchIfHeapBigInt):
(JSC::AssemblyHelpers::branchIfNotHeapBigInt):
(JSC::AssemblyHelpers::unboxBigInt32):
(JSC::AssemblyHelpers::boxBigInt32):
(JSC::AssemblyHelpers::emitTypeOf):
* jit/JIT.cpp:
(JSC::JIT::privateCompileMainPass):
* jit/JIT.h:
* jit/JITArithmetic.cpp:
(JSC::JIT::emit_op_negate):
(JSC::JIT::emitSlow_op_negate):
* jit/JITOpcodes.cpp:
(JSC::JIT::emit_op_is_big_int):
(JSC::JIT::compileOpStrictEq):
(JSC::JIT::compileOpStrictEqJump):
(JSC::JIT::emit_op_to_numeric):
* jit/JITOpcodes32_64.cpp:
(JSC::JIT::emit_op_is_big_int):
(JSC::JIT::emit_op_to_numeric):
* jit/JITOperations.cpp:
* jit/JITOperations.h:
* llint/LLIntOfflineAsmConfig.h:
* llint/LowLevelInterpreter.asm:
* llint/LowLevelInterpreter64.asm:
* parser/ParserArena.cpp:
(JSC::IdentifierArena::makeBigIntDecimalIdentifier):
* runtime/ArrayPrototype.cpp:
* runtime/BigIntConstructor.cpp:
(JSC::toBigInt):
(JSC::callBigIntConstructor):
* runtime/BigIntObject.cpp:
(JSC::BigIntObject::create):
(JSC::BigIntObject::finishCreation):
* runtime/BigIntObject.h:
* runtime/BigIntPrototype.cpp:
(JSC::toThisBigIntValue):
(JSC::bigIntProtoFuncToStringImpl):
* runtime/CommonSlowPaths.cpp:
(JSC::SLOW_PATH_DECL):
(JSC::updateArithProfileForUnaryArithOp):
(JSC::updateArithProfileForBinaryArithOp):
* runtime/JSBigInt.cpp:
(JSC::JSBigInt::createStructure):
(JSC::JSBigInt::parseInt):
(JSC::JSBigInt::stringToBigInt):
(JSC::JSBigInt::inc):
(JSC::JSBigInt::dec):
(JSC::JSBigInt::bitwiseAnd):
(JSC::JSBigInt::toStringGeneric):
(JSC::JSBigInt::equalsToNumber):
(JSC::JSBigInt::equalsToInt32):
* runtime/JSBigInt.h:
(JSC::asHeapBigInt):
* runtime/JSCJSValue.cpp:
(JSC::JSValue::toNumberSlowCase const):
(JSC::JSValue::toObjectSlowCase const):
(JSC::JSValue::toThisSlowCase const):
(JSC::JSValue::synthesizePrototype const):
(JSC::JSValue::dumpInContextAssumingStructure const):
(JSC::JSValue::dumpForBacktrace const):
(JSC::JSValue::toStringSlowCase const):
* runtime/JSCJSValue.h:
* runtime/JSCJSValueInlines.h:
(JSC::JSValue::JSValue):
(JSC::JSValue::asHeapBigInt const):
(JSC::JSValue::isBigInt const):
(JSC::JSValue::isHeapBigInt const):
(JSC::JSValue::isBigInt32 const):
(JSC::JSValue::bigInt32AsInt32 const):
(JSC::JSValue::isPrimitive const):
(JSC::JSValue::getPrimitiveNumber):
(JSC::JSValue::toNumeric const):
(JSC::JSValue::toBigIntOrInt32 const):
(JSC::JSValue::equalSlowCaseInline):
(JSC::JSValue::strictEqualForCells):
(JSC::JSValue::strictEqual):
(JSC::JSValue::pureStrictEqual):
(JSC::JSValue::pureToBoolean const):
* runtime/JSCell.cpp:
(JSC::JSCell::put):
(JSC::JSCell::putByIndex):
(JSC::JSCell::toPrimitive const):
(JSC::JSCell::getPrimitiveNumber const):
(JSC::JSCell::toNumber const):
(JSC::JSCell::toObjectSlow const):
* runtime/JSCell.h:
* runtime/JSCellInlines.h:
(JSC::JSCell::isHeapBigInt const):
(JSC::JSCell::toBoolean const):
(JSC::JSCell::pureToBoolean const):
* runtime/JSString.h:
(JSC::JSValue::toBoolean const):
* runtime/JSType.cpp:
(WTF::printInternal):
* runtime/JSType.h:
* runtime/JSTypeInfo.h:
* runtime/ObjectInitializationScope.cpp:
* runtime/Operations.cpp:
(JSC::jsAddSlowCase):
(JSC::jsIsObjectTypeOrNull):
* runtime/Operations.h:
(JSC::compareBigIntToOtherPrimitive):
(JSC::bigIntCompare):
(JSC::jsLess):
(JSC::jsLessEq):
(JSC::arithmeticBinaryOp):
(JSC::jsSub):
(JSC::jsMul):
(JSC::jsDiv):
(JSC::jsRemainder):
(JSC::jsPow):
(JSC::jsInc):
(JSC::jsDec):
(JSC::jsBitwiseNot):
(JSC::shift):
(JSC::jsLShift):
(JSC::jsRShift):
(JSC::bitwiseBinaryOp):
(JSC::jsBitwiseAnd):
(JSC::jsBitwiseOr):
(JSC::jsBitwiseXor):
* runtime/Scribble.h: Copied from Source/JavaScriptCore/runtime/BigIntObject.h.
(JSC::scribbleFreeCells):
(JSC::isScribbledValue):
(JSC::scribble):
* runtime/StructureInlines.h:
(JSC::prototypeForLookupPrimitiveImpl):

Source/WTF:

Add a USE(BIGINT32) flag.

* wtf/PlatformUse.h:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@260331 268f45cc-cd09-0410-ab3c-d52691b4dbfc

108 files changed:
JSTests/ChangeLog
JSTests/stress/big-int-division.js
JSTests/stress/big-int-left-shift-wrapped-value.js
JSTests/stress/big-int-logical-not.js
JSTests/stress/big-int-mod-jit.js
JSTests/stress/big-int-multiplication.js
JSTests/stress/big-int-right-shift-general.js
JSTests/stress/big-int-type-of-proven-type.js
JSTests/stress/compare-number-and-other.js
JSTests/stress/compare-strict-eq-on-various-types.js
JSTests/stress/ftl-string-strict-equality.js
JSTests/stress/missing-exception-check-in-string-compare.js
Source/JavaScriptCore/CMakeLists.txt
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
Source/JavaScriptCore/assembler/X86Assembler.h
Source/JavaScriptCore/bytecode/ArithProfile.cpp
Source/JavaScriptCore/bytecode/ArithProfile.h
Source/JavaScriptCore/bytecode/BytecodeList.rb
Source/JavaScriptCore/bytecode/BytecodeLivenessAnalysisInlines.h
Source/JavaScriptCore/bytecode/BytecodeUseDef.cpp
Source/JavaScriptCore/bytecode/CodeBlock.cpp
Source/JavaScriptCore/bytecode/DataFormat.h
Source/JavaScriptCore/bytecode/MethodOfGettingAValueProfile.cpp
Source/JavaScriptCore/bytecode/MethodOfGettingAValueProfile.h
Source/JavaScriptCore/bytecode/SpeculatedType.cpp
Source/JavaScriptCore/bytecode/SpeculatedType.h
Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h
Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
Source/JavaScriptCore/dfg/DFGCapabilities.cpp
Source/JavaScriptCore/dfg/DFGClobberize.h
Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp
Source/JavaScriptCore/dfg/DFGDoesGC.cpp
Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
Source/JavaScriptCore/dfg/DFGGraph.cpp
Source/JavaScriptCore/dfg/DFGMayExit.cpp
Source/JavaScriptCore/dfg/DFGNode.h
Source/JavaScriptCore/dfg/DFGNodeFlags.h
Source/JavaScriptCore/dfg/DFGNodeType.h
Source/JavaScriptCore/dfg/DFGOSRExit.cpp
Source/JavaScriptCore/dfg/DFGOperations.cpp
Source/JavaScriptCore/dfg/DFGOperations.h
Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
Source/JavaScriptCore/dfg/DFGSafeToExecute.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
Source/JavaScriptCore/dfg/DFGStrengthReductionPhase.cpp
Source/JavaScriptCore/dfg/DFGUseKind.cpp
Source/JavaScriptCore/dfg/DFGUseKind.h
Source/JavaScriptCore/ftl/FTLCapabilities.cpp
Source/JavaScriptCore/ftl/FTLCommonValues.cpp
Source/JavaScriptCore/ftl/FTLCommonValues.h
Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
Source/JavaScriptCore/ftl/FTLOSRExitCompiler.cpp
Source/JavaScriptCore/heap/HeapSnapshotBuilder.cpp
Source/JavaScriptCore/heap/MarkedBlockInlines.h
Source/JavaScriptCore/heap/PreciseAllocation.cpp
Source/JavaScriptCore/inspector/agents/InspectorHeapAgent.cpp
Source/JavaScriptCore/interpreter/Interpreter.cpp
Source/JavaScriptCore/jit/AssemblyHelpers.cpp
Source/JavaScriptCore/jit/AssemblyHelpers.h
Source/JavaScriptCore/jit/JIT.cpp
Source/JavaScriptCore/jit/JIT.h
Source/JavaScriptCore/jit/JITArithmetic.cpp
Source/JavaScriptCore/jit/JITOpcodes.cpp
Source/JavaScriptCore/jit/JITOpcodes32_64.cpp
Source/JavaScriptCore/jit/JITOperations.cpp
Source/JavaScriptCore/jit/JITOperations.h
Source/JavaScriptCore/llint/LLIntOfflineAsmConfig.h
Source/JavaScriptCore/llint/LowLevelInterpreter.asm
Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm
Source/JavaScriptCore/llint/LowLevelInterpreter64.asm
Source/JavaScriptCore/parser/ParserArena.cpp
Source/JavaScriptCore/runtime/ArrayPrototype.cpp
Source/JavaScriptCore/runtime/BigIntConstructor.cpp
Source/JavaScriptCore/runtime/BigIntObject.cpp
Source/JavaScriptCore/runtime/BigIntObject.h
Source/JavaScriptCore/runtime/BigIntPrototype.cpp
Source/JavaScriptCore/runtime/CommonSlowPaths.cpp
Source/JavaScriptCore/runtime/IntlNumberFormatPrototype.cpp
Source/JavaScriptCore/runtime/JSBigInt.cpp
Source/JavaScriptCore/runtime/JSBigInt.h
Source/JavaScriptCore/runtime/JSCJSValue.cpp
Source/JavaScriptCore/runtime/JSCJSValue.h
Source/JavaScriptCore/runtime/JSCJSValueInlines.h
Source/JavaScriptCore/runtime/JSCell.cpp
Source/JavaScriptCore/runtime/JSCell.h
Source/JavaScriptCore/runtime/JSCellInlines.h
Source/JavaScriptCore/runtime/JSGlobalObject.h
Source/JavaScriptCore/runtime/JSString.h
Source/JavaScriptCore/runtime/JSType.cpp
Source/JavaScriptCore/runtime/JSType.h
Source/JavaScriptCore/runtime/JSTypeInfo.h
Source/JavaScriptCore/runtime/ObjectInitializationScope.cpp
Source/JavaScriptCore/runtime/Operations.cpp
Source/JavaScriptCore/runtime/Operations.h
Source/JavaScriptCore/runtime/Scribble.h [new file with mode: 0644]
Source/JavaScriptCore/runtime/StructureInlines.h
Source/JavaScriptCore/runtime/VM.cpp
Source/JavaScriptCore/runtime/VM.h
Source/JavaScriptCore/tools/JSDollarVM.cpp
Source/WTF/ChangeLog
Source/WTF/wtf/PlatformUse.h
Tools/Scripts/run-jsc-stress-tests

index 2e9f72c..ee6405b 100644 (file)
@@ -1,3 +1,30 @@
+2020-04-18  Robin Morisset  <rmorisset@apple.com>
+
+        Support an inlined representation in JSValue of small BigInts ("BigInt32")
+        https://bugs.webkit.org/show_bug.cgi?id=206182
+
+        Reviewed by Yusuke Suzuki.
+
+        I improved several of the tests to give more informative error messages in the process of fixing them.
+
+        More interestingly I had to modify "missing-exception-check-in-string-compare" because it relied on "s1 == s1" resolving ropes, and we now just return true.
+
+        * stress/big-int-division.js:
+        (testDiv):
+        * stress/big-int-left-shift-wrapped-value.js:
+        (assert.sameValue):
+        * stress/big-int-logical-not.js:
+        (assert):
+        * stress/big-int-mod-jit.js:
+        * stress/big-int-right-shift-general.js:
+        (testRightShift):
+        * stress/big-int-type-of-proven-type.js:
+        (assert):
+        * stress/compare-strict-eq-on-various-types.js:
+        (testAllTypesCall):
+        * stress/ftl-string-strict-equality.js:
+        * stress/missing-exception-check-in-string-compare.js:
+
 2020-04-18  Keith Miller  <keith_miller@apple.com>
 
         Redesign how we do for-of iteration for JSArrays
index 401890a..3e510b9 100644 (file)
@@ -1,4 +1,5 @@
 //@ runBigIntEnabled
+//@ runBigIntEnabledNoJIT
 
 // Copyright (C) 2017 Robin Templeton. All rights reserved.
 // This code is governed by the BSD license found in the LICENSE file.
@@ -14,7 +15,7 @@ assert.sameValue = function (input, expected, message) {
 }
 
 function testDiv(x, y, z) {
-    assert.sameValue(x / y, z, x + " / " + y + " = " + z);
+    assert.sameValue(x / y, z, x + " / " + y + " = " + (x/y) + " but should be " + z);
 }
 
 testDiv(0xFEDCBA9876543210n, 0xFEDCBA9876543210n, 0x1n);
index c69dbda..f812b1c 100644 (file)
@@ -3,7 +3,7 @@
 assert = {
     sameValue: function (input, expected, message) {
         if (input !== expected)
-            throw new Error(message);
+            throw new Error(message + " input: " + input + " != expected: " + expected);
     }
 };
 
index af28a72..d02e552 100644 (file)
@@ -1,8 +1,8 @@
 //@ runBigIntEnabled
 
-function assert(a, e) {
+function assert(a, e, n) {
     if (a !== e) {
-        throw new Error("Bad!");
+        throw new Error("Bad logical negation for " + n);
     }
 }
 
@@ -12,9 +12,9 @@ function logicalNot(a) {
 noInline(logicalNot);
 
 for (let i = 0; i < 100000; i++) {
-    assert(logicalNot(10n), false);
-    assert(logicalNot(1n), false);
-    assert(logicalNot(0n), true);
-    assert(logicalNot(-1n), false);
+    assert(logicalNot(10n), false, 10n);
+    assert(logicalNot(1n), false, 1n);
+    assert(logicalNot(0n), true, 0n);
+    assert(logicalNot(-1n), false, -1n);
 }
 
index 81c5da7..859540c 100644 (file)
@@ -25,9 +25,9 @@ noInline(bigIntModFolding);
 
 for (let i = 0; i < 10000; i++) {
     let r = bigIntModFolding(10, 30);
-    assert.sameValue(r, -10, "-(" + 10 + " % " + 30 + ") = " + r);
+    assert.sameValue(r, -10, "[Number] -(" + 10 + " % " + 30 + ") = " + r);
 }
 
 let r = bigIntModFolding(10n, 30n);
-assert.sameValue(r, -10n, "-(" + 10n + " % " + 30n + ") = " + r);
+assert.sameValue(r, -10n, "[BigInt] -(" + 10n + " % " + 30n + ") = " + r);
 
index 3ecd636..7ce5acf 100644 (file)
@@ -1,18 +1,18 @@
 //@ runBigIntEnabled
+//@ runBigIntEnabledNoJIT
 
 // Copyright (C) 2017 Robin Templeton. All rights reserved.
 // This code is governed by the BSD license found in the LICENSE file.
 
-assert = {
-    sameValue: function (input, expected, message) {
-    if (input !== expected)
-        throw new Error(message);
-    }
-};
+function testOneMul(x, y, z) {
+    let result = x * y;
+    if (result !== z)
+        throw new Error("Computing " + x + " * " + y + " resulted in " + result + " instead of the expected " + z);
+}
 
 function testMul(x, y, z) {
-    assert.sameValue(x * y, z, x + " * " + y + " = " + z);
-    assert.sameValue(y * x, z, y + " * " + x + " = " + z);
+    testOneMul(x, y, z);
+    testOneMul(y, x, z);
 }
 
 testMul(0xFEDCBA9876543210n, 0xFEDCBA9876543210n, 0xFDBAC097C8DC5ACCDEEC6CD7A44A4100n);
index 4e58ca6..d02aacb 100644 (file)
 //@ runBigIntEnabled
 
 // Copyright (C) 2017 Josh Wolfe. All rights reserved.
+// Copyright (C) 2020 Apple. All rights reserved
 // This code is governed by the BSD license found in the LICENSE file.
 
-function assert(a) {
-    if (!a)
-        throw new Error("Bad assertion");
+function testRightShift(left, right, expected)
+{
+    var result = left >> right;
+    if (result !== expected)
+        throw new Error("" + left + ", " + right + " resulted in " + result + " but expected " + expected);
 }
 
-assert.sameValue = function (input, expected, message) {
-    if (input !== expected)
-        throw new Error(message);
+for (var i = 0; i < 1000 ; ++i) {
+    testRightShift(0n, 0n, 0n);
+    testRightShift(0b101n, -1n, 0b1010n);
+    testRightShift(0b101n, -2n, 0b10100n);
+    testRightShift(0b101n, -3n, 0b101000n);
+    testRightShift(0b101n, 1n, 0b10n);
+    testRightShift(0b101n, 2n, 1n);
+    testRightShift(0b101n, 3n, 0n);
+    testRightShift(0n, -128n, 0n);
+    testRightShift(0n, 128n, 0n);
+    testRightShift(0x246n, 0n, 0x246n);
+    testRightShift(0x246n, -127n, 0x12300000000000000000000000000000000n);
+    testRightShift(0x246n, -128n, 0x24600000000000000000000000000000000n);
+    testRightShift(0x246n, -129n, 0x48c00000000000000000000000000000000n);
+    testRightShift(0x246n, 128n, 0n);
+    testRightShift(0x123456789abcdef0fedcba9876543212345678n, -64n, 0x123456789abcdef0fedcba98765432123456780000000000000000n);
+    testRightShift(0x123456789abcdef0fedcba9876543212345678n, -32n, 0x123456789abcdef0fedcba987654321234567800000000n);
+    testRightShift(0x123456789abcdef0fedcba9876543212345678n, -16n, 0x123456789abcdef0fedcba98765432123456780000n);
+    testRightShift(0x123456789abcdef0fedcba9876543212345678n, 0n, 0x123456789abcdef0fedcba9876543212345678n);
+    testRightShift(0x123456789abcdef0fedcba9876543212345678n, 16n, 0x123456789abcdef0fedcba987654321234n);
+    testRightShift(0x123456789abcdef0fedcba9876543212345678n, 32n, 0x123456789abcdef0fedcba98765432n);
+    testRightShift(0x123456789abcdef0fedcba9876543212345678n, 64n, 0x123456789abcdef0fedcban);
+    testRightShift(0x123456789abcdef0fedcba9876543212345678n, 127n, 0x2468acn);
+    testRightShift(0x123456789abcdef0fedcba9876543212345678n, 128n, 0x123456n);
+    testRightShift(0x123456789abcdef0fedcba9876543212345678n, 129n, 0x91a2bn);
+    testRightShift(-5n, -1n, -0xan);
+    testRightShift(-5n, -2n, -0x14n);
+    testRightShift(-5n, -3n, -0x28n);
+    testRightShift(-5n, 1n, -3n);
+    testRightShift(-5n, 2n, -2n);
+    testRightShift(-5n, 3n, -1n);
+    testRightShift(-1n, -128n, -0x100000000000000000000000000000000n);
+    testRightShift(-1n, 0n, -1n);
+    testRightShift(-1n, 128n, -1n);
+    testRightShift(-0x246n, 0n, -0x246n);
+    testRightShift(-0x246n, -127n, -0x12300000000000000000000000000000000n);
+    testRightShift(-0x246n, -128n, -0x24600000000000000000000000000000000n);
+    testRightShift(-0x246n, -129n, -0x48c00000000000000000000000000000000n);
+    testRightShift(-0x246n, 128n, -1n);
+    testRightShift(-0x123456789abcdef0fedcba9876543212345678n, -64n, -0x123456789abcdef0fedcba98765432123456780000000000000000n);
+    testRightShift(-0x123456789abcdef0fedcba9876543212345678n, -32n, -0x123456789abcdef0fedcba987654321234567800000000n);
+    testRightShift(-0x123456789abcdef0fedcba9876543212345678n, -16n, -0x123456789abcdef0fedcba98765432123456780000n);
+    testRightShift(-0x123456789abcdef0fedcba9876543212345678n, 0n, -0x123456789abcdef0fedcba9876543212345678n);
+    testRightShift(-0x123456789abcdef0fedcba9876543212345678n, 16n, -0x123456789abcdef0fedcba987654321235n);
+    testRightShift(-0x123456789abcdef0fedcba9876543212345678n, 32n, -0x123456789abcdef0fedcba98765433n);
+    testRightShift(-0x123456789abcdef0fedcba9876543212345678n, 64n, -0x123456789abcdef0fedcbbn);
+    testRightShift(-0x123456789abcdef0fedcba9876543212345678n, 127n, -0x2468adn);
+    testRightShift(-0x123456789abcdef0fedcba9876543212345678n, 128n, -0x123457n);
+    testRightShift(-0x123456789abcdef0fedcba9876543212345678n, 129n, -0x91a2cn);
 }
-
-assert.sameValue(0n >> 0n, 0n, "0n >> 0n === 0n");
-assert.sameValue(0b101n >> -1n, 0b1010n, "0b101n >> -1n === 0b1010n");
-assert.sameValue(0b101n >> -2n, 0b10100n, "0b101n >> -2n === 0b10100n");
-assert.sameValue(0b101n >> -3n, 0b101000n, "0b101n >> -3n === 0b101000n");
-assert.sameValue(0b101n >> 1n, 0b10n, "0b101n >> 1n === 0b10n");
-assert.sameValue(0b101n >> 2n, 1n, "0b101n >> 2n === 1n");
-assert.sameValue(0b101n >> 3n, 0n, "0b101n >> 3n === 0n");
-assert.sameValue(0n >> -128n, 0n, "0n >> -128n === 0n");
-assert.sameValue(0n >> 128n, 0n, "0n >> 128n === 0n");
-assert.sameValue(0x246n >> 0n, 0x246n, "0x246n >> 0n === 0x246n");
-assert.sameValue(0x246n >> -127n, 0x12300000000000000000000000000000000n, "0x246n >> -127n === 0x12300000000000000000000000000000000n");
-assert.sameValue(0x246n >> -128n, 0x24600000000000000000000000000000000n, "0x246n >> -128n === 0x24600000000000000000000000000000000n");
-assert.sameValue(0x246n >> -129n, 0x48c00000000000000000000000000000000n, "0x246n >> -129n === 0x48c00000000000000000000000000000000n");
-assert.sameValue(0x246n >> 128n, 0n, "0x246n >> 128n === 0n");
-assert.sameValue(
-  0x123456789abcdef0fedcba9876543212345678n >> -64n, 0x123456789abcdef0fedcba98765432123456780000000000000000n,
-  "0x123456789abcdef0fedcba9876543212345678n >> -64n === 0x123456789abcdef0fedcba98765432123456780000000000000000n");
-assert.sameValue(
-  0x123456789abcdef0fedcba9876543212345678n >> -32n, 0x123456789abcdef0fedcba987654321234567800000000n,
-  "0x123456789abcdef0fedcba9876543212345678n >> -32n === 0x123456789abcdef0fedcba987654321234567800000000n");
-assert.sameValue(
-  0x123456789abcdef0fedcba9876543212345678n >> -16n, 0x123456789abcdef0fedcba98765432123456780000n,
-  "0x123456789abcdef0fedcba9876543212345678n >> -16n === 0x123456789abcdef0fedcba98765432123456780000n");
-assert.sameValue(
-  0x123456789abcdef0fedcba9876543212345678n >> 0n, 0x123456789abcdef0fedcba9876543212345678n,
-  "0x123456789abcdef0fedcba9876543212345678n >> 0n === 0x123456789abcdef0fedcba9876543212345678n");
-assert.sameValue(
-  0x123456789abcdef0fedcba9876543212345678n >> 16n, 0x123456789abcdef0fedcba987654321234n,
-  "0x123456789abcdef0fedcba9876543212345678n >> 16n === 0x123456789abcdef0fedcba987654321234n");
-assert.sameValue(
-  0x123456789abcdef0fedcba9876543212345678n >> 32n, 0x123456789abcdef0fedcba98765432n,
-  "0x123456789abcdef0fedcba9876543212345678n >> 32n === 0x123456789abcdef0fedcba98765432n");
-assert.sameValue(
-  0x123456789abcdef0fedcba9876543212345678n >> 64n, 0x123456789abcdef0fedcban,
-  "0x123456789abcdef0fedcba9876543212345678n >> 64n === 0x123456789abcdef0fedcban");
-assert.sameValue(
-  0x123456789abcdef0fedcba9876543212345678n >> 127n, 0x2468acn,
-  "0x123456789abcdef0fedcba9876543212345678n >> 127n === 0x2468acn");
-assert.sameValue(
-  0x123456789abcdef0fedcba9876543212345678n >> 128n, 0x123456n,
-  "0x123456789abcdef0fedcba9876543212345678n >> 128n === 0x123456n");
-assert.sameValue(
-  0x123456789abcdef0fedcba9876543212345678n >> 129n, 0x91a2bn,
-  "0x123456789abcdef0fedcba9876543212345678n >> 129n === 0x91a2bn");
-assert.sameValue(-5n >> -1n, -0xan, "-5n >> -1n === -0xan");
-assert.sameValue(-5n >> -2n, -0x14n, "-5n >> -2n === -0x14n");
-assert.sameValue(-5n >> -3n, -0x28n, "-5n >> -3n === -0x28n");
-assert.sameValue(-5n >> 1n, -3n, "-5n >> 1n === -3n");
-assert.sameValue(-5n >> 2n, -2n, "-5n >> 2n === -2n");
-assert.sameValue(-5n >> 3n, -1n, "-5n >> 3n === -1n");
-assert.sameValue(-1n >> -128n, -0x100000000000000000000000000000000n, "-1n >> -128n === -0x100000000000000000000000000000000n");
-assert.sameValue(-1n >> 0n, -1n, "-1n >> 0n === -1n");
-assert.sameValue(-1n >> 128n, -1n, "-1n >> 128n === -1n");
-assert.sameValue(-0x246n >> 0n, -0x246n, "-0x246n >> 0n === -0x246n");
-assert.sameValue(-0x246n >> -127n, -0x12300000000000000000000000000000000n, "-0x246n >> -127n === -0x12300000000000000000000000000000000n");
-assert.sameValue(-0x246n >> -128n, -0x24600000000000000000000000000000000n, "-0x246n >> -128n === -0x24600000000000000000000000000000000n");
-assert.sameValue(-0x246n >> -129n, -0x48c00000000000000000000000000000000n, "-0x246n >> -129n === -0x48c00000000000000000000000000000000n");
-assert.sameValue(-0x246n >> 128n, -1n, "-0x246n >> 128n === -1n");
-assert.sameValue(
-  -0x123456789abcdef0fedcba9876543212345678n >> -64n, -0x123456789abcdef0fedcba98765432123456780000000000000000n,
-  "-0x123456789abcdef0fedcba9876543212345678n >> -64n === -0x123456789abcdef0fedcba98765432123456780000000000000000n");
-assert.sameValue(
-  -0x123456789abcdef0fedcba9876543212345678n >> -32n, -0x123456789abcdef0fedcba987654321234567800000000n,
-  "-0x123456789abcdef0fedcba9876543212345678n >> -32n === -0x123456789abcdef0fedcba987654321234567800000000n");
-assert.sameValue(
-  -0x123456789abcdef0fedcba9876543212345678n >> -16n, -0x123456789abcdef0fedcba98765432123456780000n,
-  "-0x123456789abcdef0fedcba9876543212345678n >> -16n === -0x123456789abcdef0fedcba98765432123456780000n");
-assert.sameValue(
-  -0x123456789abcdef0fedcba9876543212345678n >> 0n, -0x123456789abcdef0fedcba9876543212345678n,
-  "-0x123456789abcdef0fedcba9876543212345678n >> 0n === -0x123456789abcdef0fedcba9876543212345678n");
-assert.sameValue(
-  -0x123456789abcdef0fedcba9876543212345678n >> 16n, -0x123456789abcdef0fedcba987654321235n,
-  "-0x123456789abcdef0fedcba9876543212345678n >> 16n === -0x123456789abcdef0fedcba987654321235n");
-assert.sameValue(
-  -0x123456789abcdef0fedcba9876543212345678n >> 32n, -0x123456789abcdef0fedcba98765433n,
-  "-0x123456789abcdef0fedcba9876543212345678n >> 32n === -0x123456789abcdef0fedcba98765433n");
-assert.sameValue(
-  -0x123456789abcdef0fedcba9876543212345678n >> 64n, -0x123456789abcdef0fedcbbn,
-  "-0x123456789abcdef0fedcba9876543212345678n >> 64n === -0x123456789abcdef0fedcbbn");
-assert.sameValue(
-  -0x123456789abcdef0fedcba9876543212345678n >> 127n, -0x2468adn,
-  "-0x123456789abcdef0fedcba9876543212345678n >> 127n === -0x2468adn");
-assert.sameValue(
-  -0x123456789abcdef0fedcba9876543212345678n >> 128n, -0x123457n,
-  "-0x123456789abcdef0fedcba9876543212345678n >> 128n === -0x123457n");
-assert.sameValue(
-  -0x123456789abcdef0fedcba9876543212345678n >> 129n, -0x91a2cn,
-  "-0x123456789abcdef0fedcba9876543212345678n >> 129n === -0x91a2cn");
-
index d2f5397..de122c9 100644 (file)
@@ -1,8 +1,8 @@
 //@ runDefault("--useBigInt=true", "--useConcurrentJIT=false")
 
-function assert(a) {
-    if (!a)
-        throw new Error("Bad assertion");
+function assert(input, expected) {
+    if (input !== expected)
+        throw new Error("Bad result: " + input);
 }
 
 function foo(o) {
@@ -13,6 +13,5 @@ function foo(o) {
 noInline(foo);
 
 for (let i = 0; i < 10000; i++) {
-    assert(foo(3n) === "3");
+    assert(foo(3n), "3");
 }
-
index 8546bf3..825e85d 100644 (file)
@@ -59,17 +59,17 @@ for (let operator of operators) {
             noInline(testMonomorphicRightConstant${testCaseIndex});
 
             for (let i = 0; i < 500; ++i) {
-                if (testMonomorphic${testCaseIndex}(${left}, ${right}) != ${llintResult})
+                if (testMonomorphic${testCaseIndex}(${left}, ${right}) !== ${llintResult})
                     throw "Failed testMonomorphic${testCaseIndex}(${left}, ${right})";
-                if (testMonomorphicLeftConstant${testCaseIndex}(${right}) != ${llintResult})
+                if (testMonomorphicLeftConstant${testCaseIndex}(${right}) !== ${llintResult})
                     throw "Failed testMonomorphicLeftConstant${testCaseIndex}(${right})";
-                if (testMonomorphicRightConstant${testCaseIndex}(${left}) != ${llintResult})
+                if (testMonomorphicRightConstant${testCaseIndex}(${left}) !== ${llintResult})
                     throw "Failed testMonomorphicLeftConstant${testCaseIndex}(${left})";
                 if (testPolymorphic(${left}, ${right}) !== ${llintResult})
-                    throw "Failed polymorphicVersion(${left})";
+                    throw "Failed polymorphicVersion(${left}, ${operator}, ${right}, expected result: ${llintResult})";
             }
             `);
             ++testCaseIndex;
         }
     }
-}
\ No newline at end of file
+}
index af2283f..7f5cb10 100644 (file)
@@ -83,7 +83,8 @@ function testAllTypesCall() {
                         ", Right = " +
                         rightCases[rightCaseIndex] +
                         ", Result = " +
-                        strictEqualOutput;
+                        strictEqualOutput +
+                        " (case: " + leftCaseIndex + ", " + rightCaseIndex + ")";
                 }
 
                 let strictNotEqualOutput = opaqueStrictNotEqualAllTypes(leftCases[leftCaseIndex], rightCases[rightCaseIndex]);
@@ -93,7 +94,8 @@ function testAllTypesCall() {
                         ", Right = " +
                         rightCases[rightCaseIndex] +
                         ", Result = " +
-                        strictEqualOutput;
+                        strictEqualOutput +
+                        " (case: " + leftCaseIndex + ", " + rightCaseIndex + ")";
                 }
             }
         }
index 043a760..8e96cf4 100644 (file)
@@ -30,6 +30,6 @@ for (var i = 0; i < 100000; ++i) {
     var result = foo(array, array2[index]);
     var expected = index >= array.length ? null : index
     if (result !== expected)
-        throw "Error: bad result: " + result;
+        throw "Error: bad result: " + result + " but expected " + expected;
 }
 
index 965a7d7..dd85a83 100644 (file)
@@ -1,6 +1,8 @@
 const s1 = (-1).toLocaleString().padEnd(2**31-1, 'aa');
+const s2 = (-1).toLocaleString().padEnd(2**31-1, 'aa');
 try {
-    s1 == s1;
+    // Force evaluation of Rope
+    s1 == s2;
 } catch (e) {
     exception = e;
 }
index 95953db..d1d9232 100644 (file)
@@ -986,6 +986,7 @@ set(JavaScriptCore_PRIVATE_FRAMEWORK_HEADERS
     runtime/SamplingProfiler.h
     runtime/ScopeOffset.h
     runtime/ScopedArgumentsTable.h
+    runtime/Scribble.h
     runtime/ScriptExecutable.h
     runtime/ScriptFetchParameters.h
     runtime/ScriptFetcher.h
index a880a0f..fd85051 100644 (file)
@@ -1,3 +1,387 @@
+2020-04-18  Robin Morisset  <rmorisset@apple.com>
+
+        Support an inlined representation in JSValue of small BigInts ("BigInt32")
+        https://bugs.webkit.org/show_bug.cgi?id=206182
+
+        Reviewed by Yusuke Suzuki.
+
+        This patch attempts to optimize the performance of BigInts, when they are small (32 bit or less).
+        It works by inlining them into JSValue on 64-bit platforms, avoiding the allocation of a JSBigInt.
+        The bit pattern we use is 0000:XXXX:XXXX:0012
+        This representation works because of the following things:
+        - It cannot be confused with a Double or Integer thanks to the top bits
+        - It cannot be confused with a pointer to a Cell, thanks to bit 1 which is set to true
+        - It cannot be confused with a pointer to wasm thanks to bit 0 which is set to false
+        - It cannot be confused with true/false because bit 2 is set to false
+        - It cannot be confused for null/undefined because bit 4 is set to true
+
+        This entire change is gated by USE(BIGINT32), to make it easier to disable if it turns out to have bugs.
+        It should also make it much easier to verify if a given bug comes from it or from something else.
+
+        Note that in this patch we create BigInt32s when parsing small BigInt constants, and most operations (e.g. Add or BitOr) produce a BigInt32 if both of their operands are BigInt32,
+        but we don't produce a BigInt32 from for example the substraction/division of two large heap-allocated JSBigInts, even if the result fits in 32-bits.
+        As a result, small BigInts can now either be heap-allocated or inlined in the JSValue.
+
+        This patch includes a significant refactor of various slow paths, which are now grouped together in Operations.h
+        Because this increased the size of Operations.h significantly, I split the parts of Operations.h which are only used by the GC into Scribble.h, to avoid bloating compile times.
+
+        In the DFG and FTL we now have 3 UseKinds for BigInts: HeapBigIntUse, BigInt32Use and AnyBigIntUse.
+        The latter is useful when we know that we are receiving BigInts, but speculation indicates a mix of heap-allocated and small (inlined) big-ints.
+
+        Unfortunately, a naive implementation of this patch significantly regresses the performance of StrictEq (and its variants), as it is no longer true that a cell and a non-cell cannot be equal.
+        Before this patch, the code was jumping to a slow path if either:
+        - at least one operand is a double
+        - or both operands are cells
+        Now, it also needs to jump to the slow path if at least one is a cell.
+        To recover this performance cost, I significantly rewrote this code, from
+          if (left is Cell && right is Cell) {
+            if (left == right)
+              return true;
+            goto slowPath;
+          }
+          if (! left is Int32) {
+            if (left is Number)
+              goto slowPath
+          }
+          if (! right is Int32) {
+            if (right is Number)
+              goto slowPath
+          }
+          return left == right
+        To the following:
+          if (left is Double || right is Double)
+            goto slowPath
+          if (left == right)
+            return true;
+          if (left is Cell || right is Cell)
+            goto slowPath
+          return false;
+        I believe this to be faster than just replacing (left is Cell && right is Cell) by an ||, because I found a bit-trick to check (left is Double || right is Double) which should help reduce the pressure on the branch predictor.
+        Early JetStream2 tests appear to confirm that this patch is roughly neutral while it was a 0.5% regression before I used this trick, but the numbers are still too noisy, I plan to do more measurements before landing this patch.
+
+        I don't yet have performance numbers for this patch on a BigInt benchmark, I will get such numbers before trying to land it, but I'd like some review in the meantime.
+
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * assembler/X86Assembler.h:
+        (JSC::X86Assembler::X86InstructionFormatter::SingleInstructionBufferWriter::memoryModRM):
+        * bytecode/ArithProfile.cpp:
+        (JSC::ArithProfile<BitfieldType>::emitObserveResult):
+        (JSC::ArithProfile<BitfieldType>::shouldEmitSetBigInt32 const):
+        (JSC::ArithProfile<BitfieldType>::shouldEmitSetHeapBigInt const):
+        (JSC::ArithProfile<BitfieldType>::emitSetHeapBigInt const):
+        (JSC::ArithProfile<BitfieldType>::emitSetBigInt32 const):
+        (WTF::printInternal):
+        * bytecode/ArithProfile.h:
+        (JSC::ObservedResults::didObserveNonInt32):
+        (JSC::ObservedResults::didObserveBigInt):
+        (JSC::ObservedResults::didObserveHeapBigInt):
+        (JSC::ObservedResults::didObserveBigInt32):
+        (JSC::ArithProfile::didObserveHeapBigInt const):
+        (JSC::ArithProfile::didObserveBigInt32 const):
+        (JSC::ArithProfile::setObservedHeapBigInt):
+        (JSC::ArithProfile::setObservedBigInt32):
+        (JSC::ArithProfile::observeResult):
+        * bytecode/BytecodeList.rb:
+        * bytecode/BytecodeLivenessAnalysisInlines.h:
+        * bytecode/BytecodeUseDef.cpp:
+        (JSC::computeUsesForBytecodeIndexImpl):
+        (JSC::computeDefsForBytecodeIndexImpl):
+        * bytecode/CodeBlock.cpp:
+        * bytecode/DataFormat.h:
+        * bytecode/MethodOfGettingAValueProfile.cpp:
+        (JSC::MethodOfGettingAValueProfile::emitReportValue const):
+        * bytecode/MethodOfGettingAValueProfile.h:
+        * bytecode/SpeculatedType.cpp:
+        (JSC::dumpSpeculation):
+        (JSC::speculationFromClassInfo):
+        (JSC::speculationFromStructure):
+        (JSC::speculationFromValue):
+        (JSC::speculationFromJSType):
+        (JSC::leastUpperBoundOfStrictlyEquivalentSpeculations):
+        * bytecode/SpeculatedType.h:
+        (JSC::isBigInt32Speculation):
+        (JSC::isHeapBigIntSpeculation):
+        (JSC::isBigIntSpeculation):
+        * bytecompiler/BytecodeGenerator.cpp:
+        (JSC::BytecodeGenerator::emitEqualityOpImpl):
+        (JSC::BytecodeGenerator::addBigIntConstant):
+        * bytecompiler/BytecodeGenerator.h:
+        * dfg/DFGAbstractInterpreterInlines.h:
+        (JSC::DFG::isToThisAnIdentity):
+        (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::parseBlock):
+        * dfg/DFGCapabilities.cpp:
+        (JSC::DFG::capabilityLevel):
+        * dfg/DFGClobberize.h:
+        (JSC::DFG::clobberize):
+        * dfg/DFGConstantFoldingPhase.cpp:
+        (JSC::DFG::ConstantFoldingPhase::foldConstants):
+        * dfg/DFGDoesGC.cpp:
+        (JSC::DFG::doesGC):
+        * dfg/DFGFixupPhase.cpp:
+        (JSC::DFG::FixupPhase::fixupNode):
+        (JSC::DFG::FixupPhase::fixupToThis):
+        (JSC::DFG::FixupPhase::fixupToNumeric):
+        (JSC::DFG::FixupPhase::observeUseKindOnNode):
+        (JSC::DFG::FixupPhase::fixupCompareStrictEqAndSameValue):
+        * dfg/DFGMayExit.cpp:
+        * dfg/DFGNode.h:
+        (JSC::DFG::Node::shouldSpeculateBigInt32):
+        (JSC::DFG::Node::shouldSpeculateHeapBigInt):
+        * dfg/DFGNodeType.h:
+        * dfg/DFGOSRExit.cpp:
+        (JSC::DFG::OSRExit::compileExit):
+        * dfg/DFGOSRExit.h:
+        * dfg/DFGOperations.cpp:
+        * dfg/DFGOperations.h:
+        * dfg/DFGPredictionPropagationPhase.cpp:
+        * dfg/DFGSafeToExecute.h:
+        (JSC::DFG::SafeToExecuteEdge::operator()):
+        (JSC::DFG::safeToExecute):
+        * dfg/DFGSpeculativeJIT.cpp:
+        (JSC::DFG::SpeculativeJIT::compilePeepHoleBranch):
+        (JSC::DFG::SpeculativeJIT::compileValueBitNot):
+        (JSC::DFG::SpeculativeJIT::emitUntypedOrAnyBigIntBitOp):
+        (JSC::DFG::SpeculativeJIT::compileValueBitwiseOp):
+        (JSC::DFG::SpeculativeJIT::emitUntypedOrBigIntRightShiftBitOp):
+        (JSC::DFG::SpeculativeJIT::compileValueLShiftOp):
+        (JSC::DFG::SpeculativeJIT::compileValueBitRShift):
+        (JSC::DFG::SpeculativeJIT::compileShiftOp):
+        (JSC::DFG::SpeculativeJIT::compileValueAdd):
+        (JSC::DFG::SpeculativeJIT::compileValueSub):
+        (JSC::DFG::SpeculativeJIT::compileIncOrDec):
+        (JSC::DFG::SpeculativeJIT::compileValueNegate):
+        (JSC::DFG::SpeculativeJIT::compileValueMul):
+        (JSC::DFG::SpeculativeJIT::compileValueDiv):
+        (JSC::DFG::SpeculativeJIT::compileValueMod):
+        (JSC::DFG::SpeculativeJIT::compileValuePow):
+        (JSC::DFG::SpeculativeJIT::compare):
+        (JSC::DFG::SpeculativeJIT::compileStrictEq):
+        (JSC::DFG::SpeculativeJIT::speculateHeapBigInt):
+        (JSC::DFG::SpeculativeJIT::speculate):
+        (JSC::DFG::SpeculativeJIT::compileToNumeric):
+        (JSC::DFG::SpeculativeJIT::compileHeapBigIntEquality):
+        * dfg/DFGSpeculativeJIT.h:
+        (JSC::DFG::SpeculateBigInt32Operand::SpeculateBigInt32Operand):
+        (JSC::DFG::SpeculateBigInt32Operand::~SpeculateBigInt32Operand):
+        (JSC::DFG::SpeculateBigInt32Operand::edge const):
+        (JSC::DFG::SpeculateBigInt32Operand::node const):
+        (JSC::DFG::SpeculateBigInt32Operand::gpr):
+        (JSC::DFG::SpeculateBigInt32Operand::use):
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::fillJSValue):
+        (JSC::DFG::SpeculativeJIT::nonSpeculativePeepholeStrictEq):
+        (JSC::DFG::SpeculativeJIT::nonSpeculativeNonPeepholeStrictEq):
+        (JSC::DFG::SpeculativeJIT::fillSpeculateInt32Internal):
+        (JSC::DFG::SpeculativeJIT::fillSpeculateCell):
+        (JSC::DFG::SpeculativeJIT::fillSpeculateBoolean):
+        (JSC::DFG::SpeculativeJIT::speculateBigInt32):
+        (JSC::DFG::SpeculativeJIT::speculateAnyBigInt):
+        (JSC::DFG::SpeculativeJIT::fillSpeculateBigInt32):
+        (JSC::DFG::SpeculativeJIT::compileBigInt32Compare):
+        (JSC::DFG::SpeculativeJIT::compilePeepHoleBigInt32Branch):
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGStrengthReductionPhase.cpp:
+        (JSC::DFG::StrengthReductionPhase::handleNode):
+        * dfg/DFGUseKind.cpp:
+        (WTF::printInternal):
+        * dfg/DFGUseKind.h:
+        (JSC::DFG::typeFilterFor):
+        (JSC::DFG::isCell):
+        * ftl/FTLCapabilities.cpp:
+        (JSC::FTL::canCompile):
+        * ftl/FTLCommonValues.cpp:
+        (JSC::FTL::CommonValues::initializeConstants):
+        * ftl/FTLCommonValues.h:
+        * ftl/FTLLowerDFGToB3.cpp:
+        (JSC::FTL::DFG::LowerDFGToB3::compileNode):
+        (JSC::FTL::DFG::LowerDFGToB3::compileValueAdd):
+        (JSC::FTL::DFG::LowerDFGToB3::compileValueSub):
+        (JSC::FTL::DFG::LowerDFGToB3::compileValueMul):
+        (JSC::FTL::DFG::LowerDFGToB3::compileBinaryMathIC):
+        (JSC::FTL::DFG::LowerDFGToB3::compileValueDiv):
+        (JSC::FTL::DFG::LowerDFGToB3::compileValueMod):
+        (JSC::FTL::DFG::LowerDFGToB3::compileValuePow):
+        (JSC::FTL::DFG::LowerDFGToB3::compileValueBitNot):
+        (JSC::FTL::DFG::LowerDFGToB3::compileValueBitAnd):
+        (JSC::FTL::DFG::LowerDFGToB3::compileValueBitOr):
+        (JSC::FTL::DFG::LowerDFGToB3::compileValueBitXor):
+        (JSC::FTL::DFG::LowerDFGToB3::compileValueBitRShift):
+        (JSC::FTL::DFG::LowerDFGToB3::compileArithBitRShift):
+        (JSC::FTL::DFG::LowerDFGToB3::compileArithBitLShift):
+        (JSC::FTL::DFG::LowerDFGToB3::compileValueBitLShift):
+        (JSC::FTL::DFG::LowerDFGToB3::compileBitURShift):
+        (JSC::FTL::DFG::LowerDFGToB3::compileToNumeric):
+        (JSC::FTL::DFG::LowerDFGToB3::compileCompareEq):
+        (JSC::FTL::DFG::LowerDFGToB3::compileCompareStrictEq):
+        (JSC::FTL::DFG::LowerDFGToB3::compileIsBigInt):
+        (JSC::FTL::DFG::LowerDFGToB3::emitBinarySnippet):
+        (JSC::FTL::DFG::LowerDFGToB3::emitBinaryBitOpSnippet):
+        (JSC::FTL::DFG::LowerDFGToB3::boolify):
+        (JSC::FTL::DFG::LowerDFGToB3::buildTypeOf):
+        (JSC::FTL::DFG::LowerDFGToB3::lowHeapBigInt):
+        (JSC::FTL::DFG::LowerDFGToB3::lowBigInt32):
+        (JSC::FTL::DFG::LowerDFGToB3::isBigInt32):
+        (JSC::FTL::DFG::LowerDFGToB3::isNotBigInt32):
+        (JSC::FTL::DFG::LowerDFGToB3::unboxBigInt32):
+        (JSC::FTL::DFG::LowerDFGToB3::boxBigInt32):
+        (JSC::FTL::DFG::LowerDFGToB3::isNotAnyBigInt):
+        (JSC::FTL::DFG::LowerDFGToB3::speculate):
+        (JSC::FTL::DFG::LowerDFGToB3::isNotHeapBigIntUnknownWhetherCell):
+        (JSC::FTL::DFG::LowerDFGToB3::isNotHeapBigInt):
+        (JSC::FTL::DFG::LowerDFGToB3::isHeapBigInt):
+        (JSC::FTL::DFG::LowerDFGToB3::speculateHeapBigInt):
+        (JSC::FTL::DFG::LowerDFGToB3::speculateHeapBigIntUnknownWhetherCell):
+        (JSC::FTL::DFG::LowerDFGToB3::speculateBigInt32):
+        (JSC::FTL::DFG::LowerDFGToB3::speculateAnyBigInt):
+        * ftl/FTLOSRExitCompiler.cpp:
+        (JSC::FTL::compileStub):
+        * heap/HeapSnapshotBuilder.cpp:
+        (JSC::HeapSnapshotBuilder::json):
+        * heap/MarkedBlockInlines.h:
+        * heap/PreciseAllocation.cpp:
+        * inspector/agents/InspectorHeapAgent.cpp:
+        (Inspector::InspectorHeapAgent::getPreview):
+        * interpreter/Interpreter.cpp:
+        (JSC::sizeOfVarargs):
+        * jit/AssemblyHelpers.cpp:
+        (JSC::AssemblyHelpers::emitConvertValueToBoolean):
+        (JSC::AssemblyHelpers::branchIfValue):
+        * jit/AssemblyHelpers.h:
+        (JSC::AssemblyHelpers::branchIfBigInt32):
+        (JSC::AssemblyHelpers::branchIfBigInt32KnownNotNumber):
+        (JSC::AssemblyHelpers::branchIfNotBigInt32KnownNotNumber):
+        (JSC::AssemblyHelpers::branchIfHeapBigInt):
+        (JSC::AssemblyHelpers::branchIfNotHeapBigInt):
+        (JSC::AssemblyHelpers::unboxBigInt32):
+        (JSC::AssemblyHelpers::boxBigInt32):
+        (JSC::AssemblyHelpers::emitTypeOf):
+        * jit/JIT.cpp:
+        (JSC::JIT::privateCompileMainPass):
+        * jit/JIT.h:
+        * jit/JITArithmetic.cpp:
+        (JSC::JIT::emit_op_negate):
+        (JSC::JIT::emitSlow_op_negate):
+        * jit/JITOpcodes.cpp:
+        (JSC::JIT::emit_op_is_big_int):
+        (JSC::JIT::compileOpStrictEq):
+        (JSC::JIT::compileOpStrictEqJump):
+        (JSC::JIT::emit_op_to_numeric):
+        * jit/JITOpcodes32_64.cpp:
+        (JSC::JIT::emit_op_is_big_int):
+        (JSC::JIT::emit_op_to_numeric):
+        * jit/JITOperations.cpp:
+        * jit/JITOperations.h:
+        * llint/LLIntOfflineAsmConfig.h:
+        * llint/LowLevelInterpreter.asm:
+        * llint/LowLevelInterpreter64.asm:
+        * parser/ParserArena.cpp:
+        (JSC::IdentifierArena::makeBigIntDecimalIdentifier):
+        * runtime/ArrayPrototype.cpp:
+        * runtime/BigIntConstructor.cpp:
+        (JSC::toBigInt):
+        (JSC::callBigIntConstructor):
+        * runtime/BigIntObject.cpp:
+        (JSC::BigIntObject::create):
+        (JSC::BigIntObject::finishCreation):
+        * runtime/BigIntObject.h:
+        * runtime/BigIntPrototype.cpp:
+        (JSC::toThisBigIntValue):
+        (JSC::bigIntProtoFuncToStringImpl):
+        * runtime/CommonSlowPaths.cpp:
+        (JSC::SLOW_PATH_DECL):
+        (JSC::updateArithProfileForUnaryArithOp):
+        (JSC::updateArithProfileForBinaryArithOp):
+        * runtime/JSBigInt.cpp:
+        (JSC::JSBigInt::createStructure):
+        (JSC::JSBigInt::parseInt):
+        (JSC::JSBigInt::stringToBigInt):
+        (JSC::JSBigInt::inc):
+        (JSC::JSBigInt::dec):
+        (JSC::JSBigInt::bitwiseAnd):
+        (JSC::JSBigInt::toStringGeneric):
+        (JSC::JSBigInt::equalsToNumber):
+        (JSC::JSBigInt::equalsToInt32):
+        * runtime/JSBigInt.h:
+        (JSC::asHeapBigInt):
+        * runtime/JSCJSValue.cpp:
+        (JSC::JSValue::toNumberSlowCase const):
+        (JSC::JSValue::toObjectSlowCase const):
+        (JSC::JSValue::toThisSlowCase const):
+        (JSC::JSValue::synthesizePrototype const):
+        (JSC::JSValue::dumpInContextAssumingStructure const):
+        (JSC::JSValue::dumpForBacktrace const):
+        (JSC::JSValue::toStringSlowCase const):
+        * runtime/JSCJSValue.h:
+        * runtime/JSCJSValueInlines.h:
+        (JSC::JSValue::JSValue):
+        (JSC::JSValue::asHeapBigInt const):
+        (JSC::JSValue::isBigInt const):
+        (JSC::JSValue::isHeapBigInt const):
+        (JSC::JSValue::isBigInt32 const):
+        (JSC::JSValue::bigInt32AsInt32 const):
+        (JSC::JSValue::isPrimitive const):
+        (JSC::JSValue::getPrimitiveNumber):
+        (JSC::JSValue::toNumeric const):
+        (JSC::JSValue::toBigIntOrInt32 const):
+        (JSC::JSValue::equalSlowCaseInline):
+        (JSC::JSValue::strictEqualForCells):
+        (JSC::JSValue::strictEqual):
+        (JSC::JSValue::pureStrictEqual):
+        (JSC::JSValue::pureToBoolean const):
+        * runtime/JSCell.cpp:
+        (JSC::JSCell::put):
+        (JSC::JSCell::putByIndex):
+        (JSC::JSCell::toPrimitive const):
+        (JSC::JSCell::getPrimitiveNumber const):
+        (JSC::JSCell::toNumber const):
+        (JSC::JSCell::toObjectSlow const):
+        * runtime/JSCell.h:
+        * runtime/JSCellInlines.h:
+        (JSC::JSCell::isHeapBigInt const):
+        (JSC::JSCell::toBoolean const):
+        (JSC::JSCell::pureToBoolean const):
+        * runtime/JSString.h:
+        (JSC::JSValue::toBoolean const):
+        * runtime/JSType.cpp:
+        (WTF::printInternal):
+        * runtime/JSType.h:
+        * runtime/JSTypeInfo.h:
+        * runtime/ObjectInitializationScope.cpp:
+        * runtime/Operations.cpp:
+        (JSC::jsAddSlowCase):
+        (JSC::jsIsObjectTypeOrNull):
+        * runtime/Operations.h:
+        (JSC::compareBigIntToOtherPrimitive):
+        (JSC::bigIntCompare):
+        (JSC::jsLess):
+        (JSC::jsLessEq):
+        (JSC::arithmeticBinaryOp):
+        (JSC::jsSub):
+        (JSC::jsMul):
+        (JSC::jsDiv):
+        (JSC::jsRemainder):
+        (JSC::jsPow):
+        (JSC::jsInc):
+        (JSC::jsDec):
+        (JSC::jsBitwiseNot):
+        (JSC::shift):
+        (JSC::jsLShift):
+        (JSC::jsRShift):
+        (JSC::bitwiseBinaryOp):
+        (JSC::jsBitwiseAnd):
+        (JSC::jsBitwiseOr):
+        (JSC::jsBitwiseXor):
+        * runtime/Scribble.h: Copied from Source/JavaScriptCore/runtime/BigIntObject.h.
+        (JSC::scribbleFreeCells):
+        (JSC::isScribbledValue):
+        (JSC::scribble):
+        * runtime/StructureInlines.h:
+        (JSC::prototypeForLookupPrimitiveImpl):
+
 2020-04-18  Keith Miller  <keith_miller@apple.com>
 
         Unreviewed, remove commented out/dead code that didn't failed to
index f5d0837..70fc99f 100644 (file)
                2AD8932B17E3868F00668276 /* HeapIterationScope.h in Headers */ = {isa = PBXBuildFile; fileRef = 2AD8932917E3868F00668276 /* HeapIterationScope.h */; };
                2AF7382D18BBBF92008A5A37 /* StructureIDTable.h in Headers */ = {isa = PBXBuildFile; fileRef = 2AF7382B18BBBF92008A5A37 /* StructureIDTable.h */; settings = {ATTRIBUTES = (Private, ); }; };
                2D342F36F7244096804ADB24 /* SourceOrigin.h in Headers */ = {isa = PBXBuildFile; fileRef = 425BA1337E4344E1B269A671 /* SourceOrigin.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               33111B8B2397256500AA34CE /* Scribble.h in Headers */ = {isa = PBXBuildFile; fileRef = 33111B8A2397256500AA34CE /* Scribble.h */; settings = {ATTRIBUTES = (Private, ); }; };
                3395C70722555F6D00BDBFAD /* B3EliminateDeadCode.h in Headers */ = {isa = PBXBuildFile; fileRef = 3395C70522555F6D00BDBFAD /* B3EliminateDeadCode.h */; };
                33A920BD23DA2C6D000EBAF0 /* CommonSlowPathsInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = 33A920BC23DA2C6D000EBAF0 /* CommonSlowPathsInlines.h */; settings = {ATTRIBUTES = (Private, ); }; };
                33B2A54722653481005A0F79 /* B3ValueInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FEC84FB1BDACDAC0080FF74 /* B3ValueInlines.h */; };
                2AF7382B18BBBF92008A5A37 /* StructureIDTable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StructureIDTable.h; sourceTree = "<group>"; };
                3032175DF1AD47D8998B34E1 /* JSSourceCode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSSourceCode.h; sourceTree = "<group>"; };
                30A5F403F11C4F599CD596D5 /* WasmSignatureInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WasmSignatureInlines.h; sourceTree = "<group>"; };
+               33111B8A2397256500AA34CE /* Scribble.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Scribble.h; sourceTree = "<group>"; };
                33743649224D79EF00C8C227 /* B3OptimizeAssociativeExpressionTrees.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = B3OptimizeAssociativeExpressionTrees.cpp; path = b3/B3OptimizeAssociativeExpressionTrees.cpp; sourceTree = "<group>"; };
                3374364A224D79EF00C8C227 /* B3OptimizeAssociativeExpressionTrees.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = B3OptimizeAssociativeExpressionTrees.h; path = b3/B3OptimizeAssociativeExpressionTrees.h; sourceTree = "<group>"; };
                3395C70422555F6C00BDBFAD /* B3EliminateDeadCode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = B3EliminateDeadCode.cpp; path = b3/B3EliminateDeadCode.cpp; sourceTree = "<group>"; };
                                0FE050201AA9095600D33B33 /* ScopedArgumentsTable.h */,
                                0FE050211AA9095600D33B33 /* ScopeOffset.cpp */,
                                0FE050221AA9095600D33B33 /* ScopeOffset.h */,
+                               33111B8A2397256500AA34CE /* Scribble.h */,
                                147341E01DC2CE9600AA29BA /* ScriptExecutable.cpp */,
                                147341CD1DC02D7900AA29BA /* ScriptExecutable.h */,
                                8852151A9C3842389B3215B7 /* ScriptFetcher.h */,
                                0FEC85911BDACDC70080FF74 /* AirValidate.h in Headers */,
                                0FEC3C531F33A41600F59B6C /* AlignedMemoryAllocator.h in Headers */,
                                0FA7620B1DB959F900B7A2FD /* AllocatingScope.h in Headers */,
+                               33111B8B2397256500AA34CE /* Scribble.h in Headers */,
                                0FDCE11C1FAE6209006F3901 /* AllocationFailureMode.h in Headers */,
                                0F75A063200D261F0038E2CF /* Allocator.h in Headers */,
                                0F30CB5E1FCE4E37004B5323 /* AllocatorForMode.h in Headers */,
index c100240..3578683 100644 (file)
@@ -4698,7 +4698,7 @@ private:
 
         // Immediates:
         //
-        // An immedaite should be appended where appropriate after an op has been emitted.
+        // An immediate should be appended where appropriate after an op has been emitted.
         // The writes are unchecked since the opcode formatters above will have ensured space.
 
         void immediate8(int imm)
index fea6674..85ec728 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 Apple Inc. All rights reserved.
+ * 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
@@ -33,9 +33,9 @@ namespace JSC {
 
 #if ENABLE(JIT)
 template<typename BitfieldType>
-void ArithProfile<BitfieldType>::emitObserveResult(CCallHelpers& jit, JSValueRegs regs, TagRegistersMode mode)
+void ArithProfile<BitfieldType>::emitObserveResult(CCallHelpers& jit, JSValueRegs regs, GPRReg tempGPR, TagRegistersMode mode)
 {
-    if (!shouldEmitSetDouble() && !shouldEmitSetNonNumeric() && !shouldEmitSetBigInt())
+    if (!shouldEmitSetDouble() && !shouldEmitSetNonNumeric() && !shouldEmitSetHeapBigInt() && !shouldEmitSetBigInt32())
         return;
 
     CCallHelpers::JumpList done;
@@ -48,9 +48,18 @@ void ArithProfile<BitfieldType>::emitObserveResult(CCallHelpers& jit, JSValueReg
 
     notDouble.link(&jit);
 
+#if USE(BIGINT32)
+    CCallHelpers::Jump notBigInt32 = jit.branchIfNotBigInt32KnownNotNumber(regs, tempGPR);
+    emitSetBigInt32(jit);
+    done.append(jit.jump());
+    notBigInt32.link(&jit);
+#else
+    UNUSED_PARAM(tempGPR);
+#endif
+
     nonNumeric.append(jit.branchIfNotCell(regs, mode));
-    nonNumeric.append(jit.branchIfNotBigInt(regs.payloadGPR()));
-    emitSetBigInt(jit);
+    nonNumeric.append(jit.branchIfNotHeapBigInt(regs.payloadGPR()));
+    emitSetHeapBigInt(jit);
     done.append(jit.jump());
 
     nonNumeric.link(&jit);
@@ -88,19 +97,39 @@ void ArithProfile<BitfieldType>::emitSetNonNumeric(CCallHelpers& jit) const
 }
 
 template<typename BitfieldType>
-bool ArithProfile<BitfieldType>::shouldEmitSetBigInt() const
+bool ArithProfile<BitfieldType>::shouldEmitSetBigInt32() const
+{
+#if USE(BIGINT32)
+    BitfieldType mask = ObservedResults::BigInt32;
+    return (m_bits & mask) != mask;
+#else
+    return false;
+#endif
+}
+
+template<typename BitfieldType>
+bool ArithProfile<BitfieldType>::shouldEmitSetHeapBigInt() const
 {
-    BitfieldType mask = ObservedResults::BigInt;
+    BitfieldType mask = ObservedResults::HeapBigInt;
     return (m_bits & mask) != mask;
 }
 
 template<typename BitfieldType>
-void ArithProfile<BitfieldType>::emitSetBigInt(CCallHelpers& jit) const
+void ArithProfile<BitfieldType>::emitSetHeapBigInt(CCallHelpers& jit) const
 {
-    if (shouldEmitSetBigInt())
-        emitUnconditionalSet(jit, ObservedResults::BigInt);
+    if (shouldEmitSetHeapBigInt())
+        emitUnconditionalSet(jit, ObservedResults::HeapBigInt);
 }
 
+#if USE(BIGINT32)
+template<typename BitfieldType>
+void ArithProfile<BitfieldType>::emitSetBigInt32(CCallHelpers& jit) const
+{
+    if (shouldEmitSetBigInt32())
+        emitUnconditionalSet(jit, ObservedResults::BigInt32);
+}
+#endif
+
 template<typename BitfieldType>
 void ArithProfile<BitfieldType>::emitUnconditionalSet(CCallHelpers& jit, BitfieldType mask) const
 {
@@ -149,8 +178,12 @@ void printInternal(PrintStream& out, const ArithProfile<T>& profile)
             out.print(separator, "Int52Overflow");
             separator = "|";
         }
-        if (profile.didObserveBigInt()) {
-            out.print(separator, "BigInt");
+        if (profile.didObserveHeapBigInt()) {
+            out.print(separator, "HeapBigInt");
+            separator = "|";
+        }
+        if (profile.didObserveBigInt32()) {
+            out.print(separator, "BigInt32");
             separator = "|";
         }
     }
index 6add1a2..7c1fe71 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016-2019 Apple Inc. All rights reserved.
+ * 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
@@ -74,21 +74,24 @@ public:
         NonNumeric       = 1 << 2,
         Int32Overflow    = 1 << 3,
         Int52Overflow    = 1 << 4,
-        BigInt           = 1 << 5,
+        HeapBigInt       = 1 << 5,
+        BigInt32         = 1 << 6,
     };
-    static constexpr uint32_t numBitsNeeded = 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 | BigInt); }
+    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 & BigInt; }
+    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; }
 
@@ -109,13 +112,16 @@ public:
     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 setObservedBigInt() { setBit(ObservedResults::BigInt); }
+    void setObservedHeapBigInt() { setBit(ObservedResults::HeapBigInt); }
+    void setObservedBigInt32() { setBit(ObservedResults::BigInt32); }
     void setObservedInt32Overflow() { setBit(ObservedResults::Int32Overflow); }
     void setObservedInt52Overflow() { setBit(ObservedResults::Int52Overflow); }
 
@@ -127,8 +133,12 @@ public:
             m_bits |= ObservedResults::Int32Overflow | ObservedResults::Int52Overflow | ObservedResults::NonNegZeroDouble | ObservedResults::NegZeroDouble;
             return;
         }
-        if (value && value.isBigInt()) {
-            m_bits |= ObservedResults::BigInt;
+        if (value.isBigInt32()) {
+            m_bits |= ObservedResults::BigInt32;
+            return;
+        }
+        if (value && value.isHeapBigInt()) {
+            m_bits |= ObservedResults::HeapBigInt;
             return;
         }
         m_bits |= ObservedResults::NonNumeric;
@@ -139,19 +149,22 @@ public:
 #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);
+    void emitObserveResult(CCallHelpers&, JSValueRegs, GPRReg tempGPR, TagRegistersMode = HaveTagRegisters);
 
     // Sets (Int32Overflow | Int52Overflow | NonNegZeroDouble | NegZeroDouble).
     bool shouldEmitSetDouble() const;
     void emitSetDouble(CCallHelpers&) const;
 
-    // Sets NonNumeric
     void emitSetNonNumeric(CCallHelpers&) const;
     bool shouldEmitSetNonNumeric() const;
 
-    // Sets BigInt
-    void emitSetBigInt(CCallHelpers&) const;
-    bool shouldEmitSetBigInt() 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)
@@ -175,7 +188,7 @@ 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_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));
 
@@ -257,7 +270,7 @@ class BinaryArithProfile : public ArithProfile<BinaryArithProfileBase> {
 
 public:
     static constexpr BinaryArithProfileBase specialFastPathBit = 1 << (lhsObservedTypeShift + ObservedType::numBitsNeeded);
-    static_assert((lhsObservedTypeShift + ObservedType::numBitsNeeded + 1) <= sizeof(BinaryArithProfileBase) * 8, "Should fit in a uint32_t.");
+    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.");
index 0ff2bf6..738beca 100644 (file)
@@ -330,6 +330,7 @@ op_group :UnaryOp,
         :is_undefined_or_null,
         :is_boolean,
         :is_number,
+        :is_big_int,
         :is_object,
         :is_object_or_null,
         :is_function,
index 9c54b43..c60965b 100644 (file)
@@ -29,7 +29,6 @@
 #include "BytecodeLivenessAnalysis.h"
 #include "CodeBlock.h"
 #include "InterpreterInlines.h"
-#include "Operations.h"
 
 namespace JSC {
 
index a5a507e..e31b134 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013-2019 Apple Inc. All rights reserved.
+ * Copyright (C) 2013-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
@@ -174,6 +174,7 @@ void computeUsesForBytecodeIndexImpl(VirtualRegister scopeRegister, const Instru
     USES(OpIsUndefinedOrNull, operand)
     USES(OpIsBoolean, operand)
     USES(OpIsNumber, operand)
+    USES(OpIsBigInt, operand)
     USES(OpIsObject, operand)
     USES(OpIsObjectOrNull, operand)
     USES(OpIsCellWithType, operand)
@@ -426,9 +427,10 @@ void computeDefsForBytecodeIndexImpl(unsigned numVars, const Instruction* instru
     DEFS(OpIdentityWithProfile, srcDst)
     DEFS(OpIsEmpty, dst)
     DEFS(OpIsUndefined, dst)
-    USES(OpIsUndefinedOrNull, dst)
+    DEFS(OpIsUndefinedOrNull, dst)
     DEFS(OpIsBoolean, dst)
     DEFS(OpIsNumber, dst)
+    DEFS(OpIsBigInt, dst)
     DEFS(OpIsObject, dst)
     DEFS(OpIsObjectOrNull, dst)
     DEFS(OpIsCellWithType, dst)
index b39152d..d4acaf8 100644 (file)
@@ -61,7 +61,6 @@
 #include "IsoCellSetInlines.h"
 #include "JIT.h"
 #include "JITMathIC.h"
-#include "JSBigInt.h"
 #include "JSCInlines.h"
 #include "JSCJSValue.h"
 #include "JSFunction.h"
index 32f588d..3ee61f9 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011, 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2011-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
@@ -44,12 +44,14 @@ enum DataFormat : uint8_t {
     DataFormatBoolean = 5,
     DataFormatCell = 6,
     DataFormatStorage = 7,
-    DataFormatJS = 8,
+    DataFormatBigInt32 = 8, // FIXME: currently unused
+    DataFormatJS = 16,
     DataFormatJSInt32 = DataFormatJS | DataFormatInt32,
     DataFormatJSDouble = DataFormatJS | DataFormatDouble,
     DataFormatJSCell = DataFormatJS | DataFormatCell,
     DataFormatJSBoolean = DataFormatJS | DataFormatBoolean,
-    
+    DataFormatJSBigInt32 = DataFormatJS | DataFormatBigInt32,
+
     // Marker deliminating ordinary data formats and OSR-only data formats.
     DataFormatOSRMarker = 32, 
     
index 701c330..830f6d6 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012-2017 Apple Inc. All rights reserved.
+ * Copyright (C) 2012-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
@@ -46,7 +46,7 @@ MethodOfGettingAValueProfile MethodOfGettingAValueProfile::fromLazyOperand(
     return result;
 }
 
-void MethodOfGettingAValueProfile::emitReportValue(CCallHelpers& jit, JSValueRegs regs) const
+void MethodOfGettingAValueProfile::emitReportValue(CCallHelpers& jit, JSValueRegs regs, GPRReg tempGPR, TagRegistersMode mode) const
 {
     switch (m_kind) {
     case None:
@@ -67,12 +67,12 @@ void MethodOfGettingAValueProfile::emitReportValue(CCallHelpers& jit, JSValueReg
     }
         
     case UnaryArithProfileReady: {
-        u.unaryArithProfile->emitObserveResult(jit, regs, DoNotHaveTagRegisters);
+        u.unaryArithProfile->emitObserveResult(jit, regs, tempGPR, mode);
         return;
     }
 
     case BinaryArithProfileReady: {
-        u.binaryArithProfile->emitObserveResult(jit, regs, DoNotHaveTagRegisters);
+        u.binaryArithProfile->emitObserveResult(jit, regs, tempGPR, mode);
         return;
     }
     }
index e8b65ea..3aba1a3 100644 (file)
@@ -34,6 +34,7 @@
 #include "BytecodeIndex.h"
 #include "GPRInfo.h"
 #include "Operands.h"
+#include "TagRegistersMode.h"
 
 namespace JSC {
 
@@ -83,7 +84,8 @@ public:
     
     explicit operator bool() const { return m_kind != None; }
 
-    void emitReportValue(CCallHelpers&, JSValueRegs) const;
+    // The temporary register is only needed on 64-bits builds (for testing BigInt32).
+    void emitReportValue(CCallHelpers&, JSValueRegs, GPRReg tempGPR, TagRegistersMode = HaveTagRegisters) const;
     void reportValue(JSValue);
 
 private:
index 1053ba5..771c79b 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011-2018 Apple Inc. All rights reserved.
+ * Copyright (C) 2011-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
@@ -239,11 +239,6 @@ void dumpSpeculation(PrintStream& outStream, SpeculatedType value)
             strOut.print("Symbol");
         else
             isTop = false;
-
-        if (value & SpecBigInt)
-            strOut.print("BigInt");
-        else
-            isTop = false;
     }
     
     if (value == SpecInt32Only)
@@ -278,7 +273,23 @@ void dumpSpeculation(PrintStream& outStream, SpeculatedType value)
         else
             isTop = false;
     }
-    
+
+    if ((value & SpecBigInt) == SpecBigInt)
+        strOut.print("BigInt");
+#if USE(BIGINT32)
+    else {
+        if (value & SpecBigInt32)
+            strOut.print("BigInt32");
+        else
+            isTop = false;
+
+        if (value & SpecHeapBigInt)
+            strOut.print("HeapBigInt");
+        else
+            isTop = false;
+    }
+#endif
+
     if (value & SpecDoubleImpureNaN)
         strOut.print("DoubleImpureNaN");
     
@@ -431,7 +442,7 @@ SpeculatedType speculationFromClassInfo(const ClassInfo* classInfo)
         return SpecSymbol;
     
     if (classInfo == JSBigInt::info())
-        return SpecBigInt;
+        return SpecHeapBigInt;
 
     if (classInfo == JSFinalObject::info())
         return SpecFinalObject;
@@ -499,8 +510,8 @@ SpeculatedType speculationFromStructure(Structure* structure)
         return SpecString;
     if (structure->typeInfo().type() == SymbolType)
         return SpecSymbol;
-    if (structure->typeInfo().type() == BigIntType)
-        return SpecBigInt;
+    if (structure->typeInfo().type() == HeapBigIntType)
+        return SpecHeapBigInt;
     if (structure->typeInfo().type() == DerivedArrayType)
         return SpecDerivedArray;
     return speculationFromClassInfo(structure->classInfo());
@@ -536,6 +547,8 @@ SpeculatedType speculationFromValue(JSValue value)
             return SpecAnyIntAsDouble;
         return SpecNonIntAsDouble;
     }
+    if (value.isBigInt32())
+        return SpecBigInt32;
     if (value.isCell())
         return speculationFromCell(value.asCell());
     if (value.isBoolean())
@@ -595,8 +608,8 @@ Optional<SpeculatedType> speculationFromJSType(JSType type)
         return SpecString;
     case SymbolType:
         return SpecSymbol;
-    case BigIntType:
-        return SpecBigInt;
+    case HeapBigIntType:
+        return SpecHeapBigInt;
     case ArrayType:
         return SpecArray;
     case DerivedArrayType:
@@ -632,6 +645,10 @@ SpeculatedType leastUpperBoundOfStrictlyEquivalentSpeculations(SpeculatedType ty
 
     if (type & SpecString)
         type |= SpecString;
+
+    if (type & SpecBigInt)
+        type |= SpecBigInt;
+
     return type;
 }
 
index 94658b0..9e3571f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011-2019 Apple Inc. All rights reserved.
+ * Copyright (C) 2011-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
@@ -98,12 +98,14 @@ static constexpr SpeculatedType SpecBoolean                           = 1ull <<
 static constexpr SpeculatedType SpecOther                             = 1ull << 39; // It's definitely either Null or Undefined.
 static constexpr SpeculatedType SpecMisc                              = SpecBoolean | SpecOther; // It's definitely either a boolean, Null, or Undefined.
 static constexpr SpeculatedType SpecEmpty                             = 1ull << 40; // It's definitely an empty value marker.
-static constexpr SpeculatedType SpecBigInt                            = 1ull << 41; // It's definitely a BigInt.
-static constexpr SpeculatedType SpecDataViewObject                    = 1ull << 42; // It's definitely a JSDataView.
+static constexpr SpeculatedType SpecHeapBigInt                        = 1ull << 41; // It's definitely a BigInt that is allocated on the heap
+static constexpr SpeculatedType SpecBigInt32                          = 1ull << 42; // It's definitely a small BigInt that is inline the JSValue
+static constexpr SpeculatedType SpecBigInt                            = SpecBigInt32 | SpecHeapBigInt;
+static constexpr SpeculatedType SpecDataViewObject                    = 1ull << 43; // It's definitely a JSDataView.
 static constexpr SpeculatedType SpecPrimitive                         = SpecString | SpecSymbol | SpecBytecodeNumber | SpecMisc | SpecBigInt; // It's any non-Object JSValue.
 static constexpr SpeculatedType SpecObject                            = SpecFinalObject | SpecArray | SpecFunction | SpecTypedArrayView | SpecDirectArguments | SpecScopedArguments | SpecStringObject | SpecRegExpObject | SpecDateObject | SpecPromiseObject | SpecMapObject | SpecSetObject | SpecWeakMapObject | SpecWeakSetObject | SpecProxyObject | SpecDerivedArray | SpecObjectOther | SpecDataViewObject; // Bitmask used for testing for any kind of object prediction.
-static constexpr SpeculatedType SpecCell                              = SpecObject | SpecString | SpecSymbol | SpecCellOther | SpecBigInt; // It's definitely a JSCell.
-static constexpr SpeculatedType SpecHeapTop                           = SpecCell | SpecBytecodeNumber | SpecMisc; // It can be any of the above, except for SpecInt52Only and SpecDoubleImpureNaN.
+static constexpr SpeculatedType SpecCell                              = SpecObject | SpecString | SpecSymbol | SpecCellOther | SpecHeapBigInt; // It's definitely a JSCell.
+static constexpr SpeculatedType SpecHeapTop                           = SpecCell | SpecBigInt32 | SpecBytecodeNumber | SpecMisc; // It can be any of the above, except for SpecInt52Only and SpecDoubleImpureNaN.
 static constexpr SpeculatedType SpecBytecodeTop                       = SpecHeapTop | SpecEmpty; // It can be any of the above, except for SpecInt52Only and SpecDoubleImpureNaN. Corresponds to what could be found in a bytecode local.
 static constexpr SpeculatedType SpecFullTop                           = SpecBytecodeTop | SpecFullNumber; // It can be anything that bytecode could see plus exotic encodings of numbers.
 
@@ -195,9 +197,19 @@ inline bool isSymbolSpeculation(SpeculatedType value)
     return value == SpecSymbol;
 }
 
+inline bool isBigInt32Speculation(SpeculatedType value)
+{
+    return value == SpecBigInt32;
+}
+
+inline bool isHeapBigIntSpeculation(SpeculatedType value)
+{
+    return value == SpecHeapBigInt;
+}
+
 inline bool isBigIntSpeculation(SpeculatedType value)
 {
-    return value == SpecBigInt;
+    return !!value && (value & SpecBigInt) == value;
 }
 
 inline bool isArraySpeculation(SpeculatedType value)
index 8fede62..c19e61c 100644 (file)
@@ -1703,7 +1703,11 @@ bool BytecodeGenerator::emitEqualityOpImpl(RegisterID* dst, RegisterID* src1, Re
             }
             if (Options::useBigInt() && value == "bigint") {
                 rewind();
-                OpIsCellWithType::emit(this, dst, op.m_value, BigIntType);
+#if USE(BIGINT32)
+                OpIsBigInt::emit(this, dst, op.m_value);
+#else
+                OpIsCellWithType::emit(this, dst, op.m_value, HeapBigIntType);
+#endif
                 return true;
             }
             if (value == "object") {
@@ -2911,7 +2915,7 @@ JSValue BytecodeGenerator::addBigIntConstant(const Identifier& identifier, uint8
     return m_bigIntMap.ensure(BigIntMapEntry(identifier.impl(), radix, sign), [&] {
         auto scope = DECLARE_CATCH_SCOPE(vm());
         auto parseIntSign = sign ? JSBigInt::ParseIntSign::Signed : JSBigInt::ParseIntSign::Unsigned;
-        JSBigInt* bigIntInMap = JSBigInt::parseInt(nullptr, vm(), identifier.string(), radix, JSBigInt::ErrorParseMode::ThrowExceptions, parseIntSign);
+        JSValue bigIntInMap = JSBigInt::parseInt(nullptr, vm(), identifier.string(), radix, JSBigInt::ErrorParseMode::ThrowExceptions, parseIntSign);
         scope.assertNoException();
         addConstantValue(bigIntInMap);
 
index 71e9a5f..397dddc 100644 (file)
@@ -1096,7 +1096,7 @@ namespace JSC {
 
         using NumberMap = HashMap<double, JSValue>;
         using IdentifierStringMap = HashMap<UniquedStringImpl*, JSString*, IdentifierRepHash>;
-        using IdentifierBigIntMap = HashMap<BigIntMapEntry, JSBigInt*>;
+        using IdentifierBigIntMap = HashMap<BigIntMapEntry, JSValue>;
         using TemplateObjectDescriptorSet = HashSet<Ref<TemplateObjectDescriptor>>;
         using TemplateDescriptorMap = HashMap<uint64_t, JSTemplateObjectDescriptor*, WTF::IntHash<uint64_t>, WTF::UnsignedWithZeroKeyHashTraits<uint64_t>>;
 
index f64808c..221a155 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013-2019 Apple Inc. All rights reserved.
+ * Copyright (C) 2013-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
@@ -46,7 +46,6 @@
 #include "JSPromiseConstructor.h"
 #include "MathCommon.h"
 #include "NumberConstructor.h"
-#include "Operations.h"
 #include "PutByIdStatus.h"
 #include "StringObject.h"
 #include "StructureCache.h"
@@ -215,7 +214,7 @@ inline ToThisResult isToThisAnIdentity(VM& vm, ECMAMode ecmaMode, AbstractValue&
         bool overridesToThis = false;
         valueForNode.m_structure.forEach([&](RegisteredStructure structure) {
             TypeInfo type = structure->typeInfo();
-            ASSERT(type.isObject() || type.type() == StringType || type.type() == SymbolType || type.type() == BigIntType);
+            ASSERT(type.isObject() || type.type() == StringType || type.type() == SymbolType || type.type() == HeapBigIntType);
             if (!ecmaMode.isStrict())
                 ASSERT(type.isObject());
             // We don't need to worry about strings/symbols here since either:
@@ -503,7 +502,15 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
             break;
         }
 
-        if (node->child1().useKind() == BigIntUse)
+        if (node->child1().useKind() == BigInt32Use) {
+#if USE(BIGINT32)
+            setTypeForNode(node, SpecBigInt32);
+#else
+            RELEASE_ASSERT_NOT_REACHED();
+#endif
+        } else if (node->child1().useKind() == HeapBigIntUse)
+            setTypeForNode(node, SpecHeapBigInt);
+        else if (node->child1().useKind() == AnyBigIntUse)
             setTypeForNode(node, SpecBigInt);
         else {
             clobberWorld();
@@ -533,7 +540,16 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
         if (handleConstantBinaryBitwiseOp(node))
             break;
 
-        if (node->binaryUseKind() == BigIntUse)
+        // FIXME: this use of binaryUseKind means that we cannot specialize to (for example) a HeapBigInt left-operand and a BigInt32 right-operand.
+        if (node->binaryUseKind() == BigInt32Use) {
+#if USE(BIGINT32)
+            setTypeForNode(node, SpecBigInt32);
+#else
+            RELEASE_ASSERT_NOT_REACHED();
+#endif
+        } else if (node->binaryUseKind() == HeapBigIntUse)
+            setTypeForNode(node, SpecHeapBigInt);
+        else if (node->binaryUseKind() == AnyBigIntUse)
             setTypeForNode(node, SpecBigInt);
         else {
             clobberWorld();
@@ -723,11 +739,14 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
 
     case ValueSub:
     case ValueAdd: {
-        DFG_ASSERT(m_graph, node, node->binaryUseKind() == UntypedUse || node->binaryUseKind() == BigIntUse);
-        if (node->binaryUseKind() == BigIntUse)
+        if (node->binaryUseKind() == HeapBigIntUse)
+            setTypeForNode(node, SpecHeapBigInt);
+        else if (node->binaryUseKind() == AnyBigIntUse || node->binaryUseKind() == BigInt32Use)
             setTypeForNode(node, SpecBigInt);
         else {
+            DFG_ASSERT(m_graph, node, node->binaryUseKind() == UntypedUse);
             clobberWorld();
+            // FIXME: do we really need SpecString here for ValueSub? It seems like we only need it for ValueAdd.
             setTypeForNode(node, SpecString | SpecBytecodeNumber | SpecBigInt);
         }
         break;
@@ -887,6 +906,7 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
     }
         
     case ValueNegate: {
+        // FIXME: we could do much smarter things for BigInts, see ValueAdd/ValueSub.
         clobberWorld();
         setTypeForNode(node, SpecBytecodeNumber | SpecBigInt);
         break;
@@ -959,7 +979,11 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
         case DoubleRepUse:
             setNonCellTypeForNode(node, typeOfDoubleIncOrDec(forNode(node->child1()).m_type));
             break;
-        case BigIntUse:
+        case HeapBigIntUse:
+            setTypeForNode(node, SpecHeapBigInt);
+            break;
+        case AnyBigIntUse:
+        case BigInt32Use:
             setTypeForNode(node, SpecBigInt);
             break;
         default:
@@ -975,7 +999,7 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
         JSValue childY = forNode(node->child2()).value();
         if (childX && childY && childX.isNumber() && childY.isNumber()) {
             // We need to call `didFoldClobberWorld` here because this path is only possible
-            // when node->useKind is UntypedUse. In the case of BigIntUse, children will be
+            // when node->useKind is UntypedUse. In the case of AnyBigIntUse or friends, children will be
             // cleared by `AbstractInterpreter::executeEffects`.
             didFoldClobberWorld();
             // Our boxing scheme here matches what we do in operationValuePow.
@@ -983,7 +1007,9 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
             break;
         }
 
-        if (node->binaryUseKind() == BigIntUse)
+        if (node->binaryUseKind() == HeapBigIntUse)
+            setTypeForNode(node, SpecHeapBigInt);
+        else if (node->binaryUseKind() == AnyBigIntUse || node->binaryUseKind() == BigInt32Use)
             setTypeForNode(node, SpecBigInt);
         else {
             clobberWorld();
@@ -993,7 +1019,10 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
     }
 
     case ValueMul: {
-        if (node->binaryUseKind() == BigIntUse)
+        // FIXME: why is this code not shared with ValueSub?
+        if (node->binaryUseKind() == HeapBigIntUse)
+            setTypeForNode(node, SpecHeapBigInt);
+        else if (node->binaryUseKind() == AnyBigIntUse || node->binaryUseKind() == BigInt32Use)
             setTypeForNode(node, SpecBigInt);
         else {
             clobberWorld();
@@ -1057,7 +1086,9 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
         if (handleConstantDivOp(node))
             break;
 
-        if (node->binaryUseKind() == BigIntUse)
+        if (node->binaryUseKind() == HeapBigIntUse)
+            setTypeForNode(node, SpecHeapBigInt);
+        else if (node->binaryUseKind() == AnyBigIntUse || node->binaryUseKind() == BigInt32Use)
             setTypeForNode(node, SpecBigInt);
         else {
             clobberWorld();
@@ -1381,6 +1412,7 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
     case IsUndefinedOrNull:
     case IsBoolean:
     case IsNumber:
+    case IsBigInt:
     case NumberIsInteger:
     case IsObject:
     case IsObjectOrNull:
@@ -1409,6 +1441,9 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
             case IsNumber:
                 setConstant(node, jsBoolean(child.value().isNumber()));
                 break;
+            case IsBigInt:
+                setConstant(node, jsBoolean(child.value().isBigInt()));
+                break;
             case NumberIsInteger:
                 setConstant(node, jsBoolean(NumberConstructor::isIntegerImpl(child.value())));
                 break;
@@ -1563,7 +1598,22 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
             }
             
             break;
+        case IsBigInt:
+            if (!(child.m_type & ~SpecBigInt)) {
+                setConstant(node, jsBoolean(true));
+                constantWasSet = true;
+                break;
+            }
+
+            if (!(child.m_type & SpecBigInt)) {
+                setConstant(node, jsBoolean(false));
+                constantWasSet = true;
+                break;
+            }
+
+            // FIXME: if the SpeculatedType informs us that we won't have a BigInt32 (or that we won't have a HeapBigInt), then we can transform this node into a IsCellWithType(HeapBigIntType) (or a hypothetical IsBigInt32 node).
 
+            break;
         case NumberIsInteger:
             if (!(child.m_type & ~SpecInt32Only)) {
                 setConstant(node, jsBoolean(true));
@@ -1937,13 +1987,14 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
             }
         }
 
+        // FIXME: why is this check here, and not later (after the type-based replacement by false).
+        // Saam seems to agree that the two checks could be switched, I'll try that in a separate patch
         if (node->isBinaryUseKind(UntypedUse)) {
-            // FIXME: Revisit this condition when introducing BigInt to JSC.
-            auto isNonStringCellConstant = [] (JSValue value) {
-                return value && value.isCell() && !value.isString();
+            auto isNonStringAndNonBigIntCellConstant = [] (JSValue value) {
+                return value && value.isCell() && !value.isString() && !value.isHeapBigInt();
             };
 
-            if (isNonStringCellConstant(left) || isNonStringCellConstant(right)) {
+            if (isNonStringAndNonBigIntCellConstant(left) || isNonStringAndNonBigIntCellConstant(right)) {
                 m_state.setShouldTryConstantFolding(true);
                 setNonCellTypeForNode(node, SpecBoolean);
                 break;
@@ -1958,19 +2009,24 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
         }
         
         if (node->child1() == node->child2()) {
-            if (node->isBinaryUseKind(BooleanUse) ||
-                node->isBinaryUseKind(Int32Use) ||
-                node->isBinaryUseKind(Int52RepUse) ||
-                node->isBinaryUseKind(StringUse) ||
-                node->isBinaryUseKind(StringIdentUse) ||
-                node->isBinaryUseKind(SymbolUse) ||
-                node->isBinaryUseKind(ObjectUse) ||
-                node->isBinaryUseKind(MiscUse, UntypedUse) ||
-                node->isBinaryUseKind(UntypedUse, MiscUse) ||
-                node->isBinaryUseKind(StringIdentUse, NotStringVarUse) ||
-                node->isBinaryUseKind(NotStringVarUse, StringIdentUse) ||
-                node->isBinaryUseKind(StringUse, UntypedUse) ||
-                node->isBinaryUseKind(UntypedUse, StringUse)) {
+            // FIXME: Is there any case not involving NaN where x === x is not guaranteed to return true?
+            // If not I might slightly simplify that check.
+            if (node->isBinaryUseKind(BooleanUse)
+                || node->isBinaryUseKind(Int32Use)
+                || node->isBinaryUseKind(Int52RepUse)
+                || node->isBinaryUseKind(StringUse)
+                || node->isBinaryUseKind(StringIdentUse)
+                || node->isBinaryUseKind(SymbolUse)
+                || node->isBinaryUseKind(ObjectUse)
+                || node->isBinaryUseKind(MiscUse, UntypedUse)
+                || node->isBinaryUseKind(UntypedUse, MiscUse)
+                || node->isBinaryUseKind(StringIdentUse, NotStringVarUse)
+                || node->isBinaryUseKind(NotStringVarUse, StringIdentUse)
+                || node->isBinaryUseKind(StringUse, UntypedUse)
+                || node->isBinaryUseKind(UntypedUse, StringUse)
+                || node->isBinaryUseKind(BigInt32Use)
+                || node->isBinaryUseKind(HeapBigIntUse)
+                || node->isBinaryUseKind(AnyBigIntUse)) {
                 setConstant(node, jsBoolean(true));
                 break;
             }
index 06b1478..34da3b1 100644 (file)
@@ -5722,6 +5722,17 @@ void ByteCodeParser::parseBlock(unsigned limit)
             NEXT_OPCODE(op_is_number);
         }
 
+        case op_is_big_int: {
+#if USE(BIGINT32)
+            auto bytecode = currentInstruction->as<OpIsBigInt>();
+            Node* value = get(bytecode.m_operand);
+            set(bytecode.m_dst, addToGraph(IsBigInt, value));
+            NEXT_OPCODE(op_is_big_int);
+#else
+            RELEASE_ASSERT_NOT_REACHED();
+#endif
+        }
+
         case op_is_cell_with_type: {
             auto bytecode = currentInstruction->as<OpIsCellWithType>();
             Node* value = get(bytecode.m_operand);
index 600332f..65329f5 100644 (file)
@@ -149,6 +149,7 @@ CapabilityLevel capabilityLevel(OpcodeID opcodeID, CodeBlock* codeBlock, const I
     case op_is_undefined_or_null:
     case op_is_boolean:
     case op_is_number:
+    case op_is_big_int:
     case op_is_object:
     case op_is_object_or_null:
     case op_is_cell_with_type:
index 3459535..50bcfe6 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013-2018 Apple Inc. All rights reserved.
+ * Copyright (C) 2013-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
@@ -175,6 +175,7 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
     case IsUndefinedOrNull:
     case IsBoolean:
     case IsNumber:
+    case IsBigInt:
     case NumberIsInteger:
     case IsObject:
     case IsTypedArrayView:
@@ -260,7 +261,7 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
         return;
 
     case ValueBitNot:
-        if (node->child1().useKind() == BigIntUse) {
+        if (node->child1().useKind() == AnyBigIntUse || node->child1().useKind() == BigInt32Use || node->child1().useKind() == HeapBigIntUse) {
             def(PureValue(node));
             return;
         }
@@ -680,7 +681,9 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
         case Int32Use:
         case Int52RepUse:
         case DoubleRepUse:
-        case BigIntUse:
+        case BigInt32Use:
+        case HeapBigIntUse:
+        case AnyBigIntUse:
             def(PureValue(node));
             return;
         case UntypedUse:
@@ -702,7 +705,8 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
     case ValuePow:
     case ValueBitLShift:
     case ValueBitRShift:
-        if (node->isBinaryUseKind(BigIntUse)) {
+        // FIXME: this use of single-argument isBinaryUseKind would prevent us from specializing (for example) for a HeapBigInt left-operand and a BigInt32 right-operand.
+        if (node->isBinaryUseKind(AnyBigIntUse) || node->isBinaryUseKind(BigInt32Use) || node->isBinaryUseKind(HeapBigIntUse)) {
             def(PureValue(node));
             return;
         }
index b3e6385..6a1fd07 100644 (file)
@@ -147,15 +147,14 @@ private:
                     JSValue child1Constant = m_state.forNode(node->child1().node()).value();
                     JSValue child2Constant = m_state.forNode(node->child2().node()).value();
 
-                    // FIXME: Revisit this condition when introducing BigInt to JSC.
-                    auto isNonStringOrBigIntCellConstant = [] (JSValue value) {
-                        return value && value.isCell() && !value.isString() && !value.isBigInt();
+                    auto isNonStringAndNonBigIntCellConstant = [] (JSValue value) {
+                        return value && value.isCell() && !value.isString() && !value.isHeapBigInt();
                     };
 
-                    if (isNonStringOrBigIntCellConstant(child1Constant)) {
+                    if (isNonStringAndNonBigIntCellConstant(child1Constant)) {
                         node->convertToCompareEqPtr(m_graph.freezeStrong(child1Constant.asCell()), node->child2());
                         changed = true;
-                    } else if (isNonStringOrBigIntCellConstant(child2Constant)) {
+                    } else if (isNonStringAndNonBigIntCellConstant(child2Constant)) {
                         node->convertToCompareEqPtr(m_graph.freezeStrong(child2Constant.asCell()), node->child1());
                         changed = true;
                     }
index f51e4e2..e6409a3 100644 (file)
@@ -31,7 +31,6 @@
 #include "DFGClobberize.h"
 #include "DFGGraph.h"
 #include "DFGNode.h"
-#include "Operations.h"
 
 namespace JSC { namespace DFG {
 
@@ -147,6 +146,7 @@ bool doesGC(Graph& graph, Node* node)
     case IsUndefinedOrNull:
     case IsBoolean:
     case IsNumber:
+    case IsBigInt:
     case NumberIsInteger:
     case IsObject:
     case IsObjectOrNull:
index e3abea7..ab7ba99 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012-2019 Apple Inc. All rights reserved.
+ * Copyright (C) 2012-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
@@ -202,14 +202,34 @@ private:
                 fixEdge<Int32Use>(node->child1());
                 fixEdge<Int32Use>(node->child2());
                 node->setResult(NodeResultInt32);
+            } else if (node->child1()->shouldSpeculateHeapBigInt()) {
+                // FIXME: the freezing does not appear useful (since the JSCell is kept alive by vm), but it refuses to compile otherwise.
+                // FIXME: we might optimize inc/dec to a specialized function call instead in that case.
+                node->setOp(op == Inc ? ValueAdd : ValueSub);
+                nodeConstantOne = m_insertionSet.insertNode(m_indexInBlock, SpecHeapBigInt, JSConstant, node->origin, OpInfo(m_graph.freeze(vm().heapBigIntConstantOne.get())));
+                node->children.setChild2(Edge(nodeConstantOne));
+                fixEdge<HeapBigIntUse>(node->child1());
+                fixEdge<HeapBigIntUse>(node->child2());
+                // HeapBigInts are cells, so the default of NodeResultJS is good here
+#if USE(BIGINT32)
+            } else if (node->child1()->shouldSpeculateBigInt32()) {
+                node->setOp(op == Inc ? ValueAdd : ValueSub);
+                nodeConstantOne = m_insertionSet.insertNode(m_indexInBlock, SpecBigInt32, JSConstant, node->origin, OpInfo(m_graph.freeze(JSValue(JSValue::JSBigInt32, 1))));
+                node->children.setChild2(Edge(nodeConstantOne));
+                fixEdge<BigInt32Use>(node->child1());
+                fixEdge<BigInt32Use>(node->child2());
+                // The default of NodeResultJS is good enough for now.
+                // FIXME: consider having a special representation for small BigInts instead.
             } else if (node->child1()->shouldSpeculateBigInt()) {
                 // FIXME: the freezing does not appear useful (since the JSCell is kept alive by vm), but it refuses to compile otherwise.
+                // FIXME: we might optimize inc/dec to a specialized function call instead in that case.
                 node->setOp(op == Inc ? ValueAdd : ValueSub);
-                nodeConstantOne = m_insertionSet.insertNode(m_indexInBlock, SpecBigInt, JSConstant, node->origin, OpInfo(m_graph.freeze(vm().bigIntConstantOne.get())));
+                nodeConstantOne = m_insertionSet.insertNode(m_indexInBlock, SpecBigInt32, JSConstant, node->origin, OpInfo(m_graph.freeze(JSValue(JSValue::JSBigInt32, 1))));
                 node->children.setChild2(Edge(nodeConstantOne));
-                fixEdge<BigIntUse>(node->child1());
-                fixEdge<BigIntUse>(node->child2());
-                // BigInts are currently cells, so the default of NodeResultJS is good here
+                fixEdge<AnyBigIntUse>(node->child1());
+                fixEdge<AnyBigIntUse>(node->child2());
+                // The default of NodeResultJS is good here
+#endif // USE(BIGINT32)
             } else if (node->child1()->shouldSpeculateInt52()) {
                 node->setOp(op == Inc ? ArithAdd : ArithSub);
                 node->setArithMode(Arith::CheckOverflow);
@@ -235,12 +255,20 @@ private:
             Edge& child1 = node->child1();
             Edge& child2 = node->child2();
 
+            if (Node::shouldSpeculateHeapBigInt(child1.node(), child2.node())) {
+                fixEdge<HeapBigIntUse>(child1);
+                fixEdge<HeapBigIntUse>(child2);
+                break;
+            }
+
+#if USE(BIGINT32)
             if (Node::shouldSpeculateBigInt(child1.node(), child2.node())) {
-                fixEdge<BigIntUse>(child1);
-                fixEdge<BigIntUse>(child2);
+                fixEdge<AnyBigIntUse>(child1);
+                fixEdge<AnyBigIntUse>(child2);
                 break; 
             }
-            
+#endif
+
             if (Node::shouldSpeculateUntypedForArithmetic(node->child1().node(), node->child2().node())) {
                 fixEdge<UntypedUse>(child1);
                 fixEdge<UntypedUse>(child2);
@@ -268,8 +296,21 @@ private:
         case ValueBitOr:
         case ValueBitAnd: {
             if (Node::shouldSpeculateBigInt(node->child1().node(), node->child2().node())) {
-                fixEdge<BigIntUse>(node->child1());
-                fixEdge<BigIntUse>(node->child2());
+#if USE(BIGINT32)
+                if (Node::shouldSpeculateBigInt32(node->child1().node(), node->child2().node())) {
+                    fixEdge<BigInt32Use>(node->child1());
+                    fixEdge<BigInt32Use>(node->child2());
+                } else if (Node::shouldSpeculateHeapBigInt(node->child1().node(), node->child2().node())) {
+                    fixEdge<HeapBigIntUse>(node->child1());
+                    fixEdge<HeapBigIntUse>(node->child2());
+                } else {
+                    fixEdge<AnyBigIntUse>(node->child1());
+                    fixEdge<AnyBigIntUse>(node->child2());
+                }
+#else
+                fixEdge<HeapBigIntUse>(node->child1());
+                fixEdge<HeapBigIntUse>(node->child2());
+#endif
                 node->clearFlags(NodeMustGenerate);
                 break;
             }
@@ -313,7 +354,17 @@ private:
 
             if (operandEdge.node()->shouldSpeculateBigInt()) {
                 node->clearFlags(NodeMustGenerate);
-                fixEdge<BigIntUse>(operandEdge);
+
+#if USE(BIGINT32)
+                if (operandEdge.node()->shouldSpeculateBigInt32())
+                    fixEdge<BigInt32Use>(operandEdge);
+                else if (operandEdge.node()->shouldSpeculateHeapBigInt())
+                    fixEdge<HeapBigIntUse>(operandEdge);
+                else
+                    fixEdge<AnyBigIntUse>(operandEdge);
+#else
+                fixEdge<HeapBigIntUse>(operandEdge);
+#endif
             } else if (operandEdge.node()->shouldSpeculateUntypedForBitOps())
                 fixEdge<UntypedUse>(operandEdge);
             else {
@@ -458,9 +509,17 @@ private:
                 }
             }
 
-            if (Node::shouldSpeculateBigInt(child1.node(), child2.node())) {
-                fixEdge<BigIntUse>(child1);
-                fixEdge<BigIntUse>(child2);
+            if (Node::shouldSpeculateHeapBigInt(child1.node(), child2.node())) {
+                fixEdge<HeapBigIntUse>(child1);
+                fixEdge<HeapBigIntUse>(child2);
+#if USE(BIGINT32)
+            } else if (Node::shouldSpeculateBigInt32(child1.node(), child2.node())) {
+                fixEdge<BigInt32Use>(child1);
+                fixEdge<BigInt32Use>(child2);
+            } else if (Node::shouldSpeculateBigInt(child1.node(), child2.node())) {
+                fixEdge<AnyBigIntUse>(child1);
+                fixEdge<AnyBigIntUse>(child2);
+#endif
             } else {
                 fixEdge<UntypedUse>(child1);
                 fixEdge<UntypedUse>(child2);
@@ -546,8 +605,21 @@ private:
             Edge& rightChild = node->child2();
 
             if (Node::shouldSpeculateBigInt(leftChild.node(), rightChild.node())) {
-                fixEdge<BigIntUse>(node->child1());
-                fixEdge<BigIntUse>(node->child2());
+#if USE(BIGINT32)
+                if (Node::shouldSpeculateBigInt32(leftChild.node(), rightChild.node())) {
+                    fixEdge<BigInt32Use>(node->child1());
+                    fixEdge<BigInt32Use>(node->child2());
+                } else if (Node::shouldSpeculateHeapBigInt(leftChild.node(), rightChild.node())) {
+                    fixEdge<HeapBigIntUse>(node->child1());
+                    fixEdge<HeapBigIntUse>(node->child2());
+                } else {
+                    fixEdge<AnyBigIntUse>(node->child1());
+                    fixEdge<AnyBigIntUse>(node->child2());
+                }
+#else
+                fixEdge<HeapBigIntUse>(node->child1());
+                fixEdge<HeapBigIntUse>(node->child2());
+#endif
                 node->clearFlags(NodeMustGenerate);
                 break;
             }
@@ -588,12 +660,20 @@ private:
             Edge& leftChild = node->child1();
             Edge& rightChild = node->child2();
 
+            if (Node::shouldSpeculateHeapBigInt(leftChild.node(), rightChild.node())) {
+                fixEdge<HeapBigIntUse>(leftChild);
+                fixEdge<HeapBigIntUse>(rightChild);
+                node->clearFlags(NodeMustGenerate);
+                break;
+            }
+#if USE(BIGINT32)
             if (Node::shouldSpeculateBigInt(leftChild.node(), rightChild.node())) {
-                fixEdge<BigIntUse>(leftChild);
-                fixEdge<BigIntUse>(rightChild);
+                fixEdge<AnyBigIntUse>(leftChild);
+                fixEdge<AnyBigIntUse>(rightChild);
                 node->clearFlags(NodeMustGenerate);
                 break; 
             }
+#endif
 
             if (Node::shouldSpeculateUntypedForArithmetic(leftChild.node(), rightChild.node())) {
                 fixEdge<UntypedUse>(leftChild);
@@ -657,12 +737,20 @@ private:
         }
 
         case ValuePow: {
+            if (Node::shouldSpeculateHeapBigInt(node->child1().node(), node->child1().node())) {
+                fixEdge<HeapBigIntUse>(node->child1());
+                fixEdge<HeapBigIntUse>(node->child2());
+                node->clearFlags(NodeMustGenerate);
+                break;
+            }
+#if USE(BIGINT32)
             if (Node::shouldSpeculateBigInt(node->child1().node(), node->child2().node())) {
-                fixEdge<BigIntUse>(node->child1());
-                fixEdge<BigIntUse>(node->child2());
+                fixEdge<AnyBigIntUse>(node->child1());
+                fixEdge<AnyBigIntUse>(node->child2());
                 node->clearFlags(NodeMustGenerate);
                 break;
             }
+#endif
 
             if (Node::shouldSpeculateUntypedForArithmetic(node->child1().node(), node->child2().node())) {
                 fixEdge<UntypedUse>(node->child1());
@@ -2548,6 +2636,7 @@ private:
         case IsUndefinedOrNull:
         case IsBoolean:
         case IsNumber:
+        case IsBigInt:
         case IsObjectOrNull:
         case IsFunction:
         case CreateDirectArguments:
@@ -2889,7 +2978,16 @@ private:
             }
             
             if (node->child1()->shouldSpeculateBigInt()) {
-                fixEdge<BigIntUse>(node->child1());
+#if USE(BIGINT32)
+                if (node->child1()->shouldSpeculateBigInt32())
+                    fixEdge<BigInt32Use>(node->child1());
+                else if (node->child1()->shouldSpeculateHeapBigInt())
+                    fixEdge<HeapBigIntUse>(node->child1());
+                else
+                    fixEdge<AnyBigIntUse>(node->child1());
+#else
+                fixEdge<HeapBigIntUse>(node->child1());
+#endif
                 node->convertToIdentity();
                 return;
             }
@@ -2962,7 +3060,16 @@ private:
     {
         // If the prediction of the child is BigInt, we attempt to convert ToNumeric to Identity, since it can only return a BigInt when fed a BigInt.
         if (node->child1()->shouldSpeculateBigInt()) {
-            fixEdge<BigIntUse>(node->child1());
+#if USE(BIGINT32)
+            if (node->child1()->shouldSpeculateBigInt32())
+                fixEdge<BigInt32Use>(node->child1());
+            else if (node->child1()->shouldSpeculateHeapBigInt())
+                fixEdge<HeapBigIntUse>(node->child1());
+            else
+                fixEdge<AnyBigIntUse>(node->child1());
+#else
+            fixEdge<HeapBigIntUse>(node->child1());
+#endif
             node->convertToIdentity();
             return;
         }
@@ -3456,13 +3563,15 @@ private:
         case StringUse:
         case KnownStringUse:
         case SymbolUse:
-        case BigIntUse:
+        case HeapBigIntUse:
         case StringObjectUse:
         case StringOrStringObjectUse:
             if (alwaysUnboxSimplePrimitives()
                 || isCellSpeculation(variable->prediction()))
                 m_profitabilityChanged |= variable->mergeIsProfitableToUnbox(true);
             break;
+        // FIXME: what should we do about BigInt32Use and AnyBigIntUse ?
+        // Also more broadly, what about all of the UseKinds which are not listed in that switch?
         default:
             break;
         }
@@ -3976,12 +4085,26 @@ private:
             node->setOpAndDefaultFlags(CompareStrictEq);
             return;
         }
+        if (Node::shouldSpeculateHeapBigInt(node->child1().node(), node->child2().node())) {
+            fixEdge<HeapBigIntUse>(node->child1());
+            fixEdge<HeapBigIntUse>(node->child2());
+            node->setOpAndDefaultFlags(CompareStrictEq);
+            return;
+        }
+#if USE(BIGINT32)
+        if (Node::shouldSpeculateBigInt32(node->child1().node(), node->child2().node())) {
+            fixEdge<BigInt32Use>(node->child1());
+            fixEdge<BigInt32Use>(node->child2());
+            node->setOpAndDefaultFlags(CompareStrictEq);
+            return;
+        }
         if (Node::shouldSpeculateBigInt(node->child1().node(), node->child2().node())) {
-            fixEdge<BigIntUse>(node->child1());
-            fixEdge<BigIntUse>(node->child2());
+            fixEdge<AnyBigIntUse>(node->child1());
+            fixEdge<AnyBigIntUse>(node->child2());
             node->setOpAndDefaultFlags(CompareStrictEq);
             return;
         }
+#endif
         if (node->child1()->shouldSpeculateStringIdent() && node->child2()->shouldSpeculateStringIdent()) {
             fixEdge<StringIdentUse>(node->child1());
             fixEdge<StringIdentUse>(node->child2());
index 944d256..ad861ad 100644 (file)
@@ -1540,7 +1540,7 @@ FrozenValue* Graph::bottomValueMatchingSpeculation(SpeculatedType prediction)
         return freeze(jsNumber(0));
 
     if (speculationContains(prediction, SpecBigInt))
-        return freeze(m_vm.bigIntConstantOne.get());
+        return freeze(m_vm.heapBigIntConstantOne.get());
 
     if (speculationContains(prediction, SpecString | SpecSymbol))
         return freeze(m_vm.smallStrings.emptyString());
index 42794c7..892e5dc 100644 (file)
@@ -32,7 +32,6 @@
 #include "DFGGraph.h"
 #include "DFGNode.h"
 #include "DFGNullAbstractState.h"
-#include "Operations.h"
 
 namespace JSC { namespace DFG {
 
index aba21ed..9f98095 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011-2018 Apple Inc. All rights reserved.
+ * Copyright (C) 2011-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
@@ -2622,6 +2622,18 @@ public:
     {
         return isSymbolSpeculation(prediction());
     }
+
+#if USE(BIGINT32)
+    bool shouldSpeculateBigInt32()
+    {
+        return isBigInt32Speculation(prediction());
+    }
+#endif
+
+    bool shouldSpeculateHeapBigInt()
+    {
+        return isHeapBigIntSpeculation(prediction());
+    }
     
     bool shouldSpeculateBigInt()
     {
@@ -2822,7 +2834,19 @@ public:
     {
         return op1->shouldSpeculateBigInt() && op2->shouldSpeculateBigInt();
     }
-    
+
+#if USE(BIGINT32)
+    static bool shouldSpeculateBigInt32(Node* op1, Node* op2)
+    {
+        return op1->shouldSpeculateBigInt32() && op2->shouldSpeculateBigInt32();
+    }
+#endif
+
+    static bool shouldSpeculateHeapBigInt(Node* op1, Node* op2)
+    {
+        return op1->shouldSpeculateHeapBigInt() && op2->shouldSpeculateHeapBigInt();
+    }
+
     static bool shouldSpeculateFinalObject(Node* op1, Node* op2)
     {
         return op1->shouldSpeculateFinalObject() && op2->shouldSpeculateFinalObject();
index 7e96a26..b0ba6bb 100644 (file)
@@ -54,6 +54,7 @@ namespace JSC { namespace DFG {
 #define NodeMayNegZeroInBaseline         0x00200
 #define NodeMayNegZeroInDFG              0x00400
 #define NodeMayHaveNonNumericResult      0x00800
+// FIXME: we should have separate flags for HeapBigInt and BigInt32, currently prediction propagation will pessimize things.
 #define NodeMayHaveBigIntResult          0x01000
 #define NodeMayHaveNonIntResult          (NodeMayHaveDoubleResult | NodeMayHaveNonNumericResult | NodeMayHaveBigIntResult)
                                 
index 1cd6e78..de35762 100644 (file)
@@ -395,6 +395,8 @@ namespace JSC { namespace DFG {
     macro(IsUndefinedOrNull, NodeResultBoolean) \
     macro(IsBoolean, NodeResultBoolean) \
     macro(IsNumber, NodeResultBoolean) \
+    /* IsBigInt is only used when USE_BIGINT32. Otherwise we emit IsCellWithType */\
+    macro(IsBigInt, NodeResultBoolean) \
     macro(NumberIsInteger, NodeResultBoolean) \
     macro(IsObject, NodeResultBoolean) \
     macro(IsObjectOrNull, NodeResultBoolean) \
index a852dbd..f6372ad 100644 (file)
@@ -383,10 +383,13 @@ void OSRExit::compileExit(CCallHelpers& jit, VM& vm, const OSRExit& exit, const
                 // We can't be sure that we have a spare register. So use the numberTagRegister,
                 // since we know how to restore it.
                 jit.load64(AssemblyHelpers::Address(exit.m_jsValueSource.asAddress()), GPRInfo::numberTagRegister);
-                profile.emitReportValue(jit, JSValueRegs(GPRInfo::numberTagRegister));
+                // We also use the notCellMaskRegister as the scratch register, for the same reason.
+                // FIXME: find a less gross way of doing this, maybe through delaying these operations until we actually have some spare registers around?
+                profile.emitReportValue(jit, JSValueRegs(GPRInfo::numberTagRegister), GPRInfo::notCellMaskRegister, DoNotHaveTagRegisters);
                 jit.move(AssemblyHelpers::TrustedImm64(JSValue::NumberTag), GPRInfo::numberTagRegister);
             } else
-                profile.emitReportValue(jit, JSValueRegs(exit.m_jsValueSource.gpr()));
+                profile.emitReportValue(jit, JSValueRegs(exit.m_jsValueSource.gpr()), GPRInfo::notCellMaskRegister, DoNotHaveTagRegisters);
+            jit.move(AssemblyHelpers::TrustedImm64(JSValue::NotCellMask), GPRInfo::notCellMaskRegister);
 #else // not USE(JSVALUE64)
             if (exit.m_jsValueSource.isAddress()) {
                 // Save a register so we can use it.
@@ -398,7 +401,7 @@ void OSRExit::compileExit(CCallHelpers& jit, VM& vm, const OSRExit& exit, const
                 JSValueRegs scratch(scratchTag, scratchPayload);
                 
                 jit.loadValue(exit.m_jsValueSource.asAddress(), scratch);
-                profile.emitReportValue(jit, scratch);
+                profile.emitReportValue(jit, scratch, InvalidGPRReg);
                 
                 jit.popToRestore(scratchTag);
                 jit.popToRestore(scratchPayload);
@@ -407,10 +410,10 @@ void OSRExit::compileExit(CCallHelpers& jit, VM& vm, const OSRExit& exit, const
                 jit.pushToSave(scratchTag);
                 jit.move(AssemblyHelpers::TrustedImm32(exit.m_jsValueSource.tag()), scratchTag);
                 JSValueRegs value(scratchTag, exit.m_jsValueSource.payloadGPR());
-                profile.emitReportValue(jit, value);
+                profile.emitReportValue(jit, value, InvalidGPRReg);
                 jit.popToRestore(scratchTag);
             } else
-                profile.emitReportValue(jit, exit.m_jsValueSource.regs());
+                profile.emitReportValue(jit, exit.m_jsValueSource.regs(), InvalidGPRReg);
 #endif // USE(JSVALUE64)
         }
     }
index 6eaf70d..866acf8 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011-2019 Apple Inc. All rights reserved.
+ * Copyright (C) 2011-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
@@ -89,7 +89,6 @@
 #include "TypedArrayInlines.h"
 #include "VMInlines.h"
 #include <wtf/InlineASM.h>
-#include <wtf/Variant.h>
 
 #if ENABLE(JIT)
 #if ENABLE(DFG_JIT)
@@ -224,56 +223,6 @@ static ALWAYS_INLINE void putWithThis(JSGlobalObject* globalObject, EncodedJSVal
     baseValue.putInline(globalObject, ident, putValue, slot);
 }
 
-template<typename BigIntOperation, typename NumberOperation>
-static ALWAYS_INLINE EncodedJSValue binaryOp(JSGlobalObject* globalObject, VM& vm, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, BigIntOperation&& bigIntOp, NumberOperation&& numberOp, const char* errorMessage)
-{
-    auto scope = DECLARE_THROW_SCOPE(vm);
-
-    JSValue op1 = JSValue::decode(encodedOp1);
-    JSValue op2 = JSValue::decode(encodedOp2);
-
-    auto leftNumeric = op1.toNumeric(globalObject);
-    RETURN_IF_EXCEPTION(scope, encodedJSValue());
-    auto rightNumeric = op2.toNumeric(globalObject);
-    RETURN_IF_EXCEPTION(scope, encodedJSValue());
-
-    if (WTF::holds_alternative<JSBigInt*>(leftNumeric) || WTF::holds_alternative<JSBigInt*>(rightNumeric)) {
-        if (WTF::holds_alternative<JSBigInt*>(leftNumeric) && WTF::holds_alternative<JSBigInt*>(rightNumeric))
-            RELEASE_AND_RETURN(scope, JSValue::encode(bigIntOp(globalObject, WTF::get<JSBigInt*>(leftNumeric), WTF::get<JSBigInt*>(rightNumeric))));
-
-        return throwVMTypeError(globalObject, scope, errorMessage);
-    }
-
-    scope.release();
-
-    return JSValue::encode(jsNumber(numberOp(WTF::get<double>(leftNumeric), WTF::get<double>(rightNumeric))));
-}
-
-template<typename BigIntOperation, typename Int32Operation>
-static ALWAYS_INLINE EncodedJSValue bitwiseBinaryOp(JSGlobalObject* globalObject, VM& vm, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, BigIntOperation&& bigIntOp, Int32Operation&& int32Op, const char* errorMessage)
-{
-    auto scope = DECLARE_THROW_SCOPE(vm);
-
-    JSValue op1 = JSValue::decode(encodedOp1);
-    JSValue op2 = JSValue::decode(encodedOp2);
-
-    auto leftNumeric = op1.toBigIntOrInt32(globalObject);
-    RETURN_IF_EXCEPTION(scope, encodedJSValue());
-    auto rightNumeric = op2.toBigIntOrInt32(globalObject);
-    RETURN_IF_EXCEPTION(scope, encodedJSValue());
-
-    if (WTF::holds_alternative<JSBigInt*>(leftNumeric) || WTF::holds_alternative<JSBigInt*>(rightNumeric)) {
-        if (WTF::holds_alternative<JSBigInt*>(leftNumeric) && WTF::holds_alternative<JSBigInt*>(rightNumeric))
-            RELEASE_AND_RETURN(scope, JSValue::encode(bigIntOp(globalObject, WTF::get<JSBigInt*>(leftNumeric), WTF::get<JSBigInt*>(rightNumeric))));
-
-        return throwVMTypeError(globalObject, scope, errorMessage);
-    }
-
-    scope.release();
-
-    return JSValue::encode(jsNumber(int32Op(WTF::get<int32_t>(leftNumeric), WTF::get<int32_t>(rightNumeric))));
-}
-
 static ALWAYS_INLINE EncodedJSValue parseIntResult(double input)
 {
     int asInt = static_cast<int>(input);
@@ -473,71 +422,34 @@ EncodedJSValue JIT_OPERATION operationValueMod(JSGlobalObject* globalObject, Enc
     CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
     JITOperationPrologueCallFrameTracer tracer(vm, callFrame);
 
-    auto bigIntOp = [] (JSGlobalObject* globalObject, JSBigInt* left, JSBigInt* right) -> JSBigInt* {
-        return JSBigInt::remainder(globalObject, left, right);
-    };
-
-    auto numberOp = [] (double left, double right) -> double {
-        return jsMod(left, right);
-    };
-
-    return binaryOp(globalObject, vm, encodedOp1, encodedOp2, bigIntOp, numberOp, "Invalid mix of BigInt and other type in remainder operation.");
+    return JSValue::encode(jsRemainder(globalObject, JSValue::decode(encodedOp1), JSValue::decode(encodedOp2)));
 }
 
-EncodedJSValue JIT_OPERATION operationInc(JSGlobalObject* globalObject, EncodedJSValue encodedOp1)
+EncodedJSValue JIT_OPERATION operationInc(JSGlobalObject* globalObject, EncodedJSValue encodedOp)
 {
     VM& vm = globalObject->vm();
     CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
     JITOperationPrologueCallFrameTracer tracer(vm, callFrame);
-    auto scope = DECLARE_THROW_SCOPE(vm);
 
-    JSValue op1 = JSValue::decode(encodedOp1);
-
-    auto operandNumeric = op1.toNumeric(globalObject);
-    RETURN_IF_EXCEPTION(scope, encodedJSValue());
-
-    if (WTF::holds_alternative<JSBigInt*>(operandNumeric))
-        RELEASE_AND_RETURN(scope, JSValue::encode(JSBigInt::inc(globalObject, WTF::get<JSBigInt*>(operandNumeric))));
-
-    double value = WTF::get<double>(operandNumeric);
-    return JSValue::encode(jsNumber(value + 1));
+    return JSValue::encode(jsInc(globalObject, JSValue::decode(encodedOp)));
 }
 
-EncodedJSValue JIT_OPERATION operationDec(JSGlobalObject* globalObject, EncodedJSValue encodedOp1)
+EncodedJSValue JIT_OPERATION operationDec(JSGlobalObject* globalObject, EncodedJSValue encodedOp)
 {
     VM& vm = globalObject->vm();
     CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
     JITOperationPrologueCallFrameTracer tracer(vm, callFrame);
-    auto scope = DECLARE_THROW_SCOPE(vm);
 
-    JSValue op1 = JSValue::decode(encodedOp1);
-
-    auto operandNumeric = op1.toNumeric(globalObject);
-    RETURN_IF_EXCEPTION(scope, encodedJSValue());
-
-    if (WTF::holds_alternative<JSBigInt*>(operandNumeric))
-        RELEASE_AND_RETURN(scope, JSValue::encode(JSBigInt::dec(globalObject, WTF::get<JSBigInt*>(operandNumeric))));
-
-    double value = WTF::get<double>(operandNumeric);
-    return JSValue::encode(jsNumber(value - 1));
+    return JSValue::encode(jsDec(globalObject, JSValue::decode(encodedOp)));
 }
 
-EncodedJSValue JIT_OPERATION operationValueBitNot(JSGlobalObject* globalObject, EncodedJSValue encodedOp1)
+EncodedJSValue JIT_OPERATION operationValueBitNot(JSGlobalObject* globalObject, EncodedJSValue encodedOp)
 {
     VM& vm = globalObject->vm();
     CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
     JITOperationPrologueCallFrameTracer tracer(vm, callFrame);
-    auto scope = DECLARE_THROW_SCOPE(vm);
-
-    JSValue op1 = JSValue::decode(encodedOp1);
 
-    auto operandNumeric = op1.toBigIntOrInt32(globalObject);
-    RETURN_IF_EXCEPTION(scope, encodedJSValue());
-
-    if (WTF::holds_alternative<JSBigInt*>(operandNumeric))
-        RELEASE_AND_RETURN(scope, JSValue::encode(JSBigInt::bitwiseNot(globalObject, WTF::get<JSBigInt*>(operandNumeric))));
-
-    return JSValue::encode(jsNumber(~WTF::get<int32_t>(operandNumeric)));
+    return JSValue::encode(jsBitwiseNot(globalObject, JSValue::decode(encodedOp)));
 }
 
 EncodedJSValue JIT_OPERATION operationValueBitAnd(JSGlobalObject* globalObject, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
@@ -546,15 +458,7 @@ EncodedJSValue JIT_OPERATION operationValueBitAnd(JSGlobalObject* globalObject,
     CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
     JITOperationPrologueCallFrameTracer tracer(vm, callFrame);
 
-    auto bigIntOp = [] (JSGlobalObject* globalObject, JSBigInt* left, JSBigInt* right) -> JSBigInt* {
-        return JSBigInt::bitwiseAnd(globalObject, left, right);
-    };
-
-    auto int32Op = [] (int32_t left, int32_t right) -> int32_t {
-        return left & right;
-    };
-
-    return bitwiseBinaryOp(globalObject, vm, encodedOp1, encodedOp2, bigIntOp, int32Op, "Invalid mix of BigInt and other type in bitwise 'and' operation."_s);
+    return JSValue::encode(jsBitwiseAnd(globalObject, JSValue::decode(encodedOp1), JSValue::decode(encodedOp2)));
 }
 
 EncodedJSValue JIT_OPERATION operationValueBitOr(JSGlobalObject* globalObject, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
@@ -563,15 +467,7 @@ EncodedJSValue JIT_OPERATION operationValueBitOr(JSGlobalObject* globalObject, E
     CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
     JITOperationPrologueCallFrameTracer tracer(vm, callFrame);
 
-    auto bigIntOp = [] (JSGlobalObject* globalObject, JSBigInt* left, JSBigInt* right) -> JSBigInt* {
-        return JSBigInt::bitwiseOr(globalObject, left, right);
-    };
-
-    auto int32Op = [] (int32_t left, int32_t right) -> int32_t {
-        return left | right;
-    };
-
-    return bitwiseBinaryOp(globalObject, vm, encodedOp1, encodedOp2, bigIntOp, int32Op, "Invalid mix of BigInt and other type in bitwise 'or' operation."_s);
+    return JSValue::encode(jsBitwiseOr(globalObject, JSValue::decode(encodedOp1), JSValue::decode(encodedOp2)));
 }
 
 EncodedJSValue JIT_OPERATION operationValueBitXor(JSGlobalObject* globalObject, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
@@ -580,15 +476,7 @@ EncodedJSValue JIT_OPERATION operationValueBitXor(JSGlobalObject* globalObject,
     CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
     JITOperationPrologueCallFrameTracer tracer(vm, callFrame);
 
-    auto bigIntOp = [] (JSGlobalObject* globalObject, JSBigInt* left, JSBigInt* right) -> JSBigInt* {
-        return JSBigInt::bitwiseXor(globalObject, left, right);
-    };
-
-    auto int32Op = [] (int32_t left, int32_t right) -> int32_t {
-        return left ^ right;
-    };
-
-    return bitwiseBinaryOp(globalObject, vm, encodedOp1, encodedOp2, bigIntOp, int32Op, "Invalid mix of BigInt and other type in bitwise 'xor' operation."_s);
+    return JSValue::encode(jsBitwiseXor(globalObject, JSValue::decode(encodedOp1), JSValue::decode(encodedOp2)));
 }
 
 EncodedJSValue JIT_OPERATION operationValueBitLShift(JSGlobalObject* globalObject, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
@@ -597,15 +485,7 @@ EncodedJSValue JIT_OPERATION operationValueBitLShift(JSGlobalObject* globalObjec
     CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
     JITOperationPrologueCallFrameTracer tracer(vm, callFrame);
 
-    auto bigIntOp = [] (JSGlobalObject* globalObject, JSBigInt* left, JSBigInt* right) -> JSBigInt* {
-        return JSBigInt::leftShift(globalObject, left, right);
-    };
-
-    auto int32Op = [] (int32_t left, int32_t right) -> int32_t {
-        return left << (right & 0x1f);
-    };
-
-    return bitwiseBinaryOp(globalObject, vm, encodedOp1, encodedOp2, bigIntOp, int32Op, "Invalid mix of BigInt and other type in left shift operation."_s);
+    return JSValue::encode(jsLShift(globalObject, JSValue::decode(encodedOp1), JSValue::decode(encodedOp2)));
 }
 
 EncodedJSValue JIT_OPERATION operationValueBitRShift(JSGlobalObject* globalObject, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
@@ -614,15 +494,7 @@ EncodedJSValue JIT_OPERATION operationValueBitRShift(JSGlobalObject* globalObjec
     CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
     JITOperationPrologueCallFrameTracer tracer(vm, callFrame);
 
-    auto bigIntOp = [] (JSGlobalObject* globalObject, JSBigInt* left, JSBigInt* right) -> JSBigInt* {
-        return JSBigInt::signedRightShift(globalObject, left, right);
-    };
-
-    auto int32Op = [] (int32_t left, int32_t right) -> int32_t {
-        return left >> (right & 0x1f);
-    };
-
-    return bitwiseBinaryOp(globalObject, vm, encodedOp1, encodedOp2, bigIntOp, int32Op, "Invalid mix of BigInt and other type in signed right shift operation."_s);
+    return JSValue::encode(jsRShift(globalObject, JSValue::decode(encodedOp1), JSValue::decode(encodedOp2)));
 }
 
 EncodedJSValue JIT_OPERATION operationValueBitURShift(JSGlobalObject* globalObject, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
@@ -660,15 +532,7 @@ EncodedJSValue JIT_OPERATION operationValueDiv(JSGlobalObject* globalObject, Enc
     CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
     JITOperationPrologueCallFrameTracer tracer(vm, callFrame);
 
-    auto bigIntOp = [] (JSGlobalObject* globalObject, JSBigInt* left, JSBigInt* right) -> JSBigInt* {
-        return JSBigInt::divide(globalObject, left, right);
-    };
-
-    auto numberOp = [] (double left, double right) -> double {
-        return left / right;
-    };
-
-    return binaryOp(globalObject, vm, encodedOp1, encodedOp2, bigIntOp, numberOp, "Invalid mix of BigInt and other type in division operation.");
+    return JSValue::encode(jsDiv(globalObject, JSValue::decode(encodedOp1), JSValue::decode(encodedOp2)));
 }
 
 EncodedJSValue JIT_OPERATION operationValuePow(JSGlobalObject* globalObject, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
@@ -677,15 +541,7 @@ EncodedJSValue JIT_OPERATION operationValuePow(JSGlobalObject* globalObject, Enc
     CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
     JITOperationPrologueCallFrameTracer tracer(vm, callFrame);
 
-    auto bigIntOp = [] (JSGlobalObject* globalObject, JSBigInt* left, JSBigInt* right) -> JSBigInt* {
-        return JSBigInt::exponentiate(globalObject, left, right);
-    };
-
-    auto numberOp = [] (double left, double right) -> double {
-        return operationMathPow(left, right);
-    };
-
-    return binaryOp(globalObject, vm, encodedOp1, encodedOp2, bigIntOp, numberOp, "Invalid mix of BigInt and other type in exponentiation operation."_s);
+    return JSValue::encode(jsPow(globalObject, JSValue::decode(encodedOp1), JSValue::decode(encodedOp2)));
 }
 
 double JIT_OPERATION operationArithAbs(JSGlobalObject* globalObject, EncodedJSValue encodedOp1)
@@ -1482,7 +1338,7 @@ size_t JIT_OPERATION operationRegExpTestGeneric(JSGlobalObject* globalObject, En
     RELEASE_AND_RETURN(scope, regexp->test(globalObject, input));
 }
 
-JSCell* JIT_OPERATION operationSubBigInt(JSGlobalObject* globalObject, JSCell* op1, JSCell* op2)
+JSCell* JIT_OPERATION operationSubHeapBigInt(JSGlobalObject* globalObject, JSCell* op1, JSCell* op2)
 {
     VM& vm = globalObject->vm();
     CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
@@ -1494,7 +1350,7 @@ JSCell* JIT_OPERATION operationSubBigInt(JSGlobalObject* globalObject, JSCell* o
     return JSBigInt::sub(globalObject, leftOperand, rightOperand);
 }
 
-JSCell* JIT_OPERATION operationBitNotBigInt(JSGlobalObject* globalObject, JSCell* op1)
+JSCell* JIT_OPERATION operationBitNotHeapBigInt(JSGlobalObject* globalObject, JSCell* op1)
 {
     VM& vm = globalObject->vm();
     CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
@@ -1505,7 +1361,7 @@ JSCell* JIT_OPERATION operationBitNotBigInt(JSGlobalObject* globalObject, JSCell
     return JSBigInt::bitwiseNot(globalObject, operand);
 }
 
-JSCell* JIT_OPERATION operationMulBigInt(JSGlobalObject* globalObject, JSCell* op1, JSCell* op2)
+JSCell* JIT_OPERATION operationMulHeapBigInt(JSGlobalObject* globalObject, JSCell* op1, JSCell* op2)
 {
     VM& vm = globalObject->vm();
     CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
@@ -1517,7 +1373,7 @@ JSCell* JIT_OPERATION operationMulBigInt(JSGlobalObject* globalObject, JSCell* o
     return JSBigInt::multiply(globalObject, leftOperand, rightOperand);
 }
     
-JSCell* JIT_OPERATION operationModBigInt(JSGlobalObject* globalObject, JSCell* op1, JSCell* op2)
+JSCell* JIT_OPERATION operationModHeapBigInt(JSGlobalObject* globalObject, JSCell* op1, JSCell* op2)
 {
     VM& vm = globalObject->vm();
     CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
@@ -1529,7 +1385,7 @@ JSCell* JIT_OPERATION operationModBigInt(JSGlobalObject* globalObject, JSCell* o
     return JSBigInt::remainder(globalObject, leftOperand, rightOperand);
 }
 
-JSCell* JIT_OPERATION operationDivBigInt(JSGlobalObject* globalObject, JSCell* op1, JSCell* op2)
+JSCell* JIT_OPERATION operationDivHeapBigInt(JSGlobalObject* globalObject, JSCell* op1, JSCell* op2)
 {
     VM& vm = globalObject->vm();
     CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
@@ -1541,7 +1397,7 @@ JSCell* JIT_OPERATION operationDivBigInt(JSGlobalObject* globalObject, JSCell* o
     return JSBigInt::divide(globalObject, leftOperand, rightOperand);
 }
 
-JSCell* JIT_OPERATION operationPowBigInt(JSGlobalObject* globalObject, JSCell* op1, JSCell* op2)
+JSCell* JIT_OPERATION operationPowHeapBigInt(JSGlobalObject* globalObject, JSCell* op1, JSCell* op2)
 {
     VM& vm = globalObject->vm();
     CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
@@ -1553,7 +1409,7 @@ JSCell* JIT_OPERATION operationPowBigInt(JSGlobalObject* globalObject, JSCell* o
     return JSBigInt::exponentiate(globalObject, leftOperand, rightOperand);
 }
 
-JSCell* JIT_OPERATION operationBitAndBigInt(JSGlobalObject* globalObject, JSCell* op1, JSCell* op2)
+JSCell* JIT_OPERATION operationBitAndHeapBigInt(JSGlobalObject* globalObject, JSCell* op1, JSCell* op2)
 {
     VM& vm = globalObject->vm();
     CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
@@ -1565,7 +1421,7 @@ JSCell* JIT_OPERATION operationBitAndBigInt(JSGlobalObject* globalObject, JSCell
     return JSBigInt::bitwiseAnd(globalObject, leftOperand, rightOperand);
 }
 
-JSCell* JIT_OPERATION operationBitLShiftBigInt(JSGlobalObject* globalObject, JSCell* op1, JSCell* op2)
+JSCell* JIT_OPERATION operationBitLShiftHeapBigInt(JSGlobalObject* globalObject, JSCell* op1, JSCell* op2)
 {
     VM& vm = globalObject->vm();
     CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
@@ -1577,7 +1433,7 @@ JSCell* JIT_OPERATION operationBitLShiftBigInt(JSGlobalObject* globalObject, JSC
     return JSBigInt::leftShift(globalObject, leftOperand, rightOperand);
 }
 
-JSCell* JIT_OPERATION operationAddBigInt(JSGlobalObject* globalObject, JSCell* op1, JSCell* op2)
+JSCell* JIT_OPERATION operationAddHeapBigInt(JSGlobalObject* globalObject, JSCell* op1, JSCell* op2)
 {
     VM& vm = globalObject->vm();
     CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
@@ -1589,7 +1445,7 @@ JSCell* JIT_OPERATION operationAddBigInt(JSGlobalObject* globalObject, JSCell* o
     return JSBigInt::add(globalObject, leftOperand, rightOperand);
 }
 
-JSCell* JIT_OPERATION operationBitRShiftBigInt(JSGlobalObject* globalObject, JSCell* op1, JSCell* op2)
+JSCell* JIT_OPERATION operationBitRShiftHeapBigInt(JSGlobalObject* globalObject, JSCell* op1, JSCell* op2)
 {
     VM& vm = globalObject->vm();
     CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
@@ -1601,7 +1457,7 @@ JSCell* JIT_OPERATION operationBitRShiftBigInt(JSGlobalObject* globalObject, JSC
     return JSBigInt::signedRightShift(globalObject, leftOperand, rightOperand);
 }
 
-JSCell* JIT_OPERATION operationBitOrBigInt(JSGlobalObject* globalObject, JSCell* op1, JSCell* op2)
+JSCell* JIT_OPERATION operationBitOrHeapBigInt(JSGlobalObject* globalObject, JSCell* op1, JSCell* op2)
 {
     VM& vm = globalObject->vm();
     CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
@@ -1613,7 +1469,7 @@ JSCell* JIT_OPERATION operationBitOrBigInt(JSGlobalObject* globalObject, JSCell*
     return JSBigInt::bitwiseOr(globalObject, leftOperand, rightOperand);
 }
 
-JSCell* JIT_OPERATION operationBitXorBigInt(JSGlobalObject* globalObject, JSCell* op1, JSCell* op2)
+JSCell* JIT_OPERATION operationBitXorHeapBigInt(JSGlobalObject* globalObject, JSCell* op1, JSCell* op2)
 {
     VM& vm = globalObject->vm();
     CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
@@ -1631,7 +1487,7 @@ size_t JIT_OPERATION operationCompareStrictEqCell(JSGlobalObject* globalObject,
     CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
     JITOperationPrologueCallFrameTracer tracer(vm, callFrame);
     
-    return JSValue::strictEqualSlowCaseInline(globalObject, op1, op2);
+    return JSValue::strictEqualForCells(globalObject, op1, op2);
 }
 
 size_t JIT_OPERATION operationSameValue(JSGlobalObject* globalObject, EncodedJSValue arg1, EncodedJSValue arg2)
@@ -1676,10 +1532,7 @@ EncodedJSValue JIT_OPERATION operationToNumeric(JSGlobalObject* globalObject, En
     CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
     JITOperationPrologueCallFrameTracer tracer(vm, callFrame);
 
-    auto variant = JSValue::decode(value).toNumeric(globalObject);
-    if (WTF::holds_alternative<JSBigInt*>(variant))
-        return JSValue::encode(WTF::get<JSBigInt*>(variant));
-    return JSValue::encode(jsNumber(WTF::get<double>(variant)));
+    return JSValue::encode(JSValue::decode(value).toNumeric(globalObject));
 }
 
 EncodedJSValue JIT_OPERATION operationGetByValWithThis(JSGlobalObject* globalObject, EncodedJSValue encodedBase, EncodedJSValue encodedThis, EncodedJSValue encodedSubscript)
index a8a4cbd..d7a8a63 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011-2017 Apple Inc. All rights reserved.
+ * Copyright (C) 2011-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
@@ -183,18 +183,18 @@ size_t JIT_OPERATION operationRegExpTestString(JSGlobalObject*, RegExpObject*, J
 size_t JIT_OPERATION operationRegExpTest(JSGlobalObject*, RegExpObject*, EncodedJSValue) WTF_INTERNAL;
 size_t JIT_OPERATION operationRegExpTestGeneric(JSGlobalObject*, EncodedJSValue, EncodedJSValue) WTF_INTERNAL;
 size_t JIT_OPERATION operationCompareStrictEqCell(JSGlobalObject*, JSCell* op1, JSCell* op2) WTF_INTERNAL;
-JSCell* JIT_OPERATION operationSubBigInt(JSGlobalObject*, JSCell* op1, JSCell* op2) WTF_INTERNAL;
-JSCell* JIT_OPERATION operationMulBigInt(JSGlobalObject*, JSCell* op1, JSCell* op2) WTF_INTERNAL;
-JSCell* JIT_OPERATION operationModBigInt(JSGlobalObject*, JSCell* op1, JSCell* op2) WTF_INTERNAL;
-JSCell* JIT_OPERATION operationDivBigInt(JSGlobalObject*, JSCell* op1, JSCell* op2) WTF_INTERNAL;
-JSCell* JIT_OPERATION operationPowBigInt(JSGlobalObject*, JSCell* op1, JSCell* op2) WTF_INTERNAL;
-JSCell* JIT_OPERATION operationBitAndBigInt(JSGlobalObject*, JSCell* op1, JSCell* op2) WTF_INTERNAL;
-JSCell* JIT_OPERATION operationBitNotBigInt(JSGlobalObject*, JSCell* op1) WTF_INTERNAL;
-JSCell* JIT_OPERATION operationBitOrBigInt(JSGlobalObject*, JSCell* op1, JSCell* op2) WTF_INTERNAL;
-JSCell* JIT_OPERATION operationBitLShiftBigInt(JSGlobalObject*, JSCell* op1, JSCell* op2) WTF_INTERNAL;
-JSCell* JIT_OPERATION operationAddBigInt(JSGlobalObject*, JSCell* op1, JSCell* op2) WTF_INTERNAL;
-JSCell* JIT_OPERATION operationBitRShiftBigInt(JSGlobalObject*, JSCell* op1, JSCell* op2) WTF_INTERNAL;
-JSCell* JIT_OPERATION operationBitXorBigInt(JSGlobalObject*, JSCell* op1, JSCell* op2) WTF_INTERNAL;
+JSCell* JIT_OPERATION operationSubHeapBigInt(JSGlobalObject*, JSCell* op1, JSCell* op2) WTF_INTERNAL;
+JSCell* JIT_OPERATION operationMulHeapBigInt(JSGlobalObject*, JSCell* op1, JSCell* op2) WTF_INTERNAL;
+JSCell* JIT_OPERATION operationModHeapBigInt(JSGlobalObject*, JSCell* op1, JSCell* op2) WTF_INTERNAL;
+JSCell* JIT_OPERATION operationDivHeapBigInt(JSGlobalObject*, JSCell* op1, JSCell* op2) WTF_INTERNAL;
+JSCell* JIT_OPERATION operationPowHeapBigInt(JSGlobalObject*, JSCell* op1, JSCell* op2) WTF_INTERNAL;
+JSCell* JIT_OPERATION operationBitAndHeapBigInt(JSGlobalObject*, JSCell* op1, JSCell* op2) WTF_INTERNAL;
+JSCell* JIT_OPERATION operationBitNotHeapBigInt(JSGlobalObject*, JSCell* op1) WTF_INTERNAL;
+JSCell* JIT_OPERATION operationBitOrHeapBigInt(JSGlobalObject*, JSCell* op1, JSCell* op2) WTF_INTERNAL;
+JSCell* JIT_OPERATION operationBitLShiftHeapBigInt(JSGlobalObject*, JSCell* op1, JSCell* op2) WTF_INTERNAL;
+JSCell* JIT_OPERATION operationAddHeapBigInt(JSGlobalObject*, JSCell* op1, JSCell* op2) WTF_INTERNAL;
+JSCell* JIT_OPERATION operationBitRShiftHeapBigInt(JSGlobalObject*, JSCell* op1, JSCell* op2) WTF_INTERNAL;
+JSCell* JIT_OPERATION operationBitXorHeapBigInt(JSGlobalObject*, JSCell* op1, JSCell* op2) WTF_INTERNAL;
 size_t JIT_OPERATION operationSameValue(JSGlobalObject*, EncodedJSValue, EncodedJSValue) WTF_INTERNAL;
 JSCell* JIT_OPERATION operationCreateActivationDirect(VM*, Structure*, JSScope*, SymbolTable*, EncodedJSValue);
 JSCell* JIT_OPERATION operationCreateDirectArguments(VM*, Structure*, uint32_t length, uint32_t minCapacity);
index 244106e..7fa6059 100644 (file)
@@ -532,7 +532,19 @@ private:
                     changed |= mergePrediction(SpecSymbol);
                     break;
                 }
-                
+
+#if USE(BIGINT32)
+                if (node->child1()->shouldSpeculateBigInt32()) {
+                    changed |= mergePrediction(SpecBigInt32);
+                    break;
+                }
+#endif
+
+                if (node->child1()->shouldSpeculateHeapBigInt()) {
+                    changed |= mergePrediction(SpecHeapBigInt);
+                    break;
+                }
+
                 if (node->child1()->shouldSpeculateBigInt()) {
                     changed |= mergePrediction(SpecBigInt);
                     break;
@@ -1004,6 +1016,7 @@ private:
         case IsUndefinedOrNull:
         case IsBoolean:
         case IsNumber:
+        case IsBigInt:
         case NumberIsInteger:
         case IsObject:
         case IsObjectOrNull:
index 4ab757b..8db0aba 100644 (file)
@@ -79,7 +79,9 @@ public:
         case StringUse:
         case StringOrOtherUse:
         case SymbolUse:
-        case BigIntUse:
+        case AnyBigIntUse:
+        case HeapBigIntUse:
+        case BigInt32Use:
         case StringObjectUse:
         case StringOrStringObjectUse:
         case NotStringVarUse:
@@ -263,6 +265,7 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node, bool igno
     case IsUndefinedOrNull:
     case IsBoolean:
     case IsNumber:
+    case IsBigInt:
     case NumberIsInteger:
     case IsObject:
     case IsObjectOrNull:
@@ -676,10 +679,10 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node, bool igno
     case ValueDiv:
     case ValueMod:
     case ValuePow:
-        return node->isBinaryUseKind(BigIntUse);
+        return node->isBinaryUseKind(AnyBigIntUse) || node->isBinaryUseKind(BigInt32Use) || node->isBinaryUseKind(HeapBigIntUse);
 
     case ValueBitNot:
-        return node->child1().useKind() == BigIntUse;
+        return node->child1().useKind() == AnyBigIntUse || node->child1().useKind() == BigInt32Use || node->child1().useKind() == HeapBigIntUse;
 
     case LastNodeType:
         RELEASE_ASSERT_NOT_REACHED();
index a8e833f..e6531ec 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011-2019 Apple Inc. All rights reserved.
+ * Copyright (C) 2011-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
@@ -368,6 +368,13 @@ void SpeculativeJIT::typeCheck(JSValueSource source, Edge edge, SpeculatedType t
     speculationCheck(exitKind, source, edge.node(), jumpToFail);
 }
 
+void SpeculativeJIT::typeCheck(JSValueSource source, Edge edge, SpeculatedType typesPassedThrough, MacroAssembler::JumpList jumpListToFail, ExitKind exitKind)
+{
+    ASSERT(needsTypeCheck(edge, typesPassedThrough));
+    m_interpreter.filter(edge, typesPassedThrough);
+    speculationCheck(exitKind, source, edge.node(), jumpListToFail);
+}
+
 RegisterSet SpeculativeJIT::usedRegisters()
 {
     RegisterSet result;
@@ -1708,6 +1715,10 @@ bool SpeculativeJIT::compilePeepHoleBranch(Node* node, MacroAssembler::Relationa
 
         if (node->isBinaryUseKind(Int32Use))
             compilePeepHoleInt32Branch(node, branchNode, condition);
+#if USE(BIGINT32)
+        else if (node->isBinaryUseKind(BigInt32Use) && (condition == MacroAssembler::Equal || condition == MacroAssembler::NotEqual))
+            compilePeepHoleBigInt32Branch(node, branchNode, condition);
+#endif
 #if USE(JSVALUE64)
         else if (node->isBinaryUseKind(Int52RepUse))
             compilePeepHoleInt52Branch(node, branchNode, condition);
@@ -3535,24 +3546,47 @@ void SpeculativeJIT::compileValueBitNot(Node* node)
 {
     Edge& child1 = node->child1();
 
-    if (child1.useKind() == BigIntUse) {
+#if USE(BIGINT32)
+    if (child1.useKind() == BigInt32Use) {
+        SpeculateBigInt32Operand operand(this, child1);
+        GPRTemporary result(this);
+        GPRReg resultGPR = result.gpr();
+
+        // The following trick relies on details of the representation of BigInt32, and will have to be updated if we move bits around.
+        static_assert(JSValue::BigInt32Tag == 0x12);
+        static_assert(JSValue::BigInt32Mask == 0xfffe000000000012);
+        constexpr uint64_t maskForBigInt32Bits = 0x0000ffffffff0000;
+        static_assert(!(JSValue::BigInt32Mask & maskForBigInt32Bits));
+        m_jit.move(TrustedImm64(maskForBigInt32Bits), resultGPR);
+        m_jit.xor64(operand.gpr(), resultGPR);
+
+        jsValueResult(resultGPR, node);
+
+        return;
+    }
+    // FIXME: add support for mixed BigInt32 / HeapBigInt
+#endif
+
+    if (child1.useKind() == HeapBigIntUse) {
         SpeculateCellOperand operand(this, child1);
         GPRReg operandGPR = operand.gpr();
 
-        speculateBigInt(child1, operandGPR);
+        speculateHeapBigInt(child1, operandGPR);
 
         flushRegisters();
         GPRFlushedCallResult result(this);
         GPRReg resultGPR = result.gpr();
 
-        callOperation(operationBitNotBigInt, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), operandGPR);
+        callOperation(operationBitNotHeapBigInt, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), operandGPR);
         m_jit.exceptionCheck();
         cellResult(resultGPR, node);
 
         return;
     }
 
-    JSValueOperand operand(this, child1);
+    ASSERT(child1.useKind() == UntypedUse || child1.useKind() == AnyBigIntUse);
+    JSValueOperand operand(this, child1, ManualOperandSpeculation);
+    speculate(node, child1); // Required for the AnyBigIntUse case
     JSValueRegs operandRegs = operand.jsValueRegs();
 
     flushRegisters();
@@ -3580,14 +3614,18 @@ void SpeculativeJIT::compileBitwiseNot(Node* node)
 }
 
 template<typename SnippetGenerator, J_JITOperation_GJJ snippetSlowPathFunction>
-void SpeculativeJIT::emitUntypedBitOp(Node* node)
+void SpeculativeJIT::emitUntypedOrAnyBigIntBitOp(Node* node)
 {
     Edge& leftChild = node->child1();
     Edge& rightChild = node->child2();
 
+    ASSERT(leftChild.useKind() == UntypedUse || leftChild.useKind() == AnyBigIntUse || rightChild.useKind() == UntypedUse || rightChild.useKind() == AnyBigIntUse);
+
     if (isKnownNotNumber(leftChild.node()) || isKnownNotNumber(rightChild.node())) {
-        JSValueOperand left(this, leftChild);
-        JSValueOperand right(this, rightChild);
+        JSValueOperand left(this, leftChild, ManualOperandSpeculation);
+        JSValueOperand right(this, rightChild, ManualOperandSpeculation);
+        speculate(node, leftChild);
+        speculate(node, rightChild);
         JSValueRegs leftRegs = left.jsValueRegs();
         JSValueRegs rightRegs = right.jsValueRegs();
 
@@ -3632,11 +3670,13 @@ void SpeculativeJIT::emitUntypedBitOp(Node* node)
     RELEASE_ASSERT(!leftOperand.isConst() || !rightOperand.isConst());
 
     if (!leftOperand.isConst()) {
-        left.emplace(this, leftChild);
+        left.emplace(this, leftChild, ManualOperandSpeculation);
+        speculate(node, leftChild); // Required for AnyBigIntUse
         leftRegs = left->jsValueRegs();
     }
     if (!rightOperand.isConst()) {
-        right.emplace(this, rightChild);
+        right.emplace(this, rightChild, ManualOperandSpeculation);
+        speculate(node, rightChild); // Required for AnyBigIntUse
         rightRegs = right->jsValueRegs();
     }
 
@@ -3672,52 +3712,84 @@ void SpeculativeJIT::compileValueBitwiseOp(Node* node)
     Edge& leftChild = node->child1();
     Edge& rightChild = node->child2();
 
-    if (leftChild.useKind() == UntypedUse || rightChild.useKind() == UntypedUse) {
+#if USE(BIGINT32)
+    if (leftChild.useKind() == BigInt32Use && rightChild.useKind() == BigInt32Use) {
+        SpeculateBigInt32Operand left(this, leftChild);
+        SpeculateBigInt32Operand right(this, rightChild);
+        GPRTemporary result(this);
+        GPRReg resultGPR = result.gpr();
+
+        m_jit.move(left.gpr(), resultGPR);
+
         switch (op) {
         case ValueBitAnd:
-            emitUntypedBitOp<JITBitAndGenerator, operationValueBitAnd>(node);
-            return;
-        case ValueBitXor:
-            emitUntypedBitOp<JITBitXorGenerator, operationValueBitXor>(node);
-            return;
+            // No need to unbox/box: bitAnd does not interfere with the encoding of BigInt32
+            m_jit.and64(right.gpr(), resultGPR);
+            break;
         case ValueBitOr:
-            emitUntypedBitOp<JITBitOrGenerator, operationValueBitOr>(node);
-            return;
+            // No need to unbox/box: bitOr does not interfere with the encoding of BigInt32
+            m_jit.or64(right.gpr(), resultGPR);
+            break;
+        case ValueBitXor:
+            // BitXor removes the tag, so we must add it back after doing the operation
+            m_jit.xor64(right.gpr(), resultGPR);
+            m_jit.or64(TrustedImm32(JSValue::BigInt32Tag), resultGPR);
+            break;
         default:
             RELEASE_ASSERT_NOT_REACHED();
         }
+
+        jsValueResult(resultGPR, node);
+        return;
     }
-    
-    ASSERT(leftChild.useKind() == BigIntUse && rightChild.useKind() == BigIntUse);
-    
-    SpeculateCellOperand left(this, node->child1());
-    SpeculateCellOperand right(this, node->child2());
-    GPRReg leftGPR = left.gpr();
-    GPRReg rightGPR = right.gpr();
+    // FIXME: add support for mixed BigInt32 / HeapBigInt
+#endif
 
-    speculateBigInt(leftChild, leftGPR);
-    speculateBigInt(rightChild, rightGPR);
+    if (leftChild.useKind() == HeapBigIntUse && rightChild.useKind() == HeapBigIntUse) {
+        SpeculateCellOperand left(this, node->child1());
+        SpeculateCellOperand right(this, node->child2());
+        GPRReg leftGPR = left.gpr();
+        GPRReg rightGPR = right.gpr();
 
-    flushRegisters();
-    GPRFlushedCallResult result(this);
-    GPRReg resultGPR = result.gpr();
+        speculateHeapBigInt(leftChild, leftGPR);
+        speculateHeapBigInt(rightChild, rightGPR);
+
+        flushRegisters();
+        GPRFlushedCallResult result(this);
+        GPRReg resultGPR = result.gpr();
+
+        switch (op) {
+        case ValueBitAnd:
+            callOperation(operationBitAndHeapBigInt, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), leftGPR, rightGPR);
+            break;
+        case ValueBitXor:
+            callOperation(operationBitXorHeapBigInt, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), leftGPR, rightGPR);
+            break;
+        case ValueBitOr:
+            callOperation(operationBitOrHeapBigInt, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), leftGPR, rightGPR);
+            break;
+        default:
+            RELEASE_ASSERT_NOT_REACHED();
+        }
+
+        m_jit.exceptionCheck();
+        cellResult(resultGPR, node);
+        return;
+    }
 
     switch (op) {
     case ValueBitAnd:
-        callOperation(operationBitAndBigInt, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), leftGPR, rightGPR);
-        break;
+        emitUntypedOrAnyBigIntBitOp<JITBitAndGenerator, operationValueBitAnd>(node);
+        return;
     case ValueBitXor:
-        callOperation(operationBitXorBigInt, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), leftGPR, rightGPR);
-        break;
+        emitUntypedOrAnyBigIntBitOp<JITBitXorGenerator, operationValueBitXor>(node);
+        return;
     case ValueBitOr:
-        callOperation(operationBitOrBigInt, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), leftGPR, rightGPR);
-        break;
+        emitUntypedOrAnyBigIntBitOp<JITBitOrGenerator, operationValueBitOr>(node);
+        return;
     default:
         RELEASE_ASSERT_NOT_REACHED();
     }
-
-    m_jit.exceptionCheck();
-    cellResult(resultGPR, node);
 }
 
 void SpeculativeJIT::compileBitwiseOp(Node* node)
@@ -3757,7 +3829,7 @@ void SpeculativeJIT::compileBitwiseOp(Node* node)
     strictInt32Result(result.gpr(), node);
 }
 
-void SpeculativeJIT::emitUntypedRightShiftBitOp(Node* node)
+void SpeculativeJIT::emitUntypedOrBigIntRightShiftBitOp(Node* node)
 {
     J_JITOperation_GJJ snippetSlowPathFunction = node->op() == ValueBitRShift
         ? operationValueBitRShift : operationValueBitURShift;
@@ -3767,9 +3839,11 @@ void SpeculativeJIT::emitUntypedRightShiftBitOp(Node* node)
     Edge& leftChild = node->child1();
     Edge& rightChild = node->child2();
 
-    if (isKnownNotNumber(leftChild.node()) || isKnownNotNumber(rightChild.node())) {
-        JSValueOperand left(this, leftChild);
-        JSValueOperand right(this, rightChild);
+    if (isKnownNotNumber(leftChild.node()) || isKnownNotNumber(rightChild.node()) || node->isBinaryUseKind(BigInt32Use) || node->isBinaryUseKind(AnyBigIntUse)) {
+        JSValueOperand left(this, leftChild, ManualOperandSpeculation);
+        JSValueOperand right(this, rightChild, ManualOperandSpeculation);
+        speculate(node, leftChild);
+        speculate(node, rightChild);
         JSValueRegs leftRegs = left.jsValueRegs();
         JSValueRegs rightRegs = right.jsValueRegs();
 
@@ -3861,27 +3935,27 @@ void SpeculativeJIT::compileValueLShiftOp(Node* node)
     Edge& leftChild = node->child1();
     Edge& rightChild = node->child2();
 
-    if (node->binaryUseKind() == BigIntUse) {
+    // FIXME: support BigInt32
+    if (node->binaryUseKind() == HeapBigIntUse) {
         SpeculateCellOperand left(this, leftChild);
         SpeculateCellOperand right(this, rightChild);
         GPRReg leftGPR = left.gpr();
         GPRReg rightGPR = right.gpr();
 
-        speculateBigInt(leftChild, leftGPR);
-        speculateBigInt(rightChild, rightGPR);
+        speculateHeapBigInt(leftChild, leftGPR);
+        speculateHeapBigInt(rightChild, rightGPR);
 
         flushRegisters();
         GPRFlushedCallResult result(this);
         GPRReg resultGPR = result.gpr();
 
-        callOperation(operationBitLShiftBigInt, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), leftGPR, rightGPR);
+        callOperation(operationBitLShiftHeapBigInt, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), leftGPR, rightGPR);
         m_jit.exceptionCheck();
         cellResult(resultGPR, node);
         return;
     }
 
-    ASSERT(leftChild.useKind() == UntypedUse && rightChild.useKind() == UntypedUse);
-    emitUntypedBitOp<JITLeftShiftGenerator, operationValueBitLShift>(node);
+    emitUntypedOrAnyBigIntBitOp<JITLeftShiftGenerator, operationValueBitLShift>(node);
 }
 
 void SpeculativeJIT::compileValueBitRShift(Node* node)
@@ -3889,27 +3963,27 @@ void SpeculativeJIT::compileValueBitRShift(Node* node)
     Edge& leftChild = node->child1();
     Edge& rightChild = node->child2();
 
-    if (node->isBinaryUseKind(BigIntUse)) {
+    // FIXME: support BigInt32
+    if (node->isBinaryUseKind(HeapBigIntUse)) {
         SpeculateCellOperand left(this, leftChild);
         SpeculateCellOperand right(this, rightChild);
         GPRReg leftGPR = left.gpr();
         GPRReg rightGPR = right.gpr();
 
-        speculateBigInt(leftChild, leftGPR);
-        speculateBigInt(rightChild, rightGPR);
+        speculateHeapBigInt(leftChild, leftGPR);
+        speculateHeapBigInt(rightChild, rightGPR);
 
         flushRegisters();
         GPRFlushedCallResult result(this);
         GPRReg resultGPR = result.gpr();
-        callOperation(operationBitRShiftBigInt, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), leftGPR, rightGPR);
+        callOperation(operationBitRShiftHeapBigInt, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), leftGPR, rightGPR);
         m_jit.exceptionCheck();
 
         cellResult(resultGPR, node);
         return;
     }
 
-    ASSERT(leftChild.useKind() == UntypedUse && rightChild.useKind() == UntypedUse);
-    emitUntypedRightShiftBitOp(node);
+    emitUntypedOrBigIntRightShiftBitOp(node);
 }
 
 void SpeculativeJIT::compileShiftOp(Node* node)
@@ -3920,7 +3994,7 @@ void SpeculativeJIT::compileShiftOp(Node* node)
 
     if (leftChild.useKind() == UntypedUse || rightChild.useKind() == UntypedUse) {
         RELEASE_ASSERT(op == BitURShift);
-        emitUntypedRightShiftBitOp(node);
+        emitUntypedOrBigIntRightShiftBitOp(node);
         return;
     }
 
@@ -3950,19 +4024,67 @@ void SpeculativeJIT::compileValueAdd(Node* node)
     Edge& leftChild = node->child1();
     Edge& rightChild = node->child2();
 
-    if (node->isBinaryUseKind(BigIntUse)) {
-        SpeculateCellOperand left(this, node->child1());
-        SpeculateCellOperand right(this, node->child2());
+#if USE(BIGINT32)
+    if (node->isBinaryUseKind(BigInt32Use)) {
+        SpeculateBigInt32Operand left(this, leftChild);
+        SpeculateBigInt32Operand right(this, rightChild);
+
+        // FIXME: do we really need this extra register, or can we mutate left/right ?
+        // FIXME: also, look into Reuse and other tricks for a more efficient generated code.
+        GPRTemporary result(this);
+        GPRReg resultGPR = result.gpr();
+        GPRTemporary temp(this);
+        GPRReg tempGPR = temp.gpr();
+
+        m_jit.move(left.gpr(), resultGPR);
+        m_jit.unboxBigInt32(resultGPR);
+        m_jit.move(right.gpr(), tempGPR);
+        m_jit.unboxBigInt32(tempGPR);
+
+        // FIXME: add some way to eliminate the overflow check
+        MacroAssembler::Jump check = m_jit.branchAdd32(MacroAssembler::Overflow, resultGPR, tempGPR, resultGPR);
+
+        speculationCheck(Overflow, JSValueRegs(), 0, check);
+
+        m_jit.boxBigInt32(resultGPR);
+        jsValueResult(resultGPR, node);
+        return;
+    }
+
+    if (node->isBinaryUseKind(AnyBigIntUse)) {
+        JSValueOperand left(this, leftChild, ManualOperandSpeculation);
+        JSValueOperand right(this, rightChild, ManualOperandSpeculation);
+        speculate(node, leftChild);
+        speculate(node, rightChild);
+        JSValueRegs leftRegs = left.jsValueRegs();
+        JSValueRegs rightRegs = right.jsValueRegs();
+
+        flushRegisters();
+        JSValueRegsFlushedCallResult result(this);
+        JSValueRegs resultRegs = result.regs();
+        // FIXME: call a more specialized function
+        callOperation(operationValueAddNotNumber, resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), leftRegs, rightRegs);
+        m_jit.exceptionCheck();
+
+        jsValueResult(resultRegs, node);
+        return;
+    }
+    // FIXME: add support for mixed BigInt32/HeapBigInt
+#endif // USE(BIGINT32)
+
+    if (node->isBinaryUseKind(HeapBigIntUse)) {
+        SpeculateCellOperand left(this, leftChild);
+        SpeculateCellOperand right(this, rightChild);
         GPRReg leftGPR = left.gpr();
         GPRReg rightGPR = right.gpr();
 
-        speculateBigInt(leftChild, leftGPR);
-        speculateBigInt(rightChild, rightGPR);
+        speculateHeapBigInt(leftChild, leftGPR);
+        speculateHeapBigInt(rightChild, rightGPR);
 
         flushRegisters();
         GPRFlushedCallResult result(this);
         GPRReg resultGPR = result.gpr();
-        callOperation(operationAddBigInt, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), leftGPR, rightGPR);
+        callOperation(operationAddHeapBigInt, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), leftGPR, rightGPR);
         m_jit.exceptionCheck();
 
         cellResult(resultGPR, node);
@@ -4008,44 +4130,90 @@ void SpeculativeJIT::compileValueSub(Node* node)
     Edge& leftChild = node->child1();
     Edge& rightChild = node->child2();
 
-    if (node->binaryUseKind() == UntypedUse) {
-#if USE(JSVALUE64)
-        bool needsScratchGPRReg = true;
-        bool needsScratchFPRReg = false;
-#else
-        bool needsScratchGPRReg = true;
-        bool needsScratchFPRReg = true;
-#endif
+#if USE(BIGINT32)
+    if (node->binaryUseKind() == BigInt32Use) {
+        SpeculateBigInt32Operand left(this, node->child1());
+        SpeculateBigInt32Operand right(this, node->child2());
+
+        // FIXME: do we really need this extra register, or can we mutate left/right ?
+        // FIXME: also, look into Reuse and other tricks for a more efficient generated code.
+        GPRTemporary result(this);
+        GPRReg resultGPR = result.gpr();
+        GPRTemporary temp(this);
+        GPRReg tempGPR = temp.gpr();
+
+        m_jit.move(left.gpr(), resultGPR);
+        m_jit.unboxBigInt32(resultGPR);
+        m_jit.move(right.gpr(), tempGPR);
+        m_jit.unboxBigInt32(tempGPR);
 
-        CodeBlock* baselineCodeBlock = m_jit.graph().baselineCodeBlockFor(node->origin.semantic);
-        BytecodeIndex bytecodeIndex = node->origin.semantic.bytecodeIndex();
-        BinaryArithProfile* arithProfile = baselineCodeBlock->binaryArithProfileForBytecodeIndex(bytecodeIndex);
-        JITSubIC* subIC = m_jit.codeBlock()->addJITSubIC(arithProfile);
-        auto repatchingFunction = operationValueSubOptimize;
-        auto nonRepatchingFunction = operationValueSub;
+        // FIXME: add some way to eliminate the overflow check
+        MacroAssembler::Jump check = m_jit.branchSub32(MacroAssembler::Overflow, resultGPR, tempGPR, resultGPR);
 
-        compileMathIC(node, subIC, needsScratchGPRReg, needsScratchFPRReg, repatchingFunction, nonRepatchingFunction);
+        speculationCheck(Overflow, JSValueRegs(), 0, check);
+
+        m_jit.boxBigInt32(resultGPR);
+        jsValueResult(resultGPR, node);
         return;
     }
+    // FIXME: add support for mixed BigInt32/HeapBigInt
 
-    ASSERT(leftChild.useKind() == BigIntUse && rightChild.useKind() == BigIntUse);
+    // FIXME: why do compileValueAdd/compileValueMul use isKnownNotNumber but not ValueSub?
+    if (node->binaryUseKind() == AnyBigIntUse) {
+        JSValueOperand left(this, leftChild, ManualOperandSpeculation);
+        JSValueOperand right(this, rightChild, ManualOperandSpeculation);
+        speculateAnyBigInt(leftChild);
+        speculateAnyBigInt(rightChild);
+        JSValueRegs leftRegs = left.jsValueRegs();
+        JSValueRegs rightRegs = right.jsValueRegs();
 
-    SpeculateCellOperand left(this, node->child1());
-    SpeculateCellOperand right(this, node->child2());
-    GPRReg leftGPR = left.gpr();
-    GPRReg rightGPR = right.gpr();
+        flushRegisters();
+        JSValueRegsFlushedCallResult result(this);
+        JSValueRegs resultRegs = result.regs();
+        callOperation(operationValueSub, resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), leftRegs, rightRegs);
+        m_jit.exceptionCheck();
+
+        jsValueResult(resultRegs, node);
+        return;
+    }
+#endif // USE(BIGINT32)
 
-    speculateBigInt(leftChild, leftGPR);
-    speculateBigInt(rightChild, rightGPR);
+    if (node->binaryUseKind() == HeapBigIntUse) {
+        SpeculateCellOperand left(this, node->child1());
+        SpeculateCellOperand right(this, node->child2());
+        GPRReg leftGPR = left.gpr();
+        GPRReg rightGPR = right.gpr();
 
-    flushRegisters();
-    GPRFlushedCallResult result(this);
-    GPRReg resultGPR = result.gpr();
+        speculateHeapBigInt(leftChild, leftGPR);
+        speculateHeapBigInt(rightChild, rightGPR);
 
-    callOperation(operationSubBigInt, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), leftGPR, rightGPR);
+        flushRegisters();
+        GPRFlushedCallResult result(this);
+        GPRReg resultGPR = result.gpr();
 
-    m_jit.exceptionCheck();
-    cellResult(resultGPR, node);
+        callOperation(operationSubHeapBigInt, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), leftGPR, rightGPR);
+
+        m_jit.exceptionCheck();
+        cellResult(resultGPR, node);
+        return;
+    }
+
+#if USE(JSVALUE64)
+    bool needsScratchGPRReg = true;
+    bool needsScratchFPRReg = false;
+#else
+    bool needsScratchGPRReg = true;
+    bool needsScratchFPRReg = true;
+#endif
+
+    CodeBlock* baselineCodeBlock = m_jit.graph().baselineCodeBlockFor(node->origin.semantic);
+    BytecodeIndex bytecodeIndex = node->origin.semantic.bytecodeIndex();
+    BinaryArithProfile* arithProfile = baselineCodeBlock->binaryArithProfileForBytecodeIndex(bytecodeIndex);
+    JITSubIC* subIC = m_jit.codeBlock()->addJITSubIC(arithProfile);
+    auto repatchingFunction = operationValueSubOptimize;
+    auto nonRepatchingFunction = operationValueSub;
+
+    compileMathIC(node, subIC, needsScratchGPRReg, needsScratchFPRReg, repatchingFunction, nonRepatchingFunction);
 }
 
 template <typename Generator, typename RepatchingFunction, typename NonRepatchingFunction>
@@ -4630,6 +4798,7 @@ void SpeculativeJIT::compileIncOrDec(Node* node)
 
 void SpeculativeJIT::compileValueNegate(Node* node)
 {
+    // FIXME: add a fast path, at least for BigInt32, but probably also for HeapBigInt here.
     CodeBlock* baselineCodeBlock = m_jit.graph().baselineCodeBlockFor(node->origin.semantic);
     BytecodeIndex bytecodeIndex = node->origin.semantic.bytecodeIndex();
     UnaryArithProfile* arithProfile = baselineCodeBlock->unaryArithProfileForBytecodeIndex(bytecodeIndex);
@@ -4810,29 +4979,61 @@ void SpeculativeJIT::compileValueMul(Node* node)
     Edge& leftChild = node->child1();
     Edge& rightChild = node->child2();
 
-    if (leftChild.useKind() == BigIntUse && rightChild.useKind() == BigIntUse) {
+#if USE(BIGINT32)
+    if (node->binaryUseKind() == BigInt32Use) {
+        // FIXME: the code between compileValueAdd, compileValueSub and compileValueMul for BigInt32 is nearly identical, so try to get rid of the duplication.
+        SpeculateBigInt32Operand left(this, node->child1());
+        SpeculateBigInt32Operand right(this, node->child2());
+
+        // FIXME: do we really need this extra register, or can we mutate left/right ?
+        // FIXME: also, look into Reuse and other tricks for a more efficient generated code.
+        GPRTemporary result(this);
+        GPRReg resultGPR = result.gpr();
+        GPRTemporary temp(this);
+        GPRReg tempGPR = temp.gpr();
+
+        m_jit.move(left.gpr(), resultGPR);
+        m_jit.unboxBigInt32(resultGPR);
+        m_jit.move(right.gpr(), tempGPR);
+        m_jit.unboxBigInt32(tempGPR);
+
+        // FIXME: add some way to eliminate the overflow check
+        MacroAssembler::Jump check = m_jit.branchMul32(MacroAssembler::Overflow, resultGPR, tempGPR, resultGPR);
+
+        speculationCheck(Overflow, JSValueRegs(), 0, check);
+
+        m_jit.boxBigInt32(resultGPR);
+        jsValueResult(resultGPR, node);
+        return;
+    }
+    // FIXME: add support for mixed BigInt32/HeapBigInt
+#endif
+
+    if (leftChild.useKind() == HeapBigIntUse && rightChild.useKind() == HeapBigIntUse) {
         SpeculateCellOperand left(this, leftChild);
         SpeculateCellOperand right(this, rightChild);
         GPRReg leftGPR = left.gpr();
         GPRReg rightGPR = right.gpr();
 
-        speculateBigInt(leftChild, leftGPR);
-        speculateBigInt(rightChild, rightGPR);
+        speculateHeapBigInt(leftChild, leftGPR);
+        speculateHeapBigInt(rightChild, rightGPR);
 
         flushRegisters();
         GPRFlushedCallResult result(this);
         GPRReg resultGPR = result.gpr();
 
-        callOperation(operationMulBigInt, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), leftGPR, rightGPR);
+        callOperation(operationMulHeapBigInt, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), leftGPR, rightGPR);
 
         m_jit.exceptionCheck();
         cellResult(resultGPR, node);
         return;
     }
 
-    if (isKnownNotNumber(leftChild.node()) || isKnownNotNumber(rightChild.node())) {
-        JSValueOperand left(this, leftChild);
-        JSValueOperand right(this, rightChild);
+    if (isKnownNotNumber(leftChild.node()) || isKnownNotNumber(rightChild.node()) || node->isBinaryUseKind(AnyBigIntUse)) {
+        JSValueOperand left(this, leftChild, ManualOperandSpeculation);
+        JSValueOperand right(this, rightChild, ManualOperandSpeculation);
+        speculate(node, leftChild);
+        speculate(node, rightChild);
         JSValueRegs leftRegs = left.jsValueRegs();
         JSValueRegs rightRegs = right.jsValueRegs();
 
@@ -5012,29 +5213,33 @@ void SpeculativeJIT::compileValueDiv(Node* node)
     Edge& leftChild = node->child1();
     Edge& rightChild = node->child2();
 
-    if (leftChild.useKind() == BigIntUse && rightChild.useKind() == BigIntUse) {
+    // FIXME: add a fast path for BigInt32. Currently we go through the slow path, because of how ugly the code for Div gets.
+
+    if (node->isBinaryUseKind(HeapBigIntUse)) {
         SpeculateCellOperand left(this, leftChild);
         SpeculateCellOperand right(this, rightChild);
         GPRReg leftGPR = left.gpr();
         GPRReg rightGPR = right.gpr();
 
-        speculateBigInt(leftChild, leftGPR);
-        speculateBigInt(rightChild, rightGPR);
+        speculateHeapBigInt(leftChild, leftGPR);
+        speculateHeapBigInt(rightChild, rightGPR);
 
         flushRegisters();
         GPRFlushedCallResult result(this);
         GPRReg resultGPR = result.gpr();
 
-        callOperation(operationDivBigInt, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), leftGPR, rightGPR);
+        callOperation(operationDivHeapBigInt, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), leftGPR, rightGPR);
 
         m_jit.exceptionCheck();
         cellResult(resultGPR, node);
         return;
     }
 
-    if (isKnownNotNumber(leftChild.node()) || isKnownNotNumber(rightChild.node())) {
-        JSValueOperand left(this, leftChild);
-        JSValueOperand right(this, rightChild);
+    if (isKnownNotNumber(leftChild.node()) || isKnownNotNumber(rightChild.node()) || node->isBinaryUseKind(AnyBigIntUse) || node->isBinaryUseKind(BigInt32Use)) {
+        JSValueOperand left(this, leftChild, ManualOperandSpeculation);
+        JSValueOperand right(this, rightChild, ManualOperandSpeculation);
+        speculate(node, leftChild);
+        speculate(node, rightChild);
         JSValueRegs leftRegs = left.jsValueRegs();
         JSValueRegs rightRegs = right.jsValueRegs();
 
@@ -5048,6 +5253,8 @@ void SpeculativeJIT::compileValueDiv(Node* node)
         return;
     }
 
+    ASSERT(node->isBinaryUseKind(UntypedUse));
+
     Optional<JSValueOperand> left;
     Optional<JSValueOperand> right;
 
@@ -5297,29 +5504,33 @@ void SpeculativeJIT::compileValueMod(Node* node)
     Edge& leftChild = node->child1();
     Edge& rightChild = node->child2();
 
-    if (node->binaryUseKind() == BigIntUse) {
+    // FIXME: add a fast path for BigInt32. Currently we go through the slow path, because of how ugly the code for Mod gets.
+
+    if (node->binaryUseKind() == HeapBigIntUse) {
         SpeculateCellOperand left(this, leftChild);
         SpeculateCellOperand right(this, rightChild);
         GPRReg leftGPR = left.gpr();
         GPRReg rightGPR = right.gpr();
 
-        speculateBigInt(leftChild, leftGPR);
-        speculateBigInt(rightChild, rightGPR);
+        speculateHeapBigInt(leftChild, leftGPR);
+        speculateHeapBigInt(rightChild, rightGPR);
 
         flushRegisters();
         GPRFlushedCallResult result(this);
         GPRReg resultGPR = result.gpr();
 
-        callOperation(operationModBigInt, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), leftGPR, rightGPR);
+        callOperation(operationModHeapBigInt, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), leftGPR, rightGPR);
 
         m_jit.exceptionCheck();
         cellResult(resultGPR, node);
         return;
     }
 
-    DFG_ASSERT(m_jit.graph(), node, node->binaryUseKind() == UntypedUse, node->binaryUseKind());
-    JSValueOperand op1(this, leftChild);
-    JSValueOperand op2(this, rightChild);
+    DFG_ASSERT(m_jit.graph(), node, node->binaryUseKind() == UntypedUse || node->binaryUseKind() == AnyBigIntUse || node->binaryUseKind() == BigInt32Use, node->binaryUseKind());
+    JSValueOperand op1(this, leftChild, ManualOperandSpeculation);
+    JSValueOperand op2(this, rightChild, ManualOperandSpeculation);
+    speculate(node, leftChild);
+    speculate(node, rightChild);
     JSValueRegs op1Regs = op1.jsValueRegs();
     JSValueRegs op2Regs = op2.jsValueRegs();
     flushRegisters();
@@ -5861,30 +6072,33 @@ void SpeculativeJIT::compileValuePow(Node* node)
     Edge& leftChild = node->child1();
     Edge& rightChild = node->child2();
 
-    if (node->binaryUseKind() == BigIntUse) {
+    // FIXME: do we want a fast path for BigInt32 for Pow? I expect it would overflow pretty often.
+    if (node->binaryUseKind() == HeapBigIntUse) {
         SpeculateCellOperand left(this, leftChild);
         SpeculateCellOperand right(this, rightChild);
         GPRReg leftGPR = left.gpr();
         GPRReg rightGPR = right.gpr();
 
-        speculateBigInt(leftChild, leftGPR);
-        speculateBigInt(rightChild, rightGPR);
+        speculateHeapBigInt(leftChild, leftGPR);
+        speculateHeapBigInt(rightChild, rightGPR);
 
         flushRegisters();
         GPRFlushedCallResult result(this);
         GPRReg resultGPR = result.gpr();
 
-        callOperation(operationPowBigInt, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), leftGPR, rightGPR);
+        callOperation(operationPowHeapBigInt, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), leftGPR, rightGPR);
 
         m_jit.exceptionCheck();
         cellResult(resultGPR, node);
         return;
     }
 
-    DFG_ASSERT(m_jit.graph(), node, node->binaryUseKind() == UntypedUse, node->binaryUseKind());
+    DFG_ASSERT(m_jit.graph(), node, node->binaryUseKind() == UntypedUse || node->binaryUseKind() == AnyBigIntUse || node->binaryUseKind() == BigInt32Use, node->binaryUseKind());
 
-    JSValueOperand left(this, leftChild);
-    JSValueOperand right(this, rightChild);
+    JSValueOperand left(this, leftChild, ManualOperandSpeculation);
+    JSValueOperand right(this, rightChild, ManualOperandSpeculation);
+    speculate(node, leftChild);
+    speculate(node, rightChild);
     JSValueRegs leftRegs = left.jsValueRegs();
     JSValueRegs rightRegs = right.jsValueRegs();
 
@@ -6032,6 +6246,12 @@ bool SpeculativeJIT::compare(Node* node, MacroAssembler::RelationalCondition con
         return false;
     }
     
+#if USE(BIGINT32)
+    if (node->isBinaryUseKind(BigInt32Use)) {
+        compileBigInt32Compare(node, condition);
+        return false;
+    }
+#endif
 #if USE(JSVALUE64)
     if (node->isBinaryUseKind(Int52RepUse)) {
         compileInt52Compare(node, condition);
@@ -6060,6 +6280,9 @@ bool SpeculativeJIT::compare(Node* node, MacroAssembler::RelationalCondition con
         return false;
     }
 
+    // FIXME: add HeapBigInt case here.
+    // Not having it means that the compare will not be fused with the branch for this case.
+
     if (node->op() == CompareEq) {
         if (node->isBinaryUseKind(BooleanUse)) {
             compileBooleanCompare(node, condition);
@@ -6138,7 +6361,24 @@ bool SpeculativeJIT::compileStrictEq(Node* node)
         return false;
     }
     
-#if USE(JSVALUE64)   
+#if USE(BIGINT32)
+    if (node->isBinaryUseKind(BigInt32Use)) {
+        unsigned branchIndexInBlock = detectPeepHoleBranch();
+        if (branchIndexInBlock != UINT_MAX) {
+            Node* branchNode = m_block->at(branchIndexInBlock);
+            compilePeepHoleBigInt32Branch(node, branchNode, MacroAssembler::Equal);
+            use(node->child1());
+            use(node->child2());
+            m_indexInBlock = branchIndexInBlock;
+            m_currentNode = branchNode;
+            return true;
+        }
+        compileBigInt32Compare(node, MacroAssembler::Equal);
+        return false;
+    }
+#endif
+
+#if USE(JSVALUE64)
     if (node->isBinaryUseKind(Int52RepUse)) {
         unsigned branchIndexInBlock = detectPeepHoleBranch();
         if (branchIndexInBlock != UINT_MAX) {
@@ -6185,8 +6425,8 @@ bool SpeculativeJIT::compileStrictEq(Node* node)
         return false;
     }
     
-    if (node->isBinaryUseKind(BigIntUse)) {
-        compileBigIntEquality(node);
+    if (node->isBinaryUseKind(HeapBigIntUse)) {
+        compileHeapBigIntEquality(node);
         return false;
     }
     
@@ -6280,8 +6520,8 @@ bool SpeculativeJIT::compileStrictEq(Node* node)
         compileStringToUntypedEquality(node, node->child2(), node->child1());
         return false;
     }
-    
-    RELEASE_ASSERT(node->isBinaryUseKind(UntypedUse));
+
+    ASSERT(node->isBinaryUseKind(UntypedUse) || node->isBinaryUseKind(AnyBigIntUse));
     return nonSpeculativeStrictEq(node);
 }
 
@@ -10559,18 +10799,18 @@ void SpeculativeJIT::speculateSymbol(Edge edge)
     speculateSymbol(edge, operand.gpr());
 }
 
-void SpeculativeJIT::speculateBigInt(Edge edge, GPRReg cell)
+void SpeculativeJIT::speculateHeapBigInt(Edge edge, GPRReg cell)
 {
-    DFG_TYPE_CHECK(JSValueSource::unboxedCell(cell), edge, ~SpecCellCheck | SpecBigInt, m_jit.branchIfNotBigInt(cell));
+    DFG_TYPE_CHECK(JSValueSource::unboxedCell(cell), edge, ~SpecCellCheck | SpecHeapBigInt, m_jit.branchIfNotHeapBigInt(cell));
 }
 
-void SpeculativeJIT::speculateBigInt(Edge edge)
+void SpeculativeJIT::speculateHeapBigInt(Edge edge)
 {
     if (!needsTypeCheck(edge, SpecBigInt))
         return;
 
     SpeculateCellOperand operand(this, edge);
-    speculateBigInt(edge, operand.gpr());
+    speculateHeapBigInt(edge, operand.gpr());
 }
 
 void SpeculativeJIT::speculateNotCell(Edge edge, JSValueRegs regs)
@@ -10740,8 +10980,16 @@ void SpeculativeJIT::speculate(Node*, Edge edge)
     case SymbolUse:
         speculateSymbol(edge);
         break;
-    case BigIntUse:
-        speculateBigInt(edge);
+#if USE(BIGINT32)
+    case BigInt32Use:
+        speculateBigInt32(edge);
+        break;
+    case AnyBigIntUse:
+        speculateAnyBigInt(edge);
+        break;
+#endif
+    case HeapBigIntUse:
+        speculateHeapBigInt(edge);
         break;
     case StringObjectUse:
         speculateStringObject(edge);
@@ -13082,17 +13330,18 @@ void SpeculativeJIT::compileToNumeric(Node* node)
     JSValueRegs argumentRegs = argument.jsValueRegs();
     JSValueRegs resultRegs = result.regs();
     GPRReg scratch = temp.gpr();
+    // FIXME: add a fast path for BigInt32 here (and for typeOf, and for boolify or whatever it is called in this file).
 
     MacroAssembler::JumpList slowCases;
 
     MacroAssembler::Jump notCell = m_jit.branchIfNotCell(argumentRegs);
-    slowCases.append(m_jit.branchIfNotBigInt(argumentRegs.payloadGPR()));
-    MacroAssembler::Jump isBigInt = m_jit.jump();
+    slowCases.append(m_jit.branchIfNotHeapBigInt(argumentRegs.payloadGPR()));
+    MacroAssembler::Jump isHeapBigInt = m_jit.jump();
 
     notCell.link(&m_jit);
     slowCases.append(m_jit.branchIfNotNumber(argumentRegs, scratch));
 
-    isBigInt.link(&m_jit);
+    isHeapBigInt.link(&m_jit);
     m_jit.moveValueRegs(argumentRegs, resultRegs);
 
     addSlowPathGenerator(slowPathCall(slowCases, this, operationToNumeric, resultRegs, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), argumentRegs));
@@ -13981,9 +14230,9 @@ void SpeculativeJIT::nonSpeculativePeepholeBranch(Node* node, Node* branchNode,
     m_currentNode = branchNode;
 }
 
-void SpeculativeJIT::compileBigIntEquality(Node* node)
+void SpeculativeJIT::compileHeapBigIntEquality(Node* node)
 {
-    // FIXME: [ESNext][BigInt] Create specialized version of strict equals for BigIntUse
+    // FIXME: [ESNext][BigInt] Create specialized version of strict equals for big ints
     // https://bugs.webkit.org/show_bug.cgi?id=182895
     SpeculateCellOperand left(this, node->child1());
     SpeculateCellOperand right(this, node->child2());
@@ -13995,8 +14244,8 @@ void SpeculativeJIT::compileBigIntEquality(Node* node)
     left.use();
     right.use();
 
-    speculateBigInt(node->child1(), leftGPR);
-    speculateBigInt(node->child2(), rightGPR);
+    speculateHeapBigInt(node->child1(), leftGPR);
+    speculateHeapBigInt(node->child2(), rightGPR);
 
     JITCompiler::Jump notEqualCase = m_jit.branchPtr(JITCompiler::NotEqual, leftGPR, rightGPR);
 
index d172fe2..96e34b8 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011-2019 Apple Inc. All rights reserved.
+ * Copyright (C) 2011-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
@@ -346,6 +346,9 @@ public:
     FPRReg fillSpeculateDouble(Edge);
     GPRReg fillSpeculateCell(Edge);
     GPRReg fillSpeculateBoolean(Edge);
+#if USE(BIGINT32)
+    GPRReg fillSpeculateBigInt32(Edge);
+#endif
     GeneratedOperandType checkGeneratedTypeForToInt32(Node*);
 
     void addSlowPathGenerator(std::unique_ptr<SlowPathGenerator>);
@@ -1148,6 +1151,9 @@ public:
     bool compilePeepHoleBranch(Node*, MacroAssembler::RelationalCondition, MacroAssembler::DoubleCondition, S_JITOperation_GJJ);
     void compilePeepHoleInt32Branch(Node*, Node* branchNode, JITCompiler::RelationalCondition);
     void compilePeepHoleInt52Branch(Node*, Node* branchNode, JITCompiler::RelationalCondition);
+#if USE(BIGINT32)
+    void compilePeepHoleBigInt32Branch(Node*, Node* branchNode, JITCompiler::RelationalCondition);
+#endif
     void compilePeepHoleBooleanBranch(Node*, Node* branchNode, JITCompiler::RelationalCondition);
     void compilePeepHoleDoubleBranch(Node*, Node* branchNode, JITCompiler::DoubleCondition);
     void compilePeepHoleObjectEquality(Node*, Node* branchNode);
@@ -1172,7 +1178,7 @@ public:
     void compileMiscStrictEq(Node*);
 
     void compileSymbolEquality(Node*);
-    void compileBigIntEquality(Node*);
+    void compileHeapBigIntEquality(Node*);
     void compilePeepHoleSymbolEquality(Node*, Node* branchNode);
     void compileSymbolUntypedEquality(Node*, Edge symbolEdge, Edge untypedEdge);
 
@@ -1222,6 +1228,9 @@ public:
     
     void compileInt32Compare(Node*, MacroAssembler::RelationalCondition);
     void compileInt52Compare(Node*, MacroAssembler::RelationalCondition);
+#if USE(BIGINT32)
+    void compileBigInt32Compare(Node*, MacroAssembler::RelationalCondition);
+#endif
     void compileBooleanCompare(Node*, MacroAssembler::RelationalCondition);
     void compileDoubleCompare(Node*, MacroAssembler::DoubleCondition);
     void compileStringCompare(Node*, MacroAssembler::RelationalCondition);
@@ -1297,11 +1306,11 @@ public:
     void compileBitwiseNot(Node*);
 
     template<typename SnippetGenerator, J_JITOperation_GJJ slowPathFunction>
-    void emitUntypedBitOp(Node*);
+    void emitUntypedOrAnyBigIntBitOp(Node*);
     void compileBitwiseOp(Node*);
     void compileValueBitwiseOp(Node*);
 
-    void emitUntypedRightShiftBitOp(Node*);
+    void emitUntypedOrBigIntRightShiftBitOp(Node*);
     void compileValueLShiftOp(Node*);
     void compileValueBitRShift(Node*);
     void compileShiftOp(Node*);
@@ -1560,6 +1569,7 @@ public:
     // Helpers for performing type checks on an edge stored in the given registers.
     bool needsTypeCheck(Edge edge, SpeculatedType typesPassedThrough) { return m_interpreter.needsTypeCheck(edge, typesPassedThrough); }
     void typeCheck(JSValueSource, Edge, SpeculatedType typesPassedThrough, MacroAssembler::Jump jumpToFail, ExitKind = BadType);
+    void typeCheck(JSValueSource, Edge, SpeculatedType typesPassedThrough, MacroAssembler::JumpList jumpListToFail, ExitKind = BadType);
     
     void speculateCellTypeWithoutTypeFiltering(Edge, GPRReg cellGPR, JSType);
     void speculateCellType(Edge, GPRReg cellGPR, SpeculatedType, JSType);
@@ -1571,6 +1581,10 @@ public:
     void speculateInt32(Edge, JSValueRegs);
     void speculateDoubleRepAnyInt(Edge);
 #endif // USE(JSVALUE64)
+#if USE(BIGINT32)
+    void speculateBigInt32(Edge);
+    void speculateAnyBigInt(Edge);
+#endif // USE(BIGINT32)
     void speculateNumber(Edge);
     void speculateRealNumber(Edge);
     void speculateDoubleRepReal(Edge);
@@ -1620,8 +1634,8 @@ public:
     void speculateStringOrStringObject(Edge);
     void speculateSymbol(Edge, GPRReg cell);
     void speculateSymbol(Edge);
-    void speculateBigInt(Edge, GPRReg cell);
-    void speculateBigInt(Edge);
+    void speculateHeapBigInt(Edge, GPRReg cell);
+    void speculateHeapBigInt(Edge);
     void speculateNotCell(Edge, JSValueRegs);
     void speculateNotCell(Edge);
     void speculateOther(Edge, JSValueRegs, GPRReg temp);
@@ -2599,6 +2613,56 @@ private:
     GPRReg m_gprOrInvalid;
 };
 
+#if USE(BIGINT32)
+class SpeculateBigInt32Operand {
+    WTF_MAKE_FAST_ALLOCATED;
+public:
+    explicit SpeculateBigInt32Operand(SpeculativeJIT* jit, Edge edge, OperandSpeculationMode mode = AutomaticOperandSpeculation)
+        : m_jit(jit)
+        , m_edge(edge)
+        , m_gprOrInvalid(InvalidGPRReg)
+    {
+        ASSERT(m_jit);
+        ASSERT_UNUSED(mode, mode == ManualOperandSpeculation || edge.useKind() == BigInt32Use);
+        if (jit->isFilled(node()))
+            gpr();
+    }
+
+    ~SpeculateBigInt32Operand()
+    {
+        ASSERT(m_gprOrInvalid != InvalidGPRReg);
+        m_jit->unlock(m_gprOrInvalid);
+    }
+
+    Edge edge() const
+    {
+        return m_edge;
+    }
+
+    Node* node() const
+    {
+        return edge().node();
+    }
+
+    GPRReg gpr()
+    {
+        if (m_gprOrInvalid == InvalidGPRReg)
+            m_gprOrInvalid = m_jit->fillSpeculateBigInt32(edge());
+        return m_gprOrInvalid;
+    }
+
+    void use()
+    {
+        m_jit->use(node());
+    }
+
+private:
+    SpeculativeJIT* m_jit;
+    Edge m_edge;
+    GPRReg m_gprOrInvalid;
+};
+#endif // USE(BIGINT32)
+
 #define DFG_TYPE_CHECK_WITH_EXIT_KIND(exitKind, source, edge, typesPassedThrough, jumpToFail) do { \
         JSValueSource _dtc_source = (source);                           \
         Edge _dtc_edge = (edge);                                        \
index 1f7e057..2e2a7bd 100644 (file)
@@ -4181,7 +4181,11 @@ void SpeculativeJIT::compile(Node* node)
     case CheckArrayOrEmpty:
         DFG_CRASH(m_jit.graph(), node, "CheckArrayOrEmpty only used in 64-bit DFG");
         break;
-        
+
+    case IsBigInt:
+        DFG_CRASH(m_jit.graph(), node, "IsBigInt is only used when USE_BIGINT32, which can only be true in 64 bit mode");
+        break;
+
     case FilterCallLinkStatus:
     case FilterGetByStatus:
     case FilterPutByIdStatus:
index 22ec26c..e5a34dc 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011-2019 Apple Inc. All rights reserved.
+ * Copyright (C) 2011-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
@@ -138,7 +138,8 @@ GPRReg SpeculativeJIT::fillJSValue(Edge edge)
     case DataFormatJSInt32:
     case DataFormatJSDouble:
     case DataFormatJSCell:
-    case DataFormatJSBoolean: {
+    case DataFormatJSBoolean:
+    case DataFormatJSBigInt32: {
         GPRReg gpr = info.gpr();
         m_gprs.lock(gpr);
         return gpr;
@@ -340,8 +341,11 @@ void SpeculativeJIT::nonSpeculativePeepholeStrictEq(Node* node, Node* branchNode
         notTaken = tmp;
     }
     
-    JSValueOperand arg1(this, node->child1());
-    JSValueOperand arg2(this, node->child2());
+    ASSERT(node->isBinaryUseKind(UntypedUse) || node->isBinaryUseKind(AnyBigIntUse));
+    JSValueOperand arg1(this, node->child1(), ManualOperandSpeculation);
+    JSValueOperand arg2(this, node->child2(), ManualOperandSpeculation);
+    speculate(node, node->child1());
+    speculate(node, node->child2());
     GPRReg arg1GPR = arg1.gpr();
     GPRReg arg2GPR = arg2.gpr();
     
@@ -363,43 +367,67 @@ void SpeculativeJIT::nonSpeculativePeepholeStrictEq(Node* node, Node* branchNode
         
         branchTest32(invert ? JITCompiler::Zero : JITCompiler::NonZero, resultGPR, taken);
     } else {
-        m_jit.or64(arg1GPR, arg2GPR, resultGPR);
-        
-        JITCompiler::Jump twoCellsCase = m_jit.branchIfCell(resultGPR);
-        
-        JITCompiler::Jump leftOK = m_jit.branchIfInt32(arg1GPR);
-        JITCompiler::Jump leftDouble = m_jit.branchIfNumber(arg1GPR);
-        leftOK.link(&m_jit);
-        JITCompiler::Jump rightOK = m_jit.branchIfInt32(arg2GPR);
-        JITCompiler::Jump rightDouble = m_jit.branchIfNumber(arg2GPR);
-        rightOK.link(&m_jit);
-        
-        branch64(invert ? JITCompiler::NotEqual : JITCompiler::Equal, arg1GPR, arg2GPR, taken);
-        jump(notTaken, ForceJump);
-        
-        twoCellsCase.link(&m_jit);
+        /* At a high level we do (assuming 'invert' to be false):
+         If (left is Double || right is Double)
+            goto slowPath;
+         if (left == right)
+            goto taken;
+         if (left is Cell || right is Cell)
+            goto slowPath;
+         goto notTaken;
+         */
+
+        JITCompiler::JumpList slowPathCases;
+
+        // This fragment implements (left is Double || right is Double), with a single branch instead of the 4 that would be naively required if we used branchIfInt32/branchIfNumber
+        // The trick is that if a JSValue is an Int32, then adding 1<<49 to it will make it overflow, leaving all high bits at 0
+        // If it is not a number at all, then 1<<49 will be its only high bit set
+        // Leaving only doubles above or equal 1<<50.
+        GPRTemporary scratch(this);
+        m_jit.move(arg1GPR, resultGPR);
+        m_jit.move(arg2GPR, scratch.gpr());
+        m_jit.add64(TrustedImm64(JSValue::LowestOfHighBits), resultGPR);
+        m_jit.add64(TrustedImm64(JSValue::LowestOfHighBits), scratch.gpr());
+        m_jit.or64(scratch.gpr(), resultGPR, resultGPR);
+        constexpr uint64_t nextLowestOfHighBits = JSValue::LowestOfHighBits << 1;
+        slowPathCases.append(m_jit.branch64(JITCompiler::AboveOrEqual, resultGPR, TrustedImm64(nextLowestOfHighBits)));
+
         branch64(JITCompiler::Equal, arg1GPR, arg2GPR, invert ? notTaken : taken);
         
-        leftDouble.link(&m_jit);
-        rightDouble.link(&m_jit);
-        
-        silentSpillAllRegisters(resultGPR);
-        callOperation(operationCompareStrictEq, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), arg1GPR, arg2GPR);
-        silentFillAllRegisters();
-        m_jit.exceptionCheck();
-        
+        // If we support BigInt32 we must go to a slow path if at least one operand is a cell (for HeapBigInt === BigInt32)
+        // If we don't support BigInt32, we only have to go to a slow path if both operands are cells (for HeapBigInt === HeapBigInt and String === String)
+        // Instead of doing two branches, we can do a single one, by observing that
+        // 1. (left is Cell && right is Cell) is the same as ((left | right) is Cell)
+        //      Both are "All high bits are 0"
+        // 2. Since we know that neither is a double, (left is Cell || right is Cell) is equivalent to ((left & right) is Cell)
+        //      If both are Int32, then the top bits will be set and the test will fail
+        //      If at least one is not Int32, then the top bits will be 0.
+        //      And if at least one is a cell, then the 'Other' tag will also be 0, making the test succeed
+#if USE(BIGINT32)
+        m_jit.and64(arg1GPR, arg2GPR, resultGPR);
+#else
+        m_jit.or64(arg1GPR, arg2GPR, resultGPR);
+#endif
+        slowPathCases.append(m_jit.branchIfCell(resultGPR));
+
+        jump(invert ? taken : notTaken, ForceJump);
+
+        addSlowPathGenerator(slowPathCall(slowPathCases, this, operationCompareStrictEq, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), arg1GPR, arg2GPR));
         branchTest32(invert ? JITCompiler::Zero : JITCompiler::NonZero, resultGPR, taken);
     }
-    
+
     jump(notTaken);
 }
 
 void SpeculativeJIT::nonSpeculativeNonPeepholeStrictEq(Node* node, bool invert)
 {
+    // FIXME: some of this code should be shareable with nonSpeculativePeepholeStrictEq
     JSValueOperand arg1(this, node->child1());
     JSValueOperand arg2(this, node->child2());
     JSValueRegs arg1Regs = arg1.jsValueRegs();
     JSValueRegs arg2Regs = arg2.jsValueRegs();
+    GPRReg arg1GPR = arg1.gpr();
+    GPRReg arg2GPR = arg2.gpr();
     
     GPRTemporary result(this);
     GPRReg resultGPR = result.gpr();
@@ -428,33 +456,60 @@ void SpeculativeJIT::nonSpeculativeNonPeepholeStrictEq(Node* node, bool invert)
         unblessedBooleanResult(resultGPR, m_currentNode, UseChildrenCalledExplicitly);
         return;
     }
-
-    m_jit.or64(arg1Regs.gpr(), arg2Regs.gpr(), resultGPR);
+    /* At a high level we do (assuming 'invert' to be false):
+    If (left is Double || right is Double)
+        goto slowPath;
+    result = (left == right);
+    if (result)
+        goto done;
+    if (left is Cell || right is Cell)
+        goto slowPath;
+    done:
+    return result;
+    */
 
     JITCompiler::JumpList slowPathCases;
 
-    JITCompiler::Jump twoCellsCase = m_jit.branchIfCell(resultGPR);
-
-    JITCompiler::Jump leftOK = m_jit.branchIfInt32(arg1Regs);
-    slowPathCases.append(m_jit.branchIfNumber(arg1Regs, InvalidGPRReg));
-    leftOK.link(&m_jit);
-    JITCompiler::Jump rightOK = m_jit.branchIfInt32(arg2Regs);
-    slowPathCases.append(m_jit.branchIfNumber(arg2Regs, InvalidGPRReg));
-    rightOK.link(&m_jit);
-
-    m_jit.compare64(invert ? JITCompiler::NotEqual : JITCompiler::Equal, arg1Regs.gpr(), arg2Regs.gpr(), resultGPR);
-
-    JITCompiler::Jump done = m_jit.jump();
-
-    twoCellsCase.link(&m_jit);
-    slowPathCases.append(m_jit.branch64(JITCompiler::NotEqual, arg1Regs.gpr(), arg2Regs.gpr()));
+    // This fragment implements (left is Double || right is Double), with a single branch instead of the 4 that would be naively required if we used branchIfInt32/branchIfNumber
+    // The trick is that if a JSValue is an Int32, then adding 1<<49 to it will make it overflow, leaving all high bits at 0
+    // If it is not a number at all, then 1<<49 will be its only high bit set
+    // Leaving only doubles above or equal 1<<50.
+    GPRTemporary scratch(this);
+    m_jit.move(arg1GPR, resultGPR);
+    m_jit.move(arg2GPR, scratch.gpr());
+    m_jit.add64(TrustedImm64(JSValue::LowestOfHighBits), resultGPR);
+    m_jit.add64(TrustedImm64(JSValue::LowestOfHighBits), scratch.gpr());
+    m_jit.or64(scratch.gpr(), resultGPR, resultGPR);
+    constexpr uint64_t nextLowestOfHighBits = JSValue::LowestOfHighBits << 1;
+    slowPathCases.append(m_jit.branch64(JITCompiler::AboveOrEqual, resultGPR, TrustedImm64(nextLowestOfHighBits)));
+
+    m_jit.compare64(JITCompiler::Equal, arg1GPR, arg2GPR, resultGPR);
+    JITCompiler::Jump done = m_jit.branchTest64(JITCompiler::NonZero, resultGPR);
+
+    // If we support BigInt32 we must go to a slow path if at least one operand is a cell (for HeapBigInt === BigInt32)
+    // If we don't support BigInt32, we only have to go to a slow path if both operands are cells (for HeapBigInt === HeapBigInt and String === String)
+    // Instead of doing two branches, we can do a single one, by observing that
+    // 1. (left is Cell && right is Cell) is the same as ((left | right) is Cell)
+    //      Both are "All high bits are 0"
+    // 2. Since we know that neither is a double, (left is Cell || right is Cell) is equivalent to ((left & right) is Cell)
+    //      If both are Int32, then the top bits will be set and the test will fail
+    //      If at least one is not Int32, then the top bits will be 0.
+    //      And if at least one is a cell, then the 'Other' tag will also be 0, making the test succeed
+#if USE(BIGINT32)
+    m_jit.and64(arg1GPR, arg2GPR, resultGPR);
+#else
+    m_jit.or64(arg1GPR, arg2GPR, resultGPR);
+#endif
+    slowPathCases.append(m_jit.branchIfCell(resultGPR));
 
-    m_jit.move(JITCompiler::TrustedImm64(!invert), resultGPR);
+    m_jit.move(TrustedImm64(0), resultGPR);
 
     addSlowPathGenerator(slowPathCall(slowPathCases, this, operationCompareStrictEq, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), arg1Regs, arg2Regs));
 
     done.link(&m_jit);
 
+    m_jit.xor64(TrustedImm64(invert), resultGPR);
+
     unblessedBooleanResult(resultGPR, m_currentNode, UseChildrenCalledExplicitly);
 }
 
@@ -1004,6 +1059,8 @@ GPRReg SpeculativeJIT::fillSpeculateInt32Internal(Edge edge, DataFormat& returnF
     case DataFormatStorage:
     case DataFormatInt52:
     case DataFormatStrictInt52:
+    case DataFormatBigInt32:
+    case DataFormatJSBigInt32:
         DFG_CRASH(m_jit.graph(), m_currentNode, "Bad data format");
         
     default:
@@ -1184,6 +1241,8 @@ GPRReg SpeculativeJIT::fillSpeculateCell(Edge edge)
     GenerationInfo& info = generationInfoFromVirtualRegister(virtualRegister);
 
     switch (info.registerFormat()) {
+    // FIXME: some of these cases look like they could share code.
+    // Look at fillSpeculateInt32Internal for an example.
     case DataFormatNone: {
         GPRReg gpr = allocate();
 
@@ -1235,6 +1294,8 @@ GPRReg SpeculativeJIT::fillSpeculateCell(Edge edge)
     case DataFormatStorage:
     case DataFormatInt52:
     case DataFormatStrictInt52:
+    case DataFormatBigInt32:
+    case DataFormatJSBigInt32:
         DFG_CRASH(m_jit.graph(), m_currentNode, "Bad data format");
         
     default:
@@ -1312,6 +1373,8 @@ GPRReg SpeculativeJIT::fillSpeculateBoolean(Edge edge)
     case DataFormatStorage:
     case DataFormatInt52:
     case DataFormatStrictInt52:
+    case DataFormatBigInt32:
+    case DataFormatJSBigInt32:
         DFG_CRASH(m_jit.graph(), m_currentNode, "Bad data format");
         
     default:
@@ -1320,6 +1383,126 @@ GPRReg SpeculativeJIT::fillSpeculateBoolean(Edge edge)
     }
 }
 
+#if USE(BIGINT32)
+void SpeculativeJIT::speculateBigInt32(Edge edge)
+{
+    if (!needsTypeCheck(edge, SpecBigInt32))
+        return;
+
+    (SpeculateBigInt32Operand(this, edge)).gpr();
+}
+
+void SpeculativeJIT::speculateAnyBigInt(Edge edge)
+{
+    if (!needsTypeCheck(edge, SpecBigInt))
+        return;
+
+    JSValueOperand value(this, edge, ManualOperandSpeculation);
+    JSValueRegs valueRegs = value.jsValueRegs();
+    GPRTemporary temp(this);
+    GPRReg tempGPR = temp.gpr();
+    JITCompiler::Jump notCell = m_jit.branchIfNotCell(valueRegs);
+    // I inlined speculateHeapBigInt because it would be incorrect to call it here if it did JSValueOperand / SpeculateXXXOperand,
+    // as it would confuse the DFG register allocator.
+    DFG_TYPE_CHECK(valueRegs, edge, ~SpecCellCheck | SpecHeapBigInt, m_jit.branchIfNotHeapBigInt(valueRegs.gpr()));
+    auto done = m_jit.jump();
+    notCell.link(&m_jit);
+    DFG_TYPE_CHECK(valueRegs, edge, SpecCellCheck | SpecBigInt32, m_jit.branchIfNotBigInt32(valueRegs.gpr(), tempGPR));
+    done.link(&m_jit);
+}
+
+GPRReg SpeculativeJIT::fillSpeculateBigInt32(Edge edge)
+{
+    AbstractValue& value = m_state.forNode(edge);
+    SpeculatedType type = value.m_type;
+
+    m_interpreter.filter(value, SpecBigInt32);
+    if (value.isClear()) {
+        if (mayHaveTypeCheck(edge.useKind()))
+            terminateSpeculativeExecution(Uncountable, JSValueRegs(), 0);
+        return allocate();
+    }
+
+    VirtualRegister virtualRegister = edge->virtualRegister();
+    GenerationInfo& info = generationInfoFromVirtualRegister(virtualRegister);
+
+    switch (info.registerFormat()) {
+    case DataFormatNone: {
+        GPRReg gpr = allocate();
+
+        if (edge->hasConstant()) {
+            JSValue jsValue = edge->asJSValue();
+            m_gprs.retain(gpr, virtualRegister, SpillOrderConstant);
+            ASSERT(jsValue.isBigInt32());
+            m_jit.move(MacroAssembler::TrustedImm64(JSValue::encode(jsValue)), gpr);
+            info.fillJSValue(*m_stream, gpr, DataFormatJSBigInt32);
+            return gpr;
+        }
+
+        DataFormat spillFormat = info.spillFormat();
+        DFG_ASSERT(m_jit.graph(), m_currentNode, (spillFormat & DataFormatJS) || spillFormat == DataFormatBigInt32, spillFormat);
+
+        m_gprs.retain(gpr, virtualRegister, SpillOrderSpilled);
+
+        if (spillFormat == DataFormatBigInt32) {
+            // We have not yet implemented this
+            RELEASE_ASSERT_NOT_REACHED();
+        }
+        if (spillFormat == DataFormatJSBigInt32) {
+            m_jit.load64(JITCompiler::addressFor(virtualRegister), gpr);
+            info.fillJSValue(*m_stream, gpr, DataFormatJSBigInt32);
+            return gpr;
+        }
+
+        m_jit.load64(JITCompiler::addressFor(virtualRegister), gpr);
+
+        info.fillJSValue(*m_stream, gpr, DataFormatJS);
+        m_gprs.unlock(gpr);
+        FALLTHROUGH;
+    }
+
+    case DataFormatJS: {
+        GPRReg gpr = info.gpr();
+        m_gprs.lock(gpr);
+        if (type & ~SpecBigInt32) {
+            CCallHelpers::JumpList failureCases;
+            GPRReg tempGPR = allocate();
+            failureCases.append(m_jit.branchIfNumber(gpr));
+            failureCases.append(m_jit.branchIfNotBigInt32KnownNotNumber(gpr, tempGPR));
+            speculationCheck(BadType, JSValueRegs(gpr), edge, failureCases);
+            unlock(tempGPR);
+        }
+        info.fillJSValue(*m_stream, gpr, DataFormatJSBigInt32);
+        return gpr;
+    }
+
+    case DataFormatJSBigInt32: {
+        GPRReg gpr = info.gpr();
+        m_gprs.lock(gpr);
+        return gpr;
+    }
+
+    case DataFormatBoolean:
+    case DataFormatJSBoolean:
+    case DataFormatJSInt32:
+    case DataFormatInt32:
+    case DataFormatJSDouble:
+    case DataFormatJSCell:
+    case DataFormatCell:
+    case DataFormatDouble:
+    case DataFormatStorage:
+    case DataFormatInt52:
+    case DataFormatStrictInt52:
+    case DataFormatBigInt32:
+        DFG_CRASH(m_jit.graph(), m_currentNode, "Bad data format");
+
+    default:
+        DFG_CRASH(m_jit.graph(), m_currentNode, "Corrupt data format");
+        return InvalidGPRReg;
+    }
+}
+#endif // USE(BIGINT32)
+
 void SpeculativeJIT::compileObjectStrictEquality(Edge objectChild, Edge otherChild)
 {
     SpeculateCellOperand op1(this, objectChild);
@@ -1556,6 +1739,61 @@ void SpeculativeJIT::compilePeepHoleInt52Branch(Node* node, Node* branchNode, JI
     jump(notTaken);
 }
 
+#if USE(BIGINT32)
+void SpeculativeJIT::compileBigInt32Compare(Node* node, MacroAssembler::RelationalCondition condition)
+{
+    SpeculateBigInt32Operand op1(this, node->child1());
+    SpeculateBigInt32Operand op2(this, node->child2());
+    GPRTemporary result(this, Reuse, op1, op2);
+
+    GPRReg op1GPR = op1.gpr();
+    GPRReg op2GPR = op2.gpr();
+    GPRReg resultGPR = result.gpr();
+
+    if (condition == MacroAssembler::Equal || condition == MacroAssembler::NotEqual) {
+        // No need to unbox the operands, since the tag bits are identical
+        m_jit.compare64(condition, op1.gpr(), op2.gpr(), result.gpr());
+    } else {
+        GPRTemporary temp(this);
+        GPRReg tempGPR = temp.gpr();
+
+        m_jit.move(op1GPR, tempGPR);
+        m_jit.unboxBigInt32(tempGPR);
+        m_jit.move(op2GPR, resultGPR);
+        m_jit.unboxBigInt32(resultGPR);
+        m_jit.compare64(condition, tempGPR, resultGPR, resultGPR);
+    }
+
+    // If we add a DataFormatBool, we should use it here.
+    m_jit.or32(TrustedImm32(JSValue::ValueFalse), result.gpr());
+    jsValueResult(result.gpr(), m_currentNode, DataFormatJSBoolean);
+}
+
+void SpeculativeJIT::compilePeepHoleBigInt32Branch(Node* node, Node* branchNode, JITCompiler::RelationalCondition condition)
+{
+    // Other conditions would require unboxing the BigInt32 to get correct results on negative numbers.
+    ASSERT(condition == MacroAssembler::Equal || condition == MacroAssembler::NotEqual);
+
+    BasicBlock* taken = branchNode->branchData()->taken.block;
+    BasicBlock* notTaken = branchNode->branchData()->notTaken.block;
+
+    // The branch instruction will branch to the taken block.
+    // If taken is next, switch taken with notTaken & invert the branch condition so we can fall through.
+    if (taken == nextBlock()) {
+        condition = JITCompiler::invert(condition);
+        BasicBlock* tmp = taken;
+        taken = notTaken;
+        notTaken = tmp;
+    }
+
+    SpeculateBigInt32Operand op1(this, node->child1());
+    SpeculateBigInt32Operand op2(this, node->child2());
+
+    branch64(condition, op1.gpr(), op2.gpr(), taken);
+    jump(notTaken);
+}
+#endif // USE(BIGINT32)
+
 void SpeculativeJIT::compileCompareEqPtr(Node* node)
 {
     JSValueOperand value(this, node->child1());
@@ -4082,6 +4320,31 @@ void SpeculativeJIT::compile(Node* node)
         break;
     }
 
+    case IsBigInt: {
+#if USE(BIGINT32)
+        JSValueOperand value(this, node->child1());
+        GPRTemporary result(this);
+        GPRReg resultGPR = result.gpr();
+
+        JITCompiler::Jump isCell = m_jit.branchIfCell(value.gpr());
+
+        m_jit.move(TrustedImm64(JSValue::BigInt32Mask), resultGPR);
+        m_jit.and64(value.gpr(), result.gpr());
+        m_jit.compare64(JITCompiler::Equal, resultGPR, TrustedImm32(JSValue::BigInt32Tag), resultGPR);
+        JITCompiler::Jump continuation = m_jit.jump();
+
+        isCell.link(&m_jit);
+        JSValueRegs valueRegs = value.jsValueRegs();
+        m_jit.compare8(JITCompiler::Equal, JITCompiler::Address(valueRegs.payloadGPR(), JSCell::typeInfoTypeOffset()), TrustedImm32(HeapBigIntType), resultGPR);
+
+        continuation.link(&m_jit);
+        unblessedBooleanResult(resultGPR, node);
+#else
+        RELEASE_ASSERT_NOT_REACHED();
+#endif
+        break;
+    }
+
     case NumberIsInteger: {
         JSValueOperand value(this, node->child1());
         GPRTemporary result(this, Reuse, value);
index 75f3a06..6a254ce 100644 (file)
@@ -125,7 +125,8 @@ private:
         case ValueBitOr:
         case ValueBitAnd:
         case ValueBitXor: {
-            if (m_node->binaryUseKind() == BigIntUse)
+            // FIXME: we should maybe support the case where one operand is always HeapBigInt and the other is always BigInt32?
+            if (m_node->binaryUseKind() == AnyBigIntUse || m_node->binaryUseKind() == BigInt32Use || m_node->binaryUseKind() == HeapBigIntUse)
                 handleCommutativity();
             break;
         }
@@ -375,7 +376,7 @@ private:
                 break;
             }
 
-            if (m_node->binaryUseKind() == BigIntUse)
+            if (m_node->binaryUseKind() == BigInt32Use || m_node->binaryUseKind() == HeapBigIntUse || m_node->binaryUseKind() == AnyBigIntUse)
                 handleCommutativity();
 
             break;
index 8ef73b6..ab631c9 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013-2016 Apple Inc. All rights reserved.
+ * Copyright (C) 2013-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
@@ -145,8 +145,14 @@ void printInternal(PrintStream& out, UseKind useKind)
     case SymbolUse:
         out.print("Symbol");
         return;
-    case BigIntUse:
-        out.print("BigInt");
+    case AnyBigIntUse:
+        out.print("AnyBigInt");
+        return;
+    case HeapBigIntUse:
+        out.print("HeapBigInt");
+        return;
+    case BigInt32Use:
+        out.print("BigInt32");
         return;
     case StringObjectUse:
         out.print("StringObject");
index 9c13c5b..f4f7b33 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013-2016 Apple Inc. All rights reserved.
+ * Copyright (C) 2013-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
@@ -66,7 +66,9 @@ enum UseKind {
     KnownStringUse,
     KnownPrimitiveUse, // This bizarre type arises for op_strcat, which has a bytecode guarantee that it will only see primitives (i.e. not objects).
     SymbolUse,
-    BigIntUse,
+    AnyBigIntUse,
+    HeapBigIntUse,
+    BigInt32Use,
     DateObjectUse,
     MapObjectUse,
     SetObjectUse,
@@ -152,8 +154,12 @@ inline SpeculatedType typeFilterFor(UseKind useKind)
         return SpecHeapTop & ~SpecObject;
     case SymbolUse:
         return SpecSymbol;
-    case BigIntUse:
+    case AnyBigIntUse:
         return SpecBigInt;
+    case HeapBigIntUse:
+        return SpecHeapBigInt;
+    case BigInt32Use:
+        return SpecBigInt32;
     case PromiseObjectUse:
         return SpecPromiseObject;
     case DateObjectUse:
@@ -212,24 +218,6 @@ inline bool mayHaveTypeCheck(UseKind kind)
     return !shouldNotHaveTypeCheck(kind);
 }
 
-inline bool isNumerical(UseKind kind)
-{
-    switch (kind) {
-    case Int32Use:
-    case KnownInt32Use:
-    case NumberUse:
-    case RealNumberUse:
-    case Int52RepUse:
-    case DoubleRepUse:
-    case DoubleRepRealUse:
-    case AnyIntUse:
-    case DoubleRepAnyIntUse:
-        return true;
-    default:
-        return false;
-    }
-}
-
 inline bool isDouble(UseKind kind)
 {
     switch (kind) {
@@ -261,7 +249,7 @@ inline bool isCell(UseKind kind)
     case StringUse:
     case KnownStringUse:
     case SymbolUse:
-    case BigIntUse:
+    case HeapBigIntUse:
     case StringObjectUse:
     case StringOrStringObjectUse:
     case DateObjectUse:
index fa74afc..8ae9820 100644 (file)
@@ -260,6 +260,7 @@ inline CapabilityLevel canCompile(Node* node)
     case IsUndefinedOrNull:
     case IsBoolean:
     case IsNumber:
+    case IsBigInt:
     case NumberIsInteger:
     case IsObject:
     case IsObjectOrNull:
@@ -483,7 +484,9 @@ CapabilityLevel canCompile(Graph& graph)
                 case StringObjectUse:
                 case StringOrStringObjectUse:
                 case SymbolUse:
-                case BigIntUse:
+                case AnyBigIntUse:
+                case BigInt32Use:
+                case HeapBigIntUse:
                 case DateObjectUse:
                 case MapObjectUse:
                 case SetObjectUse:
index 5b4925c..c0f4f2b 100644 (file)
@@ -58,6 +58,9 @@ void CommonValues::initializeConstants(B3::Procedure& proc, B3::BasicBlock* bloc
     intPtrThree = block->appendNew<ConstPtrValue>(proc, Origin(), 3);
     intPtrEight = block->appendNew<ConstPtrValue>(proc, Origin(), 8);
     doubleZero = block->appendNew<ConstDoubleValue>(proc, Origin(), 0.);
+#if USE(BIGINT32)
+    bigInt32Zero = block->appendNew<Const64Value>(proc, Origin(), JSValue::BigInt32Tag);
+#endif
 }
 
 } } // namespace JSC::FTL
index c1d6d5a..31a3932 100644 (file)
@@ -56,7 +56,10 @@ public:
     LValue intPtrThree { nullptr };
     LValue intPtrEight { nullptr };
     LValue doubleZero { nullptr };
-    
+#if USE(BIGINT32)
+    LValue bigInt32Zero { nullptr };
+#endif
+
     const unsigned rangeKind { 0 };
     const unsigned profKind { 0 };
     const LValue branchWeights { nullptr };
index a3ebf71..43524df 100644 (file)
@@ -1356,6 +1356,9 @@ private:
         case IsNumber:
             compileIsNumber();
             break;
+        case IsBigInt:
+            compileIsBigInt();
+            break;
         case NumberIsInteger:
             compileNumberIsInteger();
             break;
@@ -2139,11 +2142,28 @@ private:
     {
         JSGlobalObject* globalObject = m_graph.globalObjectFor(m_node->origin.semantic);
 
-        if (m_node->isBinaryUseKind(BigIntUse)) {
-            LValue left = lowBigInt(m_node->child1());
-            LValue right = lowBigInt(m_node->child2());
+#if USE(BIGINT32)
+        if (m_node->isBinaryUseKind(BigInt32Use)) {
+            LValue left = lowBigInt32(m_node->child1());
+            LValue right = lowBigInt32(m_node->child2());
+
+            LValue unboxedLeft = unboxBigInt32(left);
+            LValue unboxedRight = unboxBigInt32(right);
+
+            CheckValue* result = m_out.speculateAdd(unboxedLeft, unboxedRight);
+            blessSpeculation(result, Overflow, noValue(), nullptr, m_origin);
+
+            LValue boxedResult = boxBigInt32(result);
+            setJSValue(boxedResult);
+            return;
+        }
+#endif
+
+        if (m_node->isBinaryUseKind(HeapBigIntUse)) {
+            LValue left = lowHeapBigInt(m_node->child1());
+            LValue right = lowHeapBigInt(m_node->child2());
 
-            LValue result = vmCall(pointerType(), operationAddBigInt, weakPointer(globalObject), left, right);
+            LValue result = vmCall(pointerType(), operationAddHeapBigInt, weakPointer(globalObject), left, right);
             setJSValue(result);
             return;
         }
@@ -2160,11 +2180,28 @@ private:
     {
         JSGlobalObject* globalObject = m_graph.globalObjectFor(m_node->origin.semantic);
 
-        if (m_node->isBinaryUseKind(BigIntUse)) {
-            LValue left = lowBigInt(m_node->child1());
-            LValue right = lowBigInt(m_node->child2());
+#if USE(BIGINT32)
+        if (m_node->isBinaryUseKind(BigInt32Use)) {
+            LValue left = lowBigInt32(m_node->child1());
+            LValue right = lowBigInt32(m_node->child2());
+
+            LValue unboxedLeft = unboxBigInt32(left);
+            LValue unboxedRight = unboxBigInt32(right);
+
+            CheckValue* result = m_out.speculateSub(unboxedLeft, unboxedRight);
+            blessSpeculation(result, Overflow, noValue(), nullptr, m_origin);
+
+            LValue boxedResult = boxBigInt32(result);
+            setJSValue(boxedResult);
+            return;
+        }
+#endif
+
+        if (m_node->isBinaryUseKind(HeapBigIntUse)) {
+            LValue left = lowHeapBigInt(m_node->child1());
+            LValue right = lowHeapBigInt(m_node->child2());
             
-            LValue result = vmCall(pointerType(), operationSubBigInt, weakPointer(globalObject), left, right);
+            LValue result = vmCall(pointerType(), operationSubHeapBigInt, weakPointer(globalObject), left, right);
             setJSValue(result);
             return;
         }
@@ -2181,11 +2218,28 @@ private:
     {
         JSGlobalObject* globalObject = m_graph.globalObjectFor(m_node->origin.semantic);
 
-        if (m_node->isBinaryUseKind(BigIntUse)) {
-            LValue left = lowBigInt(m_node->child1());
-            LValue right = lowBigInt(m_node->child2());
+#if USE(BIGINT32)
+        if (m_node->isBinaryUseKind(BigInt32Use)) {
+            LValue left = lowBigInt32(m_node->child1());
+            LValue right = lowBigInt32(m_node->child2());
+
+            LValue unboxedLeft = unboxBigInt32(left);
+            LValue unboxedRight = unboxBigInt32(right);
+
+            CheckValue* result = m_out.speculateMul(unboxedLeft, unboxedRight);
+            blessSpeculation(result, Overflow, noValue(), nullptr, m_origin);
+
+            LValue boxedResult = boxBigInt32(result);
+            setJSValue(boxedResult);
+            return;
+        }
+#endif
+
+        if (m_node->isBinaryUseKind(HeapBigIntUse)) {
+            LValue left = lowHeapBigInt(m_node->child1());
+            LValue right = lowHeapBigInt(m_node->child2());
             
-            LValue result = vmCall(Int64, operationMulBigInt, weakPointer(globalObject), left, right);
+            LValue result = vmCall(Int64, operationMulHeapBigInt, weakPointer(globalObject), left, right);
             setJSValue(result);
             return;
         }
@@ -2289,7 +2343,21 @@ private:
     void compileBinaryMathIC(BinaryArithProfile* arithProfile, Func1 repatchingFunction, Func2 nonRepatchingFunction)
     {
         Node* node = m_node;
-        
+
+#if USE(BIGINT32)
+        if (node->isBinaryUseKind(AnyBigIntUse)) {
+            // FIXME: This is not supported by the IC yet.
+            LValue left = lowJSValue(node->child1(), ManualOperandSpeculation);
+            LValue right = lowJSValue(node->child2(), ManualOperandSpeculation);
+            speculate(node, node->child1());
+            speculate(node, node->child2());
+
+            JSGlobalObject* globalObject = m_graph.globalObjectFor(m_node->origin.semantic);
+            setJSValue(vmCall(pointerType(), nonRepatchingFunction, weakPointer(globalObject), left, right));
+            return;
+        }
+#endif
+
         LValue left = lowJSValue(node->child1());
         LValue right = lowJSValue(node->child2());
 
@@ -2558,11 +2626,12 @@ private:
     void compileValueDiv()
     {
         JSGlobalObject* globalObject = m_graph.globalObjectFor(m_node->origin.semantic);
-        if (m_node->isBinaryUseKind(BigIntUse)) {
-            LValue left = lowBigInt(m_node->child1());
-            LValue right = lowBigInt(m_node->child2());
+        // FIXME: add a fast path for BigInt32 here
+        if (m_node->isBinaryUseKind(HeapBigIntUse)) {
+            LValue left = lowHeapBigInt(m_node->child1());
+            LValue right = lowHeapBigInt(m_node->child2());
             
-            LValue result = vmCall(pointerType(), operationDivBigInt, weakPointer(globalObject), left, right);
+            LValue result = vmCall(pointerType(), operationDivHeapBigInt, weakPointer(globalObject), left, right);
             setJSValue(result);
             return;
         }
@@ -2637,18 +2706,21 @@ private:
     void compileValueMod()
     {
         JSGlobalObject* globalObject = m_graph.globalObjectFor(m_node->origin.semantic);
-        if (m_node->binaryUseKind() == BigIntUse) {
-            LValue left = lowBigInt(m_node->child1());
-            LValue right = lowBigInt(m_node->child2());
+        // FIXME: add a BigInt32 fast path here
+        if (m_node->binaryUseKind() == HeapBigIntUse) {
+            LValue left = lowHeapBigInt(m_node->child1());
+            LValue right = lowHeapBigInt(m_node->child2());
 
-            LValue result = vmCall(pointerType(), operationModBigInt, weakPointer(globalObject), left, right);
+            LValue result = vmCall(pointerType(), operationModHeapBigInt, weakPointer(globalObject), left, right);
             setJSValue(result);
             return;
         }
 
-        DFG_ASSERT(m_graph, m_node, m_node->binaryUseKind() == UntypedUse, m_node->binaryUseKind());
-        LValue left = lowJSValue(m_node->child1());
-        LValue right = lowJSValue(m_node->child2());
+        DFG_ASSERT(m_graph, m_node, m_node->binaryUseKind() == UntypedUse || m_node->binaryUseKind() == AnyBigIntUse, m_node->binaryUseKind());
+        LValue left = lowJSValue(m_node->child1(), ManualOperandSpeculation);
+        LValue right = lowJSValue(m_node->child2(), ManualOperandSpeculation);
+        speculate(m_node, m_node->child1());
+        speculate(m_node, m_node->child2());
         LValue result = vmCall(Int64, operationValueMod, weakPointer(globalObject), left, right);
         setJSValue(result);
     }
@@ -2813,17 +2885,21 @@ private:
     void compileValuePow()
     {
         JSGlobalObject* globalObject = m_graph.globalObjectFor(m_node->origin.semantic);
-        if (m_node->isBinaryUseKind(BigIntUse)) {
-            LValue base = lowBigInt(m_node->child1());
-            LValue exponent = lowBigInt(m_node->child2());
+        // FIXME: maybe add a fast path for BigInt32 here
+        if (m_node->isBinaryUseKind(HeapBigIntUse)) {
+            LValue base = lowHeapBigInt(m_node->child1());
+            LValue exponent = lowHeapBigInt(m_node->child2());
             
-            LValue result = vmCall(pointerType(), operationPowBigInt, weakPointer(globalObject), base, exponent);
+            LValue result = vmCall(pointerType(), operationPowHeapBigInt, weakPointer(globalObject), base, exponent);
             setJSValue(result);
             return;
         }
 
-        LValue base = lowJSValue(m_node->child1());
-        LValue exponent = lowJSValue(m_node->child2());
+        ASSERT(m_node->isBinaryUseKind(UntypedUse) || m_node->isBinaryUseKind(AnyBigIntUse));
+        LValue base = lowJSValue(m_node->child1(), ManualOperandSpeculation);
+        LValue exponent = lowJSValue(m_node->child2(), ManualOperandSpeculation);
+        speculate(m_node, m_node->child1());
+        speculate(m_node, m_node->child2());
         LValue result = vmCall(Int64, operationValuePow, weakPointer(globalObject), base, exponent);
         setJSValue(result);
     }
@@ -3204,9 +3280,23 @@ private:
     void compileValueBitNot()
     {
         JSGlobalObject* globalObject = m_graph.globalObjectFor(m_node->origin.semantic);
-        if (m_node->child1().useKind() == BigIntUse) {
-            LValue operand = lowBigInt(m_node->child1());
-            LValue result = vmCall(pointerType(), operationBitNotBigInt, weakPointer(globalObject), operand);
+
+#if USE(BIGINT32)
+        if (m_node->child1().useKind() == BigInt32Use) {
+            LValue operand = lowBigInt32(m_node->child1());
+            // The following trick relies on details of the representation of BigInt32, and will have to be updated if we move bits around.
+            static_assert(JSValue::BigInt32Tag == 0x12);
+            static_assert(JSValue::BigInt32Mask == 0xfffe000000000012);
+            uint64_t maskForBigInt32Bits = 0x0000ffffffff0000;
+            LValue result = m_out.bitXor(operand, m_out.constInt64(maskForBigInt32Bits));
+            setJSValue(result);
+            return;
+        }
+#endif
+
+        if (m_node->child1().useKind() == HeapBigIntUse) {
+            LValue operand = lowHeapBigInt(m_node->child1());
+            LValue result = vmCall(pointerType(), operationBitNotHeapBigInt, weakPointer(globalObject), operand);
             setJSValue(result);
             return;
         }
@@ -3224,11 +3314,23 @@ private:
     void compileValueBitAnd()
     {
         JSGlobalObject* globalObject = m_graph.globalObjectFor(m_node->origin.semantic);
-        if (m_node->isBinaryUseKind(BigIntUse)) {
-            LValue left = lowBigInt(m_node->child1());
-            LValue right = lowBigInt(m_node->child2());
+
+#if USE(BIGINT32)
+        if (m_node->isBinaryUseKind(BigInt32Use)) {
+            LValue left = lowBigInt32(m_node->child1());
+            LValue right = lowBigInt32(m_node->child2());
+            // No need to unbox, since the tagging is not affected by bitAnd
+            LValue result = m_out.bitAnd(left, right);
+            setJSValue(result);
+            return;
+        }
+#endif
+
+        if (m_node->isBinaryUseKind(HeapBigIntUse)) {
+            LValue left = lowHeapBigInt(m_node->child1());
+            LValue right = lowHeapBigInt(m_node->child2());
             
-            LValue result = vmCall(pointerType(), operationBitAndBigInt, weakPointer(globalObject), left, right);
+            LValue result = vmCall(pointerType(), operationBitAndHeapBigInt, weakPointer(globalObject), left, right);
             setJSValue(result);
             return;
         }
@@ -3244,11 +3346,23 @@ private:
     void compileValueBitOr()
     {
         JSGlobalObject* globalObject = m_graph.globalObjectFor(m_node->origin.semantic);
-        if (m_node->isBinaryUseKind(BigIntUse)) {
-            LValue left = lowBigInt(m_node->child1());
-            LValue right = lowBigInt(m_node->child2());
 
-            LValue result = vmCall(pointerType(), operationBitOrBigInt, weakPointer(globalObject), left, right);
+#if USE(BIGINT32)
+        if (m_node->isBinaryUseKind(BigInt32Use)) {
+            LValue left = lowBigInt32(m_node->child1());
+            LValue right = lowBigInt32(m_node->child2());
+            // No need to unbox, since the tagging is not affected by bitAnd
+            LValue result = m_out.bitOr(left, right);
+            setJSValue(result);
+            return;
+        }
+#endif
+
+        if (m_node->isBinaryUseKind(HeapBigIntUse)) {
+            LValue left = lowHeapBigInt(m_node->child1());
+            LValue right = lowHeapBigInt(m_node->child2());
+
+            LValue result = vmCall(pointerType(), operationBitOrHeapBigInt, weakPointer(globalObject), left, right);
             setJSValue(result);
             return;
         }
@@ -3264,11 +3378,23 @@ private:
     void compileValueBitXor()
     {
         JSGlobalObject* globalObject = m_graph.globalObjectFor(m_node->origin.semantic);
-        if (m_node->isBinaryUseKind(BigIntUse)) {
-            LValue left = lowBigInt(m_node->child1());
-            LValue right = lowBigInt(m_node->child2());
 
-            LValue result = vmCall(pointerType(), operationBitXorBigInt, weakPointer(globalObject), left, right);
+#if USE(BIGINT32)
+        if (m_node->isBinaryUseKind(BigInt32Use)) {
+            LValue left = lowBigInt32(m_node->child1());
+            LValue right = lowBigInt32(m_node->child2());
+            LValue resultMissingTag = m_out.bitXor(left, right);
+            LValue result = m_out.bitOr(resultMissingTag, m_out.constInt64(JSValue::BigInt32Tag));
+            setJSValue(result);
+            return;
+        }
+#endif
+
+        if (m_node->isBinaryUseKind(HeapBigIntUse)) {
+            LValue left = lowHeapBigInt(m_node->child1());
+            LValue right = lowHeapBigInt(m_node->child2());
+
+            LValue result = vmCall(pointerType(), operationBitXorHeapBigInt, weakPointer(globalObject), left, right);
             setJSValue(result);
             return;
         }
@@ -3284,11 +3410,29 @@ private:
     void compileValueBitRShift()
     {
         JSGlobalObject* globalObject = m_graph.globalObjectFor(m_node->origin.semantic);
-        if (m_node->isBinaryUseKind(BigIntUse)) {
-            LValue left = lowBigInt(m_node->child1());
-            LValue right = lowBigInt(m_node->child2());
 
-            LValue result = vmCall(pointerType(), operationBitRShiftBigInt, weakPointer(globalObject), left, right);
+#if USE(BIGINT32)
+        if (m_node->isBinaryUseKind(AnyBigIntUse) || m_node->isBinaryUseKind(BigInt32Use)) {
+            // FIXME: do something smarter here
+            // Things are a bit tricky because a right-shift by a negative number is a left-shift for BigInts.
+            // So even a right shift can overflow.
+
+            LValue left = lowJSValue(m_node->child1(), ManualOperandSpeculation);
+            LValue right = lowJSValue(m_node->child2(), ManualOperandSpeculation);
+            speculate(m_node, m_node->child1());
+            speculate(m_node, m_node->child2());
+
+            LValue result = vmCall(pointerType(), operationValueBitRShift, weakPointer(globalObject), left, right);
+            setJSValue(result);
+            return;
+        }
+#endif // USE(BIGINT32)
+
+        if (m_node->isBinaryUseKind(HeapBigIntUse)) {
+            LValue left = lowHeapBigInt(m_node->child1());
+            LValue right = lowHeapBigInt(m_node->child2());
+
+            LValue result = vmCall(pointerType(), operationBitRShiftHeapBigInt, weakPointer(globalObject), left, right);
             setJSValue(result);
             return;
         }
@@ -3300,24 +3444,25 @@ private:
     {
         setInt32(m_out.aShr(
             lowInt32(m_node->child1()),
-            m_out.bitAnd(lowInt32(m_node->child2()), m_out.constInt32(31))));
+            m_out.bitAnd(lowInt32(m_node->child2()), m_out.constInt32(31)))); // FIXME: I don't think that the BitAnd is useful, it is included in the semantics of shift in B3
     }
     
     void compileArithBitLShift()
     {
         setInt32(m_out.shl(
             lowInt32(m_node->child1()),
-            m_out.bitAnd(lowInt32(m_node->child2()), m_out.constInt32(31))));
+            m_out.bitAnd(lowInt32(m_node->child2()), m_out.constInt32(31)))); // FIXME: I don't think that the BitAnd is useful, it is included in the semantics of shift in B3
     }
     
     void compileValueBitLShift()
     {
         JSGlobalObject* globalObject = m_graph.globalObjectFor(m_node->origin.semantic);
-        if (m_node->isBinaryUseKind(BigIntUse)) {
-            LValue left = lowBigInt(m_node->child1());
-            LValue right = lowBigInt(m_node->child2());
+        // FIXME: consider adding a fast path for BigInt32 here.
+        if (m_node->isBinaryUseKind(HeapBigIntUse)) {
+            LValue left = lowHeapBigInt(m_node->child1());
+            LValue right = lowHeapBigInt(m_node->child2());
             
-            LValue result = vmCall(pointerType(), operationBitLShiftBigInt, weakPointer(globalObject), left, right);
+            LValue result = vmCall(pointerType(), operationBitLShiftHeapBigInt, weakPointer(globalObject), left, right);
             setJSValue(result);
             return;
         }
@@ -3334,7 +3479,7 @@ private:
         }
         setInt32(m_out.lShr(
             lowInt32(m_node->child1()),
-            m_out.bitAnd(lowInt32(m_node->child2()), m_out.constInt32(31))));
+            m_out.bitAnd(lowInt32(m_node->child2()), m_out.constInt32(31)))); // FIXME: I don't think that the BitAnd is useful, it is included in the semantics of shift in B3
     }
     
     void compileUInt32ToNumber()
@@ -7384,6 +7529,9 @@ private:
         
         if (abstractValue(m_node->child1()).m_type & (SpecBytecodeNumber | SpecBigInt)) {
             LBasicBlock notNumber = m_out.newBlock();
+#if USE(BIGINT32)
+            LBasicBlock notBigInt32 = m_out.newBlock();
+#endif
             LBasicBlock isCellPath = m_out.newBlock();
             LBasicBlock slowPath = m_out.newBlock();
             LBasicBlock continuation = m_out.newBlock();
@@ -7393,10 +7541,14 @@ private:
 
             // notNumber case.
             LBasicBlock lastNext = m_out.appendTo(notNumber, continuation);
+#if USE(BIGINT32)
+            m_out.branch(isBigInt32(value, provenType(m_node->child1())), unsure(continuation), unsure(notBigInt32));
+            m_out.appendTo(notBigInt32);
+#endif
             m_out.branch(isCell(value, provenType(m_node->child1())), unsure(isCellPath), unsure(slowPath));
 
             m_out.appendTo(isCellPath);
-            m_out.branch(isBigInt(value, provenType(m_node->child1())), unsure(continuation), unsure(slowPath));
+            m_out.branch(isHeapBigInt(value, provenType(m_node->child1())), unsure(continuation), unsure(slowPath));
             
             m_out.appendTo(slowPath);
             // We have several attempts to remove ToNumeric. But ToNumeric still exists.
@@ -8466,7 +8618,10 @@ private:
             || m_node->isBinaryUseKind(BooleanUse)
             || m_node->isBinaryUseKind(SymbolUse)
             || m_node->isBinaryUseKind(StringIdentUse)
-            || m_node->isBinaryUseKind(StringUse)) {
+            || m_node->isBinaryUseKind(StringUse)
+            || m_node->isBinaryUseKind(BigInt32Use)
+            || m_node->isBinaryUseKind(HeapBigIntUse)
+            || m_node->isBinaryUseKind(AnyBigIntUse)) {
             compileCompareStrictEq();
             return;
         }
@@ -8509,7 +8664,105 @@ private:
                 m_out.equal(lowInt32(m_node->child1()), lowInt32(m_node->child2())));
             return;
         }
-        
+
+#if USE(BIGINT32)
+        if (m_node->isBinaryUseKind(BigInt32Use)) {
+            LValue left = lowBigInt32(m_node->child1());
+            LValue right = lowBigInt32(m_node->child2());
+
+            // No need to unbox since the tag bits are the same on both sides
+            LValue result = m_out.equal(left, right);
+            setBoolean(result);
+            return;
+        }
+
+        if (m_node->isBinaryUseKind(AnyBigIntUse)) {
+            LValue left = lowJSValue(m_node->child1(), ManualOperandSpeculation);
+            LValue right = lowJSValue(m_node->child2(), ManualOperandSpeculation);
+
+            // Note that we cannot start with if (left == right), because we must insert the right checks (see ManualOperandSpeculation above)
+            // The code that we generate looks like the following pseudo-code:
+            /*
+             if (isBigInt32(left)) {
+                if (isBigInt32(right))
+                    return left == right;
+                CHECK(isHeapBigInt(right));
+                return call(JSBigInt::equalsToInt32(right, unboxed(left));
+             }
+             CHECK(isHeapBigInt(left))
+             if (left == right)
+                return true;
+             if (isBigInt32(right))
+                return call(JSBigInt::equalsToInt32(left, unboxed(right));
+             CHECK(isHeapBigInt(right));
+             return call(JSBigInt::equals(left, right));
+            */
+            LBasicBlock leftIsBigInt32 = m_out.newBlock();
+            LBasicBlock bothAreBigInt32 = m_out.newBlock();
+            LBasicBlock onlyLeftIsBigInt32 = m_out.newBlock();
+            LBasicBlock leftIsNotBigInt32 = m_out.newBlock();
+            LBasicBlock leftEqualsRight = m_out.newBlock();
+            LBasicBlock leftIsHeapBigInt = m_out.newBlock();
+            LBasicBlock rightIsBigInt32 = m_out.newBlock();
+            LBasicBlock rightIsNotBigInt32 = m_out.newBlock();
+            LBasicBlock continuation = m_out.newBlock();
+
+            // Inserts a check that a value is a HeapBigInt, assuming only that we know it is not a BigInt32
+            auto checkIsHeapBigInt = [&](LValue lowValue, Edge highValue) {
+                if (m_interpreter.needsTypeCheck(highValue, SpecHeapBigInt)) {
+                    ASSERT(mayHaveTypeCheck(highValue.useKind()));
+                    LValue checkFailed = isNotHeapBigIntUnknownWhetherCell(lowValue, ~SpecBigInt32);
+                    appendOSRExit(BadType, jsValueValue(lowValue), highValue.node(), checkFailed, m_origin);
+                }
+            };
+
+            m_out.branch(isBigInt32(left, provenType(m_node->child1())), unsure(leftIsBigInt32), unsure(leftIsNotBigInt32));
+
+            LBasicBlock lastNext = m_out.appendTo(leftIsBigInt32, bothAreBigInt32);
+            m_out.branch(isBigInt32(right, provenType(m_node->child2())), unsure(bothAreBigInt32), unsure(onlyLeftIsBigInt32));
+
+            m_out.appendTo(bothAreBigInt32, onlyLeftIsBigInt32);
+            ValueFromBlock resultBothAreBigInt32 = m_out.anchor(m_out.equal(left, right));
+            m_out.jump(continuation);
+
+            m_out.appendTo(onlyLeftIsBigInt32, leftIsNotBigInt32);
+            checkIsHeapBigInt(right, m_node->child2());
+            LValue unboxedLeft = unboxBigInt32(left);
+            ValueFromBlock resultLeftIsBigInt32 = m_out.anchor(m_out.notNull(vmCall(pointerType(), operationCompareEqHeapBigIntToInt32, weakPointer(globalObject), right, unboxedLeft)));
+            m_out.jump(continuation);
+
+            m_out.appendTo(leftIsNotBigInt32, leftEqualsRight);
+            checkIsHeapBigInt(left, m_node->child1());
+            m_out.branch(m_out.equal(left, right), unsure(leftEqualsRight), unsure(leftIsHeapBigInt));
+
+            m_out.appendTo(leftEqualsRight, leftIsHeapBigInt);
+            ValueFromBlock resultLeftEqualsRight = m_out.anchor(m_out.booleanTrue);
+            m_out.jump(continuation);
+
+            m_out.appendTo(leftIsHeapBigInt, rightIsBigInt32);
+            m_out.branch(isBigInt32(right, provenType(m_node->child2())), unsure(rightIsBigInt32), unsure(rightIsNotBigInt32));
+
+            m_out.appendTo(rightIsBigInt32, rightIsNotBigInt32);
+            LValue unboxedRight = unboxBigInt32(right);
+            ValueFromBlock resultRightIsBigInt32 = m_out.anchor(m_out.notNull(vmCall(pointerType(), operationCompareEqHeapBigIntToInt32, weakPointer(globalObject), left, unboxedRight)));
+            m_out.jump(continuation);
+
+            m_out.appendTo(rightIsNotBigInt32, continuation);
+            checkIsHeapBigInt(right, m_node->child2());
+            // FIXME: [ESNext][BigInt] Create specialized version of strict equals for big ints
+            // https://bugs.webkit.org/show_bug.cgi?id=182895
+            ValueFromBlock resultBothHeapBigInt = m_out.anchor(m_out.notNull(vmCall(pointerType(), operationCompareStrictEq, weakPointer(globalObject), left, right)));
+            m_out.jump(continuation);
+
+            m_out.appendTo(continuation, lastNext);
+            setBoolean(m_out.phi(Int32, resultBothAreBigInt32, resultLeftIsBigInt32, resultLeftEqualsRight, resultRightIsBigInt32, resultBothHeapBigInt));
+
+            m_interpreter.filter(m_node->child1(), SpecBigInt);
+            m_interpreter.filter(m_node->child2(), SpecBigInt);
+            return;
+        }
+#endif // USE(BIGINT32)
+
         if (m_node->isBinaryUseKind(Int52RepUse)) {
             Int52Kind kind;
             LValue left = lowWhicheverInt52(m_node->child1(), kind);
@@ -8592,11 +8845,11 @@ private:
             return;
         }
         
-        if (m_node->isBinaryUseKind(BigIntUse)) {
-            // FIXME: [ESNext][BigInt] Create specialized version of strict equals for BigIntUse
+        if (m_node->isBinaryUseKind(HeapBigIntUse)) {
+            // FIXME: [ESNext][BigInt] Create specialized version of strict equals for big ints
             // https://bugs.webkit.org/show_bug.cgi?id=182895
-            LValue left = lowBigInt(m_node->child1());
-            LValue right = lowBigInt(m_node->child2());
+            LValue left = lowHeapBigInt(m_node->child1());
+            LValue right = lowHeapBigInt(m_node->child2());
 
             LBasicBlock notTriviallyEqualCase = m_out.newBlock();
             LBasicBlock continuation = m_out.newBlock();
@@ -8682,6 +8935,7 @@ private:
             return;
         }
 
+        // FIXME: we can do something much smarter here, see the DFGSpeculativeJIT approach in e.g. SpeculativeJIT::nonSpeculativePeepholeStrictEq
         DFG_ASSERT(m_graph, m_node, m_node->isBinaryUseKind(UntypedUse), m_node->child1().useKind(), m_node->child2().useKind());
         nonSpeculativeCompare(
             [&] (LValue left, LValue right) {
@@ -10645,7 +10899,37 @@ private:
         m_out.appendTo(continuation, lastNext);
         setBoolean(m_out.phi(Int32, trueResult, falseResult, patchpointResult));
     }
-    
+
+#if USE(BIGINT32)
+    void compileIsBigInt()
+    {
+        LValue value = lowJSValue(m_node->child1());
+
+        LBasicBlock isCellCase = m_out.newBlock();
+        LBasicBlock isNotCellCase = m_out.newBlock();
+        LBasicBlock continuation = m_out.newBlock();
+
+        m_out.branch(isCell(value, provenType(m_node->child1())), unsure(isCellCase), unsure(isNotCellCase));
+
+        LBasicBlock lastNext = m_out.appendTo(isNotCellCase, isCellCase);
+        // FIXME: we should filter the provenType to include the fact that we know we are not dealing with a cell
+        ValueFromBlock notCellResult = m_out.anchor(isBigInt32(value, provenType(m_node->child1())));
+        m_out.jump(continuation);
+
+        m_out.appendTo(isCellCase, continuation);
+        ValueFromBlock cellResult = m_out.anchor(isCellWithType(value, m_node->queriedType(), m_node->speculatedTypeForQuery(), provenType(m_node->child1())));
+        m_out.jump(continuation);
+
+        m_out.appendTo(continuation, lastNext);
+        setBoolean(m_out.phi(Int32, notCellResult, cellResult));
+    }
+#else // if !USE(BIGINT32)
+    NO_RETURN_DUE_TO_CRASH ALWAYS_INLINE void compileIsBigInt()
+    {
+        // If we are not dealing with BigInt32, we should just emit IsCellWithType(HeapBigInt) instead.
+        RELEASE_ASSERT_NOT_REACHED();
+    }
+#endif
     void compileIsCellWithType()
     {
         if (m_node->child1().useKind() == UntypedUse) {
@@ -14383,9 +14667,11 @@ private:
     void emitBinarySnippet(J_JITOperation_GJJ slowPathFunction)
     {
         Node* node = m_node;
-        
-        LValue left = lowJSValue(node->child1());
-        LValue right = lowJSValue(node->child2());
+
+        LValue left = lowJSValue(node->child1(), ManualOperandSpeculation);
+        LValue right = lowJSValue(node->child2(), ManualOperandSpeculation);
+        speculate(node->child1());
+        speculate(node->child2());
 
         SnippetOperand leftOperand(m_state.forNode(node->child1()).resultType());
         SnippetOperand rightOperand(m_state.forNode(node->child2()).resultType());
@@ -14451,8 +14737,11 @@ private:
     {
         Node* node = m_node;
         
-        LValue left = lowJSValue(node->child1());
-        LValue right = lowJSValue(node->child2());
+        ASSERT(node->isBinaryUseKind(UntypedUse) || node->isBinaryUseKind(AnyBigIntUse));
+        LValue left = lowJSValue(node->child1(), ManualOperandSpeculation);
+        LValue right = lowJSValue(node->child2(), ManualOperandSpeculation);
+        speculate(node, node->child1());
+        speculate(node, node->child2());
 
         SnippetOperand leftOperand(m_state.forNode(node->child1()).resultType());
         SnippetOperand rightOperand(m_state.forNode(node->child2()).resultType());
@@ -15064,7 +15353,7 @@ private:
             
             // Implements the following control flow structure:
             // if (value is cell) {
-            //     if (value is string or value is BigInt)
+            //     if (value is string or value is HeapBigInt)
             //         result = !!value->length
             //     else {
             //         do evil things for masquerades-as-undefined
@@ -15074,6 +15363,8 @@ private:
             //     result = !!unboxInt32(value)
             // } else if (value is number) {
             //     result = !!unboxDouble(value)
+            // } else if (value is BigInt32) {
+            //     result = (value != BigInt32Tag)
             // } else {
             //     result = value == jsTrue
             // }
@@ -15081,13 +15372,17 @@ private:
             LBasicBlock cellCase = m_out.newBlock();
             LBasicBlock notStringCase = m_out.newBlock();
             LBasicBlock stringCase = m_out.newBlock();
-            LBasicBlock bigIntCase = m_out.newBlock();
-            LBasicBlock notStringOrBigIntCase = m_out.newBlock();
+            LBasicBlock heapBigIntCase = m_out.newBlock();
+            LBasicBlock notStringNorHeapBigIntCase = m_out.newBlock();
             LBasicBlock notCellCase = m_out.newBlock();
             LBasicBlock int32Case = m_out.newBlock();
             LBasicBlock notInt32Case = m_out.newBlock();
             LBasicBlock doubleCase = m_out.newBlock();
             LBasicBlock notDoubleCase = m_out.newBlock();
+#if USE(BIGINT32)
+            LBasicBlock bigInt32Case = m_out.newBlock();
+            LBasicBlock notBigInt32Case = m_out.newBlock();
+#endif
             LBasicBlock continuation = m_out.newBlock();
             
             Vector<ValueFromBlock> results;
@@ -15101,20 +15396,20 @@ private:
             
             m_out.appendTo(notStringCase, stringCase);
             m_out.branch(
-                isBigInt(value, provenType(edge) & (SpecCell - SpecString)),
-                unsure(bigIntCase), unsure(notStringOrBigIntCase));
+                isHeapBigInt(value, provenType(edge) & (SpecCell - SpecString)),
+                unsure(heapBigIntCase), unsure(notStringNorHeapBigIntCase));
 
-            m_out.appendTo(stringCase, bigIntCase);
+            m_out.appendTo(stringCase, heapBigIntCase);
             results.append(m_out.anchor(m_out.notEqual(value, weakPointer(jsEmptyString(m_graph.m_vm)))));
             m_out.jump(continuation);
 
-            m_out.appendTo(bigIntCase, notStringOrBigIntCase);
+            m_out.appendTo(heapBigIntCase, notStringNorHeapBigIntCase);
             LValue nonZeroBigInt = m_out.notZero32(
                 m_out.load32NonNegative(value, m_heaps.JSBigInt_length));
             results.append(m_out.anchor(nonZeroBigInt));
             m_out.jump(continuation);
             
-            m_out.appendTo(notStringOrBigIntCase, notCellCase);
+            m_out.appendTo(notStringNorHeapBigIntCase, notCellCase);
             LValue isTruthyObject;
             if (masqueradesAsUndefinedWatchpointIsStillValid())
                 isTruthyObject = m_out.booleanTrue;
@@ -15157,8 +15452,22 @@ private:
                 unboxDouble(value), m_out.constDouble(0));
             results.append(m_out.anchor(doubleIsTruthy));
             m_out.jump(continuation);
+
+#if USE(BIGINT32)
+            m_out.appendTo(notDoubleCase, bigInt32Case);
+            m_out.branch(
+                isBigInt32(value, provenType(edge) & ~SpecCell),
+                unsure(bigInt32Case), unsure(notBigInt32Case));
+
+            m_out.appendTo(bigInt32Case, notBigInt32Case);
+            LValue bigInt32NotZero = m_out.notEqual(value, m_out.constInt64(JSValue::BigInt32Tag));
+            results.append(m_out.anchor(bigInt32NotZero));
+            m_out.jump(continuation);
             
+            m_out.appendTo(notBigInt32Case, continuation);
+#else
             m_out.appendTo(notDoubleCase, continuation);
+#endif
             LValue miscIsTruthy = m_out.equal(
                 value, m_out.constInt64(JSValue::encode(jsBoolean(true))));
             results.append(m_out.anchor(miscIsTruthy));
@@ -15763,13 +16072,15 @@ private:
         //         }
         //     } else if (is string) {
         //         return string
-        //     } else if (is bigint) {
+        //     } else if (is heapbigint) {
         //         return bigint
         //     } else {
         //         return symbol
         //     }
         // } else if (is number) {
         //     return number
+        // } else if (is bigint32) {
+        //     return bigint
         // } else if (is null) {
         //     return object
         // } else if (is boolean) {
@@ -15797,6 +16108,9 @@ private:
         LBasicBlock notCellCase = m_out.newBlock();
         LBasicBlock numberCase = m_out.newBlock();
         LBasicBlock notNumberCase = m_out.newBlock();
+#if USE(BIGINT32)
+        LBasicBlock notBigInt32Case = m_out.newBlock();
+#endif
         LBasicBlock notNullCase = m_out.newBlock();
         LBasicBlock booleanCase = m_out.newBlock();
         LBasicBlock undefinedCase = m_out.newBlock();
@@ -15849,7 +16163,7 @@ private:
 
         m_out.appendTo(notStringCase, bigIntCase);
         m_out.branch(
-            isBigInt(value, provenType(child) & (SpecCell - SpecObject - SpecString)),
+            isHeapBigInt(value, provenType(child) & (SpecCell - SpecObject - SpecString)),
             unsure(bigIntCase), unsure(symbolCase));
 
         m_out.appendTo(bigIntCase, symbolCase);
@@ -15865,8 +16179,15 @@ private:
         
         m_out.appendTo(numberCase, notNumberCase);
         functor(TypeofType::Number);
-        
+
+#if USE(BIGINT32)
+        m_out.appendTo(notNumberCase, notBigInt32Case);
+        m_out.branch(isBigInt32(value, provenType(child) & ~SpecCell), unsure(bigIntCase), unsure(notBigInt32Case));
+
+        m_out.appendTo(notBigInt32Case, notNullCase);
+#else
         m_out.appendTo(notNumberCase, notNullCase);
+#endif
         LValue isNull;
         if (provenType(child) & SpecOther)
             isNull = m_out.equal(value, m_out.constInt64(JSValue::ValueNull));
@@ -16564,15 +16885,33 @@ private:
         return result;
     }
 
-    LValue lowBigInt(Edge edge, OperandSpeculationMode mode = AutomaticOperandSpeculation)
+    LValue lowHeapBigInt(Edge edge, OperandSpeculationMode mode = AutomaticOperandSpeculation)
     {
-        ASSERT_UNUSED(mode, mode == ManualOperandSpeculation || edge.useKind() == BigIntUse);
+        ASSERT_UNUSED(mode, mode == ManualOperandSpeculation || edge.useKind() == HeapBigIntUse);
 
         LValue result = lowCell(edge, mode);
-        speculateBigInt(edge, result);
+        speculateHeapBigInt(edge, result);
         return result;
     }
-    
+
+#if USE(BIGINT32)
+    LValue lowBigInt32(Edge edge, OperandSpeculationMode mode = AutomaticOperandSpeculation)
+    {
+        ASSERT_UNUSED(mode, mode == ManualOperandSpeculation || edge.useKind() == BigInt32Use);
+
+        LoweredNodeValue value = m_jsValueValues.get(edge.node());
+        if (isValid(value)) {
+            LValue result = value.value();
+            FTL_TYPE_CHECK(jsValueValue(result), edge, SpecBigInt32, isNotBigInt32(result));
+            return result;
+        }
+
+        if (mayHaveTypeCheck(edge.useKind()))
+            terminate(Uncountable);
+        return m_out.bigInt32Zero;
+    }
+#endif
+
     LValue lowNonNullObject(Edge edge, OperandSpeculationMode mode = AutomaticOperandSpeculation)
     {
         ASSERT_UNUSED(mode, mode == ManualOperandSpeculation || edge.useKind() == ObjectUse);
@@ -16758,7 +17097,73 @@ private:
     {
         return m_out.add(m_out.zeroExt(value, Int64), m_numberTag);
     }
-    
+
+#if USE(BIGINT32)
+    LValue isBigInt32(LValue jsValue, SpeculatedType type = SpecFullTop)
+    {
+        if (LValue proven = isProvenValue(type, SpecBigInt32))
+            return proven;
+        return m_out.equal(
+            m_out.bitAnd(jsValue, m_out.constInt64(JSValue::BigInt32Mask)),
+            m_out.constInt64(JSValue::BigInt32Tag));
+    }
+    LValue isNotBigInt32(LValue jsValue, SpeculatedType type = SpecFullTop)
+    {
+        if (LValue proven = isProvenValue(type, ~SpecBigInt32))
+            return proven;
+        return m_out.notEqual(
+            m_out.bitAnd(jsValue, m_out.constInt64(JSValue::BigInt32Mask)),
+            m_out.constInt64(JSValue::BigInt32Tag));
+    }
+    LValue unboxBigInt32(LValue jsValue)
+    {
+        return m_out.castToInt32(m_out.lShr(jsValue, m_out.constInt64(16)));
+    }
+    LValue boxBigInt32(LValue int32Value)
+    {
+        return m_out.bitOr(
+            m_out.shl(m_out.zeroExt(int32Value, B3::Int64), m_out.constInt64(16)),
+            m_out.constInt64(JSValue::BigInt32Tag));
+    }
+    LValue isNotAnyBigInt(LValue jsValue, SpeculatedType type = SpecFullTop)
+    {
+        if (LValue proven = isProvenValue(type, ~SpecBigInt))
+            return proven;
+
+        // if (isBigInt32)
+        //   return false
+        // if (!isCell)
+        //   return true;
+        // return !isHeapBigInt
+        LBasicBlock isBigInt32Case = m_out.newBlock();
+        LBasicBlock isNotBigInt32Case = m_out.newBlock();
+        LBasicBlock isNotCellCase = m_out.newBlock();
+        LBasicBlock isCellCase = m_out.newBlock();
+        LBasicBlock continuation = m_out.newBlock();
+
+        m_out.branch(isBigInt32(jsValue, type), unsure(isBigInt32Case), unsure(isNotBigInt32Case));
+
+        LBasicBlock lastNext = m_out.appendTo(isBigInt32Case, isNotBigInt32Case);
+        ValueFromBlock returnFalse = m_out.anchor(m_out.booleanFalse);
+        m_out.jump(continuation);
+
+        m_out.appendTo(isNotBigInt32Case, isNotCellCase);
+        // FIXME: we should filter the type passed to isCell to account for the previous test that told us we are definitely not a BigInt32.
+        m_out.branch(isCell(jsValue, type), unsure(isCellCase), unsure(isNotCellCase));
+
+        m_out.appendTo(isNotCellCase, isCellCase);
+        ValueFromBlock returnTrue = m_out.anchor(m_out.booleanTrue);
+        m_out.jump(continuation);
+
+        m_out.appendTo(isCellCase, continuation);
+        ValueFromBlock returnIsNotHeapBigInt = m_out.anchor(isNotHeapBigInt(jsValue));
+        m_out.jump(continuation);
+
+        m_out.appendTo(continuation, lastNext);
+        return m_out.phi(Int32, returnFalse, returnTrue, returnIsNotHeapBigInt);
+    }
+#endif // USE(BIGINT32)
+
     LValue isCellOrMisc(LValue jsValue, SpeculatedType type = SpecFullTop)
     {
         if (LValue proven = isProvenValue(type, SpecCellCheck | SpecMisc))
@@ -17070,8 +17475,16 @@ private:
         case BooleanUse:
             speculateBoolean(edge);
             break;
-        case BigIntUse:
-            speculateBigInt(edge);
+#if USE(BIGINT32)
+        case BigInt32Use:
+            speculateBigInt32(edge);
+            break;
+        case AnyBigIntUse:
+            speculateAnyBigInt(edge);
+            break;
+#endif // USE(BIGINT32)
+        case HeapBigIntUse:
+            speculateHeapBigInt(edge);
             break;
         case NotStringVarUse:
             speculateNotStringVar(edge);
@@ -17257,18 +17670,38 @@ private:
             m_out.constInt32(vm().symbolStructure->id()));
     }
 
-    LValue isNotBigInt(LValue cell, SpeculatedType type = SpecFullTop)
+    LValue isNotHeapBigIntUnknownWhetherCell(LValue value, SpeculatedType type = SpecFullTop)
+    {
+        if (LValue proven = isProvenValue(type, ~SpecHeapBigInt))
+            return proven;
+
+        LBasicBlock isCellCase = m_out.newBlock();
+        LBasicBlock continuation = m_out.newBlock();
+
+        ValueFromBlock defaultToFalse = m_out.anchor(m_out.booleanFalse);
+        m_out.branch(isCell(value, type), unsure(isCellCase), unsure(continuation));
+
+        LBasicBlock lastNext = m_out.appendTo(isCellCase, continuation);
+        ValueFromBlock returnForCell = m_out.anchor(isHeapBigInt(value, type));
+        m_out.jump(continuation);
+
+        m_out.appendTo(continuation, lastNext);
+        LValue result = m_out.phi(Int32, defaultToFalse, returnForCell);
+        return result;
+    }
+
+    LValue isNotHeapBigInt(LValue cell, SpeculatedType type = SpecFullTop)
     {
-        if (LValue proven = isProvenValue(type & SpecCell, ~SpecBigInt))
+        if (LValue proven = isProvenValue(type & SpecCell, ~SpecHeapBigInt))
             return proven;
         return m_out.notEqual(
             m_out.load32(cell, m_heaps.JSCell_structureID),
             m_out.constInt32(vm().bigIntStructure->id()));
     }
 
-    LValue isBigInt(LValue cell, SpeculatedType type = SpecFullTop)
+    LValue isHeapBigInt(LValue cell, SpeculatedType type = SpecFullTop)
     {
-        if (LValue proven = isProvenValue(type & SpecCell, SpecBigInt))
+        if (LValue proven = isProvenValue(type & SpecCell, SpecHeapBigInt))
             return proven;
         return m_out.equal(
             m_out.load32(cell, m_heaps.JSCell_structureID),
@@ -17726,15 +18159,28 @@ private:
         speculateSymbol(edge, lowCell(edge));
     }
 
-    void speculateBigInt(Edge edge, LValue cell)
+    void speculateHeapBigInt(Edge edge, LValue cell)
+    {
+        FTL_TYPE_CHECK(jsValueValue(cell), edge, SpecHeapBigInt, isNotHeapBigInt(cell));
+    }
+    void speculateHeapBigInt(Edge edge)
     {
-        FTL_TYPE_CHECK(jsValueValue(cell), edge, SpecBigInt, isNotBigInt(cell));
+        speculateHeapBigInt(edge, lowCell(edge));
     }
 
-    void speculateBigInt(Edge edge)
+#if USE(BIGINT32)
+    void speculateBigInt32(Edge edge)
     {
-        speculateBigInt(edge, lowCell(edge));
+        LValue value = lowJSValue(edge, ManualOperandSpeculation);
+        FTL_TYPE_CHECK(jsValueValue(value), edge, SpecBigInt32, isNotBigInt32(value));
+    }
+
+    void speculateAnyBigInt(Edge edge)
+    {
+        LValue value = lowJSValue(edge, ManualOperandSpeculation);
+        FTL_TYPE_CHECK(jsValueValue(value), edge, SpecBigInt, isNotAnyBigInt(value));
     }
+#endif
 
     void speculateNonNullObject(Edge edge, LValue cell)
     {
index d372277..3c9137e 100644 (file)
@@ -282,7 +282,7 @@ static void compileStub(VM& vm, unsigned exitID, JITCode* jitCode, OSRExit& exit
         }
 
         if (exit.m_descriptor->m_valueProfile)
-            exit.m_descriptor->m_valueProfile.emitReportValue(jit, JSValueRegs(GPRInfo::regT0));
+            exit.m_descriptor->m_valueProfile.emitReportValue(jit, JSValueRegs(GPRInfo::regT0), GPRInfo::regT1);
     }
 
     // Materialize all objects. Don't materialize an object until all
index ab3e9c6..2589e2c 100644 (file)
@@ -422,7 +422,7 @@ String HeapSnapshotBuilder::json(Function<bool (const HeapSnapshotNode&)> allowN
 
         void* wrappedAddress = 0;
         unsigned labelIndex = 0;
-        if (!node.cell->isString() && !node.cell->isBigInt()) {
+        if (!node.cell->isString() && !node.cell->isHeapBigInt()) {
             Structure* structure = node.cell->structure(vm);
             if (!structure || !structure->globalObject())
                 flags |= static_cast<unsigned>(NodeFlags::Internal);
index 99fa40e..f27eef4 100644 (file)
@@ -29,7 +29,7 @@
 #include "JSCast.h"
 #include "MarkedBlock.h"
 #include "MarkedSpace.h"
-#include "Operations.h"
+#include "Scribble.h"
 #include "SuperSampler.h"
 #include "VM.h"
 
index 6cbaec1..032469f 100644 (file)
@@ -30,7 +30,7 @@
 #include "Heap.h"
 #include "IsoCellSetInlines.h"
 #include "JSCInlines.h"
-#include "Operations.h"
+#include "Scribble.h"
 #include "SubspaceInlines.h"
 
 namespace JSC {
index 81332d2..61f2e76 100644 (file)
@@ -187,8 +187,8 @@ void InspectorHeapAgent::getPreview(ErrorString& errorString, int heapObjectId,
     }
 
     // BigInt preview.
-    if (cell->isBigInt()) {
-        resultString = JSBigInt::tryGetString(vm, asBigInt(cell), 10);
+    if (cell->isHeapBigInt()) {
+        resultString = JSBigInt::tryGetString(vm, asHeapBigInt(cell), 10);
         return;
     }
 
index c4d9c72..ad4b215 100644 (file)
@@ -205,7 +205,7 @@ unsigned sizeOfVarargs(JSGlobalObject* globalObject, JSValue arguments, uint32_t
         break;
     case StringType:
     case SymbolType:
-    case BigIntType:
+    case HeapBigIntType:
         throwException(globalObject, scope, createInvalidFunctionApplyParameterError(globalObject,  arguments));
         return 0;
         
index e309ecf..c89f685 100644 (file)
@@ -688,7 +688,7 @@ void AssemblyHelpers::emitConvertValueToBoolean(VM& vm, JSValueRegs value, GPRRe
 {
     // Implements the following control flow structure:
     // if (value is cell) {
-    //     if (value is string or value is BigInt)
+    //     if (value is string or value is HeapBigInt)
     //         result = !!value->length
     //     else {
     //         do evil things for masquerades-as-undefined
@@ -698,6 +698,8 @@ void AssemblyHelpers::emitConvertValueToBoolean(VM& vm, JSValueRegs value, GPRRe
     //     result = !!unboxInt32(value)
     // } else if (value is number) {
     //     result = !!unboxDouble(value)
+    // } else if (value is BigInt32) {
+    //     result = !!unboxBigInt32(value)
     // } else {
     //     result = value == jsTrue
     // }
@@ -706,7 +708,7 @@ void AssemblyHelpers::emitConvertValueToBoolean(VM& vm, JSValueRegs value, GPRRe
 
     auto notCell = branchIfNotCell(value);
     auto isString = branchIfString(value.payloadGPR());
-    auto isBigInt = branchIfBigInt(value.payloadGPR());
+    auto isHeapBigInt = branchIfHeapBigInt(value.payloadGPR());
 
     if (shouldCheckMasqueradesAsUndefined) {
         ASSERT(scratchIfShouldCheckMasqueradesAsUndefined != InvalidGPRReg);
@@ -729,7 +731,7 @@ void AssemblyHelpers::emitConvertValueToBoolean(VM& vm, JSValueRegs value, GPRRe
     comparePtr(invert ? Equal : NotEqual, value.payloadGPR(), result, result);
     done.append(jump());
 
-    isBigInt.link(this);
+    isHeapBigInt.link(this);
     load32(Address(value.payloadGPR(), JSBigInt::offsetOfLength()), result);
     compare32(invert ? Equal : NotEqual, result, TrustedImm32(0), result);
     done.append(jump());
@@ -752,6 +754,15 @@ void AssemblyHelpers::emitConvertValueToBoolean(VM& vm, JSValueRegs value, GPRRe
     done.append(jump());
 
     notDouble.link(this);
+#if USE(BIGINT32)
+    auto isNotBigInt32 = branchIfNotBigInt32KnownNotNumber(value.gpr(), result);
+    move(value.gpr(), result);
+    urshift64(TrustedImm32(16), result);
+    compare32(invert ? Equal : NotEqual, result, TrustedImm32(0), result);
+    done.append(jump());
+
+    isNotBigInt32.link(this);
+#endif // USE(BIGINT32)
 #if USE(JSVALUE64)
     compare64(invert ? NotEqual : Equal, value.gpr(), TrustedImm32(JSValue::ValueTrue), result);
 #else
@@ -767,7 +778,7 @@ AssemblyHelpers::JumpList AssemblyHelpers::branchIfValue(VM& vm, JSValueRegs val
 {
     // Implements the following control flow structure:
     // if (value is cell) {
-    //     if (value is string or value is BigInt)
+    //     if (value is string or value is HeapBigInt)
     //         result = !!value->length
     //     else {
     //         do evil things for masquerades-as-undefined
@@ -777,6 +788,8 @@ AssemblyHelpers::JumpList AssemblyHelpers::branchIfValue(VM& vm, JSValueRegs val
     //     result = !!unboxInt32(value)
     // } else if (value is number) {
     //     result = !!unboxDouble(value)
+    // } else if (value is BigInt32) {
+    //     result = !!unboxBigInt32(value)
     // } else {
     //     result = value == jsTrue
     // }
@@ -786,7 +799,7 @@ AssemblyHelpers::JumpList AssemblyHelpers::branchIfValue(VM& vm, JSValueRegs val
 
     auto notCell = branchIfNotCell(value);
     auto isString = branchIfString(value.payloadGPR());
-    auto isBigInt = branchIfBigInt(value.payloadGPR());
+    auto isHeapBigInt = branchIfHeapBigInt(value.payloadGPR());
 
     if (shouldCheckMasqueradesAsUndefined) {
         ASSERT(scratchIfShouldCheckMasqueradesAsUndefined != InvalidGPRReg);
@@ -817,7 +830,7 @@ AssemblyHelpers::JumpList AssemblyHelpers::branchIfValue(VM& vm, JSValueRegs val
     truthy.append(branchPtr(invert ? Equal : NotEqual, value.payloadGPR(), TrustedImmPtr(jsEmptyString(vm))));
     done.append(jump());
 
-    isBigInt.link(this);
+    isHeapBigInt.link(this);
     truthy.append(branchTest32(invert ? Zero : NonZero, Address(value.payloadGPR(), JSBigInt::offsetOfLength())));
     done.append(jump());
 
@@ -842,6 +855,15 @@ AssemblyHelpers::JumpList AssemblyHelpers::branchIfValue(VM& vm, JSValueRegs val
     }
 
     notDouble.link(this);
+#if USE(BIGINT32)
+    auto isNotBigInt32 = branchIfNotBigInt32KnownNotNumber(value.gpr(), scratch);
+    move(value.gpr(), scratch);
+    urshift64(TrustedImm32(16), scratch);
+    truthy.append(branchTest32(invert ? Zero : NonZero, scratch));
+    done.append(jump());
+
+    isNotBigInt32.link(this);
+#endif // USE(BIGINT32)
 #if USE(JSVALUE64)
     truthy.append(branch64(invert ? NotEqual : Equal, value.gpr(), TrustedImm64(JSValue::encode(jsBoolean(true)))));
 #else
index 3d7ef7c..ed073e7 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011-2019 Apple Inc. All rights reserved.
+ * Copyright (C) 2011-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
@@ -929,7 +929,46 @@ public:
         return branchIfNotBoolean(regs.tagGPR(), tempGPR);
 #endif
     }
-    
+
+#if USE(BIGINT32)
+    Jump branchIfBigInt32(GPRReg gpr, GPRReg tempGPR, TagRegistersMode mode = HaveTagRegisters)
+    {
+        Jump number = branchIfNumber(gpr, mode);
+        Jump bigInt32 = branchIfBigInt32KnownNotNumber(gpr, tempGPR);
+        number.link(this);
+        return bigInt32;
+    }
+    JumpList branchIfNotBigInt32(GPRReg gpr, GPRReg tempGPR, TagRegistersMode mode = HaveTagRegisters)
+    {
+        JumpList result;
+        result.append(branchIfNumber(gpr, mode));
+        Jump bigInt32 = branchIfBigInt32KnownNotNumber(gpr, tempGPR);
+        result.append(jump());
+        bigInt32.link(this);
+        return result;
+    }
+
+    Jump branchIfBigInt32KnownNotNumber(GPRReg gpr, GPRReg tempGPR)
+    {
+        ASSERT(tempGPR != InvalidGPRReg);
+        move(gpr, tempGPR);
+        and64(TrustedImm32(JSValue::BigInt32Tag), tempGPR);
+        return branch64(Equal, tempGPR, TrustedImm32(JSValue::BigInt32Tag));
+    }
+    Jump branchIfNotBigInt32KnownNotNumber(JSValueRegs regs, GPRReg tempGPR)
+    {
+        return branchIfNotBigInt32KnownNotNumber(regs.gpr(), tempGPR);
+    }
+    Jump branchIfNotBigInt32KnownNotNumber(GPRReg gpr, GPRReg tempGPR)
+    {
+        ASSERT(tempGPR != InvalidGPRReg);
+        move(gpr, tempGPR);
+        and64(TrustedImm32(JSValue::BigInt32Tag), tempGPR);
+        return branch64(NotEqual, tempGPR, TrustedImm32(JSValue::BigInt32Tag));
+    }
+#endif // USE(BIGINT32)
+
+    // FIXME: rename these to make it clear that they require their input to be a cell.
     Jump branchIfObject(GPRReg cellGPR)
     {
         return branch8(
@@ -951,13 +990,14 @@ public:
     {
         return branch8(NotEqual, Address(cellGPR, JSCell::typeInfoTypeOffset()), TrustedImm32(type));
     }
-    
+
+    // FIXME: rename these to make it clear that they require their input to be a cell.
     Jump branchIfString(GPRReg cellGPR) { return branchIfType(cellGPR, StringType); }
     Jump branchIfNotString(GPRReg cellGPR) { return branchIfNotType(cellGPR, StringType); }
     Jump branchIfSymbol(GPRReg cellGPR) { return branchIfType(cellGPR, SymbolType); }
     Jump branchIfNotSymbol(GPRReg cellGPR) { return branchIfNotType(cellGPR, SymbolType); }
-    Jump branchIfBigInt(GPRReg cellGPR) { return branchIfType(cellGPR, BigIntType); }
-    Jump branchIfNotBigInt(GPRReg cellGPR) { return branchIfNotType(cellGPR, BigIntType); }
+    Jump branchIfHeapBigInt(GPRReg cellGPR) { return branchIfType(cellGPR, HeapBigIntType); }
+    Jump branchIfNotHeapBigInt(GPRReg cellGPR) { return branchIfNotType(cellGPR, HeapBigIntType); }
     Jump branchIfFunction(GPRReg cellGPR) { return branchIfType(cellGPR, JSFunctionType); }
     Jump branchIfNotFunction(GPRReg cellGPR) { return branchIfNotType(cellGPR, JSFunctionType); }
     
@@ -1356,6 +1396,19 @@ public:
         
         done.link(this);
     }
+#endif // USE(JSVALUE64)
+
+#if USE(BIGINT32)
+    void unboxBigInt32(GPRReg gpr)
+    {
+        urshift64(trustedImm32ForShift(Imm32(16)), gpr);
+    }
+
+    void boxBigInt32(GPRReg gpr)
+    {
+        lshift64(trustedImm32ForShift(Imm32(16)), gpr);
+        or64(TrustedImm32(JSValue::BigInt32Tag), gpr);
+    }
 #endif
 
 #if USE(JSVALUE32_64)
@@ -1715,7 +1768,7 @@ public:
         //         }
         //     } else if (is string) {
         //         return string
-        //     } else if (is bigint) {
+        //     } else if (is heapbigint) {
         //         return bigint
         //     } else {
         //         return symbol
@@ -1726,6 +1779,8 @@ public:
         //     return object
         // } else if (is boolean) {
         //     return boolean
+        // } else if (is bigint32) {
+        //     return bigint
         // } else {
         //     return undefined
         // }
@@ -1757,10 +1812,10 @@ public:
 
         notString.link(this);
 
-        Jump notBigInt = branchIfNotBigInt(cellGPR);
+        Jump notHeapBigInt = branchIfNotHeapBigInt(cellGPR);
         functor(TypeofType::BigInt, false);
 
-        notBigInt.link(this);
+        notHeapBigInt.link(this);
         functor(TypeofType::Symbol, false);
         
         notCell.link(this);
@@ -1776,6 +1831,12 @@ public:
         Jump notBoolean = branchIfNotBoolean(regs, tempGPR);
         functor(TypeofType::Boolean, false);
         notBoolean.link(this);
+
+#if USE(BIGINT32)
+        Jump notBigInt32 = branchIfNotBigInt32KnownNotNumber(regs, tempGPR);
+        functor(TypeofType::BigInt, false);
+        notBigInt32.link(this);
+#endif
         
         functor(TypeofType::Undefined, true);
     }
index 8b3a238..94b0e56 100644 (file)
@@ -370,6 +370,7 @@ void JIT::privateCompileMainPass()
         DEFINE_OP(op_is_undefined_or_null)
         DEFINE_OP(op_is_boolean)
         DEFINE_OP(op_is_number)
+        DEFINE_OP(op_is_big_int)
         DEFINE_OP(op_is_object)
         DEFINE_OP(op_is_cell_with_type)
         DEFINE_OP(op_jeq_null)
index 7587b01..34f3d50 100644 (file)
@@ -531,6 +531,11 @@ namespace JSC {
         void emit_op_is_undefined_or_null(const Instruction*);
         void emit_op_is_boolean(const Instruction*);
         void emit_op_is_number(const Instruction*);
+#if USE(BIGINT32)
+        void emit_op_is_big_int(const Instruction*);
+#else
+        NO_RETURN void emit_op_is_big_int(const Instruction*);
+#endif
         void emit_op_is_object(const Instruction*);
         void emit_op_is_cell_with_type(const Instruction*);
         void emit_op_jeq_null(const Instruction*);
index 778673e..67b00b0 100644 (file)
@@ -476,6 +476,7 @@ void JIT::emit_op_negate(const Instruction* currentInstruction)
     UnaryArithProfile* arithProfile = &currentInstruction->as<OpNegate>().metadata(m_codeBlock).m_arithProfile;
     JITNegIC* negateIC = m_codeBlock->addJITNegIC(arithProfile);
     m_instructionToMathIC.add(currentInstruction, negateIC);
+    // FIXME: it would be better to call those operationValueNegate, since the operand can be a BigInt
     emitMathICFast<OpNegate>(negateIC, currentInstruction, operationArithNegateProfiled, operationArithNegate);
 }
 
@@ -484,6 +485,7 @@ void JIT::emitSlow_op_negate(const Instruction* currentInstruction, Vector<SlowC
     linkAllSlowCases(iter);
 
     JITNegIC* negIC = bitwise_cast<JITNegIC*>(m_instructionToMathIC.get(currentInstruction));
+    // FIXME: it would be better to call those operationValueNegate, since the operand can be a BigInt
     emitMathICSlow<OpNegate>(negIC, currentInstruction, operationArithNegateProfiledOptimize, operationArithNegateProfiled, operationArithNegateOptimize);
 }
 
index fc94ca3..ceb600e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2019 Apple Inc. All rights reserved.
+ * Copyright (C) 2009-2020 Apple Inc. All rights reserved.
  * Copyright (C) 2010 Patrick Gansterer <paroga@paroga.com>
  *
  * Redistribution and use in source and binary forms, with or without
@@ -283,6 +283,37 @@ void JIT::emit_op_is_number(const Instruction* currentInstruction)
     emitPutVirtualRegister(dst);
 }
 
+#if USE(BIGINT32)
+void JIT::emit_op_is_big_int(const Instruction* currentInstruction)
+{
+    auto bytecode = currentInstruction->as<OpIsBigInt>();
+    VirtualRegister dst = bytecode.m_dst;
+    VirtualRegister value = bytecode.m_operand;
+
+    emitGetVirtualRegister(value, regT0);
+    Jump isCell = branchIfCell(regT0);
+
+    move(TrustedImm64(JSValue::BigInt32Mask), regT1);
+    and64(regT1, regT0);
+    compare64(Equal, regT0, TrustedImm32(JSValue::BigInt32Tag), regT0);
+    boxBoolean(regT0, JSValueRegs { regT0 });
+    Jump done = jump();
+
+    isCell.link(this);
+    compare8(Equal, Address(regT0, JSCell::typeInfoTypeOffset()), TrustedImm32(HeapBigIntType), regT0);
+    boxBoolean(regT0, JSValueRegs { regT0 });
+
+    done.link(this);
+    emitPutVirtualRegister(dst);
+}
+#else // if !USE(BIGINT32)
+NO_RETURN void JIT::emit_op_is_big_int(const Instruction*)
+{
+    // If we only have HeapBigInts, then we emit isCellWithType instead of isBigInt.
+    RELEASE_ASSERT_NOT_REACHED();
+}
+#endif
+
 void JIT::emit_op_is_cell_with_type(const Instruction* currentInstruction)
 {
     auto bytecode = currentInstruction->as<OpIsCellWithType>();
@@ -570,12 +601,53 @@ void JIT::compileOpStrictEq(const Instruction* currentInstruction, CompileOpStri
     VirtualRegister src2 = bytecode.m_rhs;
 
     emitGetVirtualRegisters(src1, regT0, src2, regT1);
-    
+
+#if USE(BIGINT32)
+    /* At a high level we do (assuming 'type' to be StrictEq):
+    If (left is Double || right is Double)
+        goto slowPath;
+    result = (left == right);
+    if (result)
+        goto done;
+    if (left is Cell || right is Cell)
+        goto slowPath;
+    done:
+    return result;
+    */
+
+    // This fragment implements (left is Double || right is Double), with a single branch instead of the 4 that would be naively required if we used branchIfInt32/branchIfNumber
+    // The trick is that if a JSValue is an Int32, then adding 1<<49 to it will make it overflow, leaving all high bits at 0
+    // If it is not a number at all, then 1<<49 will be its only high bit set
+    // Leaving only doubles above or equal 1<<50.
+    move(regT0, regT2);
+    move(regT1, regT3);
+    move(TrustedImm64(JSValue::LowestOfHighBits), regT5);
+    add64(regT5, regT2);
+    add64(regT5, regT3);
+    lshift64(TrustedImm32(1), regT5);
+    or64(regT2, regT3);
+    addSlowCase(branch64(AboveOrEqual, regT3, regT5));
+
+    compare64(Equal, regT0, regT1, regT5);
+    Jump done = branchTest64(NonZero, regT5);
+
+    move(regT0, regT2);
+    // Jump slow if at least one is a cell (to cover strings and BigInts).
+    and64(regT1, regT2);
+    // FIXME: we could do something more precise: unless there is a BigInt32, we only need to do the slow path if both are strings
+    addSlowCase(branchIfCell(regT2));
+
+    done.link(this);
+    if (type == CompileOpStrictEqType::NStrictEq)
+        xor64(TrustedImm64(1), regT5);
+    boxBoolean(regT5, JSValueRegs { regT5 });
+    emitPutVirtualRegister(dst, regT5);
+#else // if !USE(BIGINT32)
     // Jump slow if both are cells (to cover strings).
     move(regT0, regT2);
     or64(regT1, regT2);
     addSlowCase(branchIfCell(regT2));
-    
+
     // Jump slow if either is a double. First test if it's an integer, which is fine, and then test
     // if it's a double.
     Jump leftOK = branchIfInt32(regT0);
@@ -592,6 +664,7 @@ void JIT::compileOpStrictEq(const Instruction* currentInstruction, CompileOpStri
     boxBoolean(regT0, JSValueRegs { regT0 });
 
     emitPutVirtualRegister(dst);
+#endif
 }
 
 void JIT::emit_op_stricteq(const Instruction* currentInstruction)
@@ -614,6 +687,45 @@ void JIT::compileOpStrictEqJump(const Instruction* currentInstruction, CompileOp
 
     emitGetVirtualRegisters(src1, regT0, src2, regT1);
 
+#if USE(BIGINT32)
+    /* At a high level we do (assuming 'type' to be StrictEq):
+    If (left is Double || right is Double)
+       goto slowPath;
+    if (left == right)
+       goto taken;
+    if (left is Cell || right is Cell)
+       goto slowPath;
+    goto notTaken;
+    */
+
+    // This fragment implements (left is Double || right is Double), with a single branch instead of the 4 that would be naively required if we used branchIfInt32/branchIfNumber
+    // The trick is that if a JSValue is an Int32, then adding 1<<49 to it will make it overflow, leaving all high bits at 0
+    // If it is not a number at all, then 1<<49 will be its only high bit set
+    // Leaving only doubles above or equal 1<<50.
+    move(regT0, regT2);
+    move(regT1, regT3);
+    move(TrustedImm64(JSValue::LowestOfHighBits), regT5);
+    add64(regT5, regT2);
+    add64(regT5, regT3);
+    lshift64(TrustedImm32(1), regT5);
+    or64(regT2, regT3);
+    addSlowCase(branch64(AboveOrEqual, regT3, regT5));
+
+    Jump areEqual = branch64(Equal, regT0, regT1);
+    if (type == CompileOpStrictEqType::StrictEq)
+        addJump(areEqual, target);
+
+    move(regT0, regT2);
+    // Jump slow if at least one is a cell (to cover strings and BigInts).
+    and64(regT1, regT2);
+    // FIXME: we could do something more precise: unless there is a BigInt32, we only need to do the slow path if both are strings
+    addSlowCase(branchIfCell(regT2));
+
+    if (type == CompileOpStrictEqType::NStrictEq) {
+        addJump(jump(), target);
+        areEqual.link(this);
+    }
+#else // if !USE(BIGINT32)
     // Jump slow if both are cells (to cover strings).
     move(regT0, regT2);
     or64(regT1, regT2);
@@ -627,11 +739,11 @@ void JIT::compileOpStrictEqJump(const Instruction* currentInstruction, CompileOp
     Jump rightOK = branchIfInt32(regT1);
     addSlowCase(branchIfNumber(regT1));
     rightOK.link(this);
-
     if (type == CompileOpStrictEqType::StrictEq)
         addJump(branch64(Equal, regT1, regT0), target);
     else
         addJump(branch64(NotEqual, regT1, regT0), target);
+#endif
 }
 
 void JIT::emit_op_jstricteq(const Instruction* currentInstruction)
@@ -686,7 +798,7 @@ void JIT::emit_op_to_numeric(const Instruction* currentInstruction)
     emitGetVirtualRegister(srcVReg, regT0);
 
     Jump isNotCell = branchIfNotCell(regT0);
-    addSlowCase(branchIfNotBigInt(regT0));
+    addSlowCase(branchIfNotHeapBigInt(regT0));
     Jump isBigInt = jump();
 
     isNotCell.link(this);
index 1c30776..4840300 100644 (file)
@@ -288,6 +288,12 @@ void JIT::emit_op_is_number(const Instruction* currentInstruction)
     emitStoreBool(dst, regT0);
 }
 
+NO_RETURN void JIT::emit_op_is_big_int(const Instruction*)
+{
+    // We emit is_cell_with_type instead, since BigInt32 is not supported on 32-bit platforms.
+    RELEASE_ASSERT_NOT_REACHED();
+}
+
 void JIT::emit_op_is_cell_with_type(const Instruction* currentInstruction)
 {
     auto bytecode = currentInstruction->as<OpIsCellWithType>();
@@ -866,7 +872,7 @@ void JIT::emit_op_to_numeric(const Instruction* currentInstruction)
     emitLoad(src, regT1, regT0);
 
     Jump isNotCell = branchIfNotCell(regT1);
-    addSlowCase(branchIfNotBigInt(regT0));
+    addSlowCase(branchIfNotHeapBigInt(regT0));
     Jump isBigInt = jump();
 
     isNotCell.link(this);
index a5e2a24..2c85c52 100644 (file)
@@ -1325,6 +1325,19 @@ size_t JIT_OPERATION operationCompareStrictEq(JSGlobalObject* globalObject, Enco
     return JSValue::strictEqual(globalObject, src1, src2);
 }
 
+#if USE(BIGINT32)
+size_t JIT_OPERATION operationCompareEqHeapBigIntToInt32(JSGlobalObject* globalObject, JSCell* heapBigInt, int32_t smallInt)
+{
+    VM& vm = globalObject->vm();
+    CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
+    JITOperationPrologueCallFrameTracer tracer(vm, callFrame);
+
+    ASSERT(heapBigInt->isHeapBigInt());
+
+    return static_cast<JSBigInt*>(heapBigInt)->equalsToInt32(smallInt);
+}
+#endif
+
 EncodedJSValue JIT_OPERATION operationNewArrayWithProfile(JSGlobalObject* globalObject, ArrayAllocationProfile* profile, const JSValue* values, int size)
 {
     VM& vm = globalObject->vm();
@@ -2848,6 +2861,7 @@ EncodedJSValue JIT_OPERATION operationValueMulProfiledNoOptimize(JSGlobalObject*
     return profiledMul(globalObject, encodedOp1, encodedOp2, *arithProfile);
 }
 
+// FIXME: it would be better to call those operationValueNegate, since the operand can be a BigInt
 EncodedJSValue JIT_OPERATION operationArithNegate(JSGlobalObject* globalObject, EncodedJSValue encodedOperand)
 {
     VM& vm = globalObject->vm();
@@ -2860,8 +2874,16 @@ EncodedJSValue JIT_OPERATION operationArithNegate(JSGlobalObject* globalObject,
     JSValue primValue = operand.toPrimitive(globalObject, PreferNumber);
     RETURN_IF_EXCEPTION(scope, encodedJSValue());
 
-    if (primValue.isBigInt())
-        return JSValue::encode(JSBigInt::unaryMinus(vm, asBigInt(primValue)));
+#if USE(BIGINT32)
+    if (primValue.isBigInt32()) {
+        int32_t value = primValue.bigInt32AsInt32();
+        if (value != INT_MIN)
+            return JSValue::encode(JSValue(JSValue::JSBigInt32, -value));
+        primValue = JSBigInt::createFrom(vm, value);
+    }
+#endif
+    if (primValue.isHeapBigInt())
+        return JSValue::encode(JSBigInt::unaryMinus(vm, primValue.asHeapBigInt()));
 
     double number = primValue.toNumber(globalObject);
     RETURN_IF_EXCEPTION(scope, encodedJSValue());
@@ -2869,6 +2891,7 @@ EncodedJSValue JIT_OPERATION operationArithNegate(JSGlobalObject* globalObject,
 
 }
 
+// FIXME: it would be better to call those operationValueNegate, since the operand can be a BigInt
 EncodedJSValue JIT_OPERATION operationArithNegateProfiled(JSGlobalObject* globalObject, EncodedJSValue encodedOperand, UnaryArithProfile* arithProfile)
 {
     ASSERT(arithProfile);
@@ -2883,10 +2906,20 @@ EncodedJSValue JIT_OPERATION operationArithNegateProfiled(JSGlobalObject* global
     JSValue primValue = operand.toPrimitive(globalObject);
     RETURN_IF_EXCEPTION(scope, encodedJSValue());
 
-    if (primValue.isBigInt()) {
-        JSBigInt* result = JSBigInt::unaryMinus(vm, asBigInt(primValue));
+#if USE(BIGINT32)
+    if (primValue.isBigInt32()) {
+        int32_t value = primValue.bigInt32AsInt32();
+        if (value != INT_MIN) {
+            auto result = JSValue(JSValue::JSBigInt32, -value);
+            arithProfile->observeResult(result);
+            return JSValue::encode(result);
+        }
+        primValue = JSBigInt::createFrom(vm, value);
+    }
+#endif
+    if (primValue.isHeapBigInt()) {
+        JSBigInt* result = JSBigInt::unaryMinus(vm, primValue.asHeapBigInt());
         arithProfile->observeResult(result);
-
         return JSValue::encode(result);
     }
 
@@ -2897,6 +2930,7 @@ EncodedJSValue JIT_OPERATION operationArithNegateProfiled(JSGlobalObject* global
     return JSValue::encode(result);
 }
 
+// FIXME: it would be better to call those operationValueNegate, since the operand can be a BigInt
 EncodedJSValue JIT_OPERATION operationArithNegateProfiledOptimize(JSGlobalObject* globalObject, EncodedJSValue encodedOperand, JITNegIC* negIC)
 {
     VM& vm = globalObject->vm();
@@ -2917,9 +2951,20 @@ EncodedJSValue JIT_OPERATION operationArithNegateProfiledOptimize(JSGlobalObject
     
     JSValue primValue = operand.toPrimitive(globalObject);
     RETURN_IF_EXCEPTION(scope, encodedJSValue());
-    
-    if (primValue.isBigInt()) {
-        JSBigInt* result = JSBigInt::unaryMinus(vm, asBigInt(primValue));
+
+#if USE(BIGINT32)
+    if (primValue.isBigInt32()) {
+        int32_t value = primValue.bigInt32AsInt32();
+        if (value != INT_MIN) {
+            auto result = JSValue(JSValue::JSBigInt32, -value);
+            arithProfile->observeResult(result);
+            return JSValue::encode(result);
+        }
+        primValue = JSBigInt::createFrom(vm, value);
+    }
+#endif
+    if (primValue.isHeapBigInt()) {
+        JSBigInt* result = JSBigInt::unaryMinus(vm, primValue.asHeapBigInt());
         arithProfile->observeResult(result);
         return JSValue::encode(result);
     }
@@ -2931,6 +2976,7 @@ EncodedJSValue JIT_OPERATION operationArithNegateProfiledOptimize(JSGlobalObject
     return JSValue::encode(result);
 }
 
+// FIXME: it would be better to call those operationValueNegate, since the operand can be a BigInt
 EncodedJSValue JIT_OPERATION operationArithNegateOptimize(JSGlobalObject* globalObject, EncodedJSValue encodedOperand, JITNegIC* negIC)
 {
     VM& vm = globalObject->vm();
@@ -2950,9 +2996,18 @@ EncodedJSValue JIT_OPERATION operationArithNegateOptimize(JSGlobalObject* global
 
     JSValue primValue = operand.toPrimitive(globalObject);
     RETURN_IF_EXCEPTION(scope, encodedJSValue());
-    
-    if (primValue.isBigInt())
-        return JSValue::encode(JSBigInt::unaryMinus(vm, asBigInt(primValue)));
+
+#if USE(BIGINT32)
+    // FIXME: why does this function profile the argument but not the result?
+    if (primValue.isBigInt32()) {
+        int32_t value = primValue.bigInt32AsInt32();
+        if (value != INT_MIN)
+            return JSValue::encode(JSValue(JSValue::JSBigInt32, -value));
+        primValue = JSBigInt::createFrom(vm, value);
+    }
+#endif
+    if (primValue.isHeapBigInt())
+        return JSValue::encode(JSBigInt::unaryMinus(vm, primValue.asHeapBigInt()));
 
     double number = primValue.toNumber(globalObject);
     RETURN_IF_EXCEPTION(scope, encodedJSValue());
index 9fcc41d..9ea3cbd 100644 (file)
@@ -207,6 +207,7 @@ size_t JIT_OPERATION operationCompareEq(JSGlobalObject*, EncodedJSValue, Encoded
 size_t JIT_OPERATION operationCompareStrictEq(JSGlobalObject*, EncodedJSValue, EncodedJSValue) WTF_INTERNAL;
 #if USE(JSVALUE64)
 EncodedJSValue JIT_OPERATION operationCompareStringEq(JSGlobalObject*, JSCell* left, JSCell* right) WTF_INTERNAL;
+size_t JIT_OPERATION operationCompareEqHeapBigIntToInt32(JSGlobalObject*, JSCell* heapBigInt, int32_t smallInt) WTF_INTERNAL;
 #else
 size_t JIT_OPERATION operationCompareStringEq(JSGlobalObject*, JSCell* left, JSCell* right) WTF_INTERNAL;
 #endif
index 52435a6..2a2bb99 100644 (file)
 #define OFFLINE_ASM_JSVALUE64 0
 #endif
 
+#if USE(BIGINT32)
+#define OFFLINE_ASM_BIGINT32 1
+#else
+#define OFFLINE_ASM_BIGINT32 0
+#endif
+
 #if CPU(ADDRESS64)
 #define OFFLINE_ASM_ADDRESS64 1
 #else
index f9a34da..06e155e 100644 (file)
@@ -196,6 +196,11 @@ if JSVALUE64
     const ValueNull       = constexpr JSValue::ValueNull
     const TagNumber       = constexpr JSValue::NumberTag
     const NotCellMask     = constexpr JSValue::NotCellMask
+    if BIGINT32
+        const TagBigInt32 = constexpr JSValue::BigInt32Tag
+        const MaskBigInt32 = constexpr JSValue::BigInt32Mask
+    end
+    const LowestOfHighBits = constexpr JSValue::LowestOfHighBits
 else
     const Int32Tag = constexpr JSValue::Int32Tag
     const BooleanTag = constexpr JSValue::BooleanTag
@@ -521,6 +526,7 @@ const JSFunctionType = constexpr JSFunctionType
 const ArrayType = constexpr ArrayType
 const DerivedArrayType = constexpr DerivedArrayType
 const ProxyObjectType = constexpr ProxyObjectType
+const HeapBigIntType = constexpr HeapBigIntType
 
 # The typed array types need to be numbered in a particular order because of the manually written
 # switch statement in get_by_val and put_by_val.
index 74c480d..9726699 100644 (file)
@@ -1307,6 +1307,12 @@ llintOpWithReturn(op_is_number, OpIsNumber, macro (size, get, dispatch, return)
 end)
 
 
+# On 32-bit platforms BIGINT32 is not supported, so we generate op_is_cell_with_type instead of op_is_big_int
+llintOp(op_is_big_int, OpIsBigInt, macro(unused, unused, unused)
+    notSupported()
+end)
+
+
 llintOpWithReturn(op_is_cell_with_type, OpIsCellWithType, macro (size, get, dispatch, return)
     get(m_operand, t1)
     loadConstantOrVariable(size, t1, t0, t3)