Skip to content

Commit

Permalink
[JSC] Implement @toIntegerOrInfinity / @toLength in C++
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=266913
rdar://120211573

Reviewed by Michael Saboff.

This patch implements @toIntegerOrInfinity / @toLength in C++. And we introduce ToIntegerOrInfinity / ToLength DFG nodes.

1. ToIntegerOrInfinity will be constant-folded into Identity when the input is Int32Use.
2. ToLength gets super fast path for Int32Use.

* Source/JavaScriptCore/builtins/BuiltinNames.h:
* Source/JavaScriptCore/builtins/GlobalOperations.js:
(linkTimeConstant.toIntegerOrInfinity): Deleted.
* Source/JavaScriptCore/bytecode/LinkTimeConstant.h:
* Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::handleIntrinsicCall):
* Source/JavaScriptCore/dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* Source/JavaScriptCore/dfg/DFGDoesGC.cpp:
(JSC::DFG::doesGC):
* Source/JavaScriptCore/dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* Source/JavaScriptCore/dfg/DFGNode.h:
(JSC::DFG::Node::hasHeapPrediction):
* Source/JavaScriptCore/dfg/DFGNodeType.h:
* Source/JavaScriptCore/dfg/DFGOperations.cpp:
(JSC::DFG::JSC_DEFINE_JIT_OPERATION):
* Source/JavaScriptCore/dfg/DFGOperations.h:
* Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp:
* Source/JavaScriptCore/dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp:
* Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h:
* Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* Source/JavaScriptCore/ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileNode):
(JSC::FTL::DFG::LowerDFGToB3::compileCompareStrictEq):
* Source/JavaScriptCore/jit/ThunkGenerators.cpp:
(JSC::toIntegerOrInfinityThunkGenerator):
* Source/JavaScriptCore/jit/ThunkGenerators.h:
* Source/JavaScriptCore/runtime/Intrinsic.h:
* Source/JavaScriptCore/runtime/JSGlobalObject.cpp:
(JSC::JSGlobalObject::init):
* Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp:
(JSC::JSC_DEFINE_HOST_FUNCTION):
* Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.h:
* Source/JavaScriptCore/runtime/VM.cpp:
(JSC::thunkGeneratorForIntrinsic):

Canonical link: https://commits.webkit.org/272574@main
  • Loading branch information
Constellation committed Jan 2, 2024
1 parent d32c300 commit 17ba660
Show file tree
Hide file tree
Showing 32 changed files with 448 additions and 30 deletions.
12 changes: 12 additions & 0 deletions Source/JavaScriptCore/assembler/MacroAssemblerARMv7.h
Original file line number Diff line number Diff line change
Expand Up @@ -2870,6 +2870,18 @@ class MacroAssemblerARMv7 : public AbstractMacroAssembler<Assembler> {
m_assembler.mov(dest, ARMThumbImmediate::makeUInt16(0));
}

void moveConditionally32(RelationalCondition cond, RegisterID left, TrustedImm32 right, RegisterID thenCase, RegisterID elseCase, RegisterID dest)
{
auto passCase = branch32(cond, left, right);
move(elseCase, dest);
auto done = jump();

passCase.link(this);
move(thenCase, dest);

done.link(this);
}

ALWAYS_INLINE DataLabel32 moveWithPatch(TrustedImm32 imm, RegisterID dst)
{
padBeforePatch();
Expand Down
3 changes: 2 additions & 1 deletion Source/JavaScriptCore/builtins/BuiltinNames.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ namespace JSC {
macro(ShadowRealm) \
macro(RegExp) \
macro(min) \
macro(trunc) \
macro(create) \
macro(defineProperty) \
macro(defaultPromiseThen) \
Expand Down Expand Up @@ -118,6 +117,8 @@ namespace JSC {
macro(nextMethod) \
macro(asyncGeneratorQueueItemNext) \
macro(this) \
macro(toIntegerOrInfinity) \
macro(toLength) \
macro(importMapStatus) \
macro(importInRealm) \
macro(evalInRealm) \
Expand Down
23 changes: 0 additions & 23 deletions Source/JavaScriptCore/builtins/GlobalOperations.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,29 +26,6 @@

// @internal

@linkTimeConstant
function toIntegerOrInfinity(target)
{
"use strict";

var numberValue = +target;

// isNaN(numberValue) or 0
if (numberValue !== numberValue || !numberValue)
return 0;
return @trunc(numberValue);
}

@linkTimeConstant
function toLength(target)
{
"use strict";

var length = @toIntegerOrInfinity(target);
// originally Math.min(Math.max(length, 0), maxSafeInteger));
return +(length > 0 ? (length < @MAX_SAFE_INTEGER ? length : @MAX_SAFE_INTEGER) : 0);
}

@linkTimeConstant
@getter
@overriddenName="get [Symbol.species]"
Expand Down
3 changes: 2 additions & 1 deletion Source/JavaScriptCore/bytecode/LinkTimeConstant.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ class JSGlobalObject;
v(typedArrayLength, nullptr) \
v(typedArrayContentType, nullptr) \
v(typedArrayGetOriginalConstructor, nullptr) \
v(toIntegerOrInfinity, nullptr) \
v(toLength, nullptr) \
v(isTypedArrayView, nullptr) \
v(isSharedTypedArrayView, nullptr) \
v(isResizableOrGrowableSharedTypedArrayView, nullptr) \
Expand All @@ -65,7 +67,6 @@ class JSGlobalObject;
v(BuiltinDescribe, nullptr) \
v(RegExp, nullptr) \
v(min, nullptr) \
v(trunc, nullptr) \
v(Promise, nullptr) \
v(InternalPromise, nullptr) \
v(defaultPromiseThen, nullptr) \
Expand Down
2 changes: 1 addition & 1 deletion Source/JavaScriptCore/bytecode/PutByVariant.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ class PutByVariant {

const StructureSet& structure() const
{
ASSERT(kind() == Replace || kind() == Setter || kind() == Proxy);
ASSERT(kind() == Replace || kind() == Setter || kind() == Proxy || kind() == CustomAccessorSetter);
return m_oldStructure;
}

Expand Down
38 changes: 38 additions & 0 deletions Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
Original file line number Diff line number Diff line change
Expand Up @@ -5023,6 +5023,44 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
break;
}

case ToIntegerOrInfinity: {
AbstractValue child = forNode(node->child1());
if (JSValue value = child.value(); value && value.isNumber()) {
if (node->child1().useKind() == UntypedUse)
didFoldClobberWorld();
double d = value.asNumber();
setConstant(node, jsNumber(trunc(std::isnan(d) ? 0.0 : d + 0.0)));
break;
}
if (node->child1().useKind() == UntypedUse)
clobberWorld();
setNonCellTypeForNode(node, SpecBytecodeNumber);
break;
}

case ToLength: {
AbstractValue child = forNode(node->child1());
if (JSValue value = child.value(); value && value.isNumber()) {
if (node->child1().useKind() == UntypedUse)
didFoldClobberWorld();
double d = value.asNumber();
d = trunc(std::isnan(d) ? 0.0 : d + 0.0);
if (d <= 0)
d = 0.0;
else
d = std::min(d, maxSafeInteger());
setConstant(node, jsNumber(d));
break;
}
if (node->child1().useKind() == UntypedUse)
clobberWorld();
if (node->child1().useKind() == Int32Use)
setNonCellTypeForNode(node, SpecInt32Only);
else
setNonCellTypeForNode(node, SpecBytecodeNumber);
break;
}

case CreateRest:
if (!m_graph.isWatchingHavingABadTimeWatchpoint(node)) {
// This means we're already having a bad time.
Expand Down
24 changes: 24 additions & 0 deletions Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3300,6 +3300,30 @@ auto ByteCodeParser::handleIntrinsicCall(Node* callee, Operand resultOperand, Ca
return CallOptimizationResult::Inlined;
}

case ToIntegerOrInfinityIntrinsic: {
if (argumentCountIncludingThis == 1) {
insertChecks();
setResult(jsConstant(jsNumber(0)));
return CallOptimizationResult::Inlined;
}
insertChecks();
VirtualRegister operand = virtualRegisterForArgumentIncludingThis(1, registerOffset);
setResult(addToGraph(ToIntegerOrInfinity, OpInfo(0), OpInfo(prediction), get(operand)));
return CallOptimizationResult::Inlined;
}

case ToLengthIntrinsic: {
if (argumentCountIncludingThis == 1) {
insertChecks();
setResult(jsConstant(jsNumber(0)));
return CallOptimizationResult::Inlined;
}
insertChecks();
VirtualRegister operand = virtualRegisterForArgumentIncludingThis(1, registerOffset);
setResult(addToGraph(ToLength, OpInfo(0), OpInfo(prediction), get(operand)));
return CallOptimizationResult::Inlined;
}

case RandomIntrinsic: {
insertChecks();
setResult(addToGraph(ArithRandom));
Expand Down
9 changes: 9 additions & 0 deletions Source/JavaScriptCore/dfg/DFGClobberize.h
Original file line number Diff line number Diff line change
Expand Up @@ -1293,6 +1293,15 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
clobberTop();
return;

case ToIntegerOrInfinity:
case ToLength: {
if (node->child1().useKind() == UntypedUse)
clobberTop();
else
def(PureValue(node));
return;
}

case OverridesHasInstance:
read(JSCell_typeInfoFlags);
def(HeapLocation(OverridesHasInstanceLoc, JSCell_typeInfoFlags, node->child1()), LazyNode(node));
Expand Down
4 changes: 4 additions & 0 deletions Source/JavaScriptCore/dfg/DFGDoesGC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,10 @@ bool doesGC(Graph& graph, Node* node)
#endif // not ASSERT_ENABLED
return true;

case ToIntegerOrInfinity:
case ToLength:
return node->child1().useKind() == UntypedUse;

case GlobalIsNaN:
return node->child1().useKind() != DoubleRepUse;

Expand Down
31 changes: 31 additions & 0 deletions Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2911,6 +2911,37 @@ class FixupPhase : public Phase {
break;
}

case ToIntegerOrInfinity: {
if (node->child1()->shouldSpeculateInt32()) {
fixIntOrBooleanEdge(node->child1());
node->convertToIdentity();
break;
}

if (node->child1()->shouldSpeculateNumber()) {
fixEdge<DoubleRepUse>(node->child1());
node->clearFlags(NodeMustGenerate);
break;
}
break;
}

case ToLength: {
if (node->child1()->shouldSpeculateInt32()) {
fixIntOrBooleanEdge(node->child1());
node->clearFlags(NodeMustGenerate);
node->setResult(NodeResultInt32);
break;
}

if (node->child1()->shouldSpeculateNumber()) {
fixEdge<DoubleRepUse>(node->child1());
node->clearFlags(NodeMustGenerate);
break;
}
break;
}

case IdentityWithProfile: {
node->convertToIdentity();
break;
Expand Down
2 changes: 2 additions & 0 deletions Source/JavaScriptCore/dfg/DFGNode.h
Original file line number Diff line number Diff line change
Expand Up @@ -1996,6 +1996,8 @@ struct Node {
case CallDOMGetter:
case CallDOM:
case ParseInt:
case ToIntegerOrInfinity:
case ToLength:
case AtomicsAdd:
case AtomicsAnd:
case AtomicsCompareExchange:
Expand Down
2 changes: 2 additions & 0 deletions Source/JavaScriptCore/dfg/DFGNodeType.h
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,8 @@ namespace JSC { namespace DFG {
macro(ToNumber, NodeResultJS | NodeMustGenerate) \
macro(ToNumeric, NodeResultJS | NodeMustGenerate) \
macro(ToObject, NodeResultJS | NodeMustGenerate) \
macro(ToIntegerOrInfinity, NodeResultJS | NodeMustGenerate) \
macro(ToLength, NodeResultJS | NodeMustGenerate) \
macro(CallObjectConstructor, NodeResultJS) \
macro(CallStringConstructor, NodeResultJS | NodeMustGenerate) \
macro(CallNumberConstructor, NodeResultJS | NodeMustGenerate) \
Expand Down
31 changes: 31 additions & 0 deletions Source/JavaScriptCore/dfg/DFGOperations.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3380,6 +3380,37 @@ JSC_DEFINE_JIT_OPERATION(operationIsNaN, UCPUStrictInt32, (JSGlobalObject* globa
return toUCPUStrictInt32(std::isnan(argument.toNumber(globalObject)));
}

JSC_DEFINE_JIT_OPERATION(operationToIntegerOrInfinityDouble, EncodedJSValue, (double d))
{
return JSValue::encode(jsNumber(trunc(std::isnan(d) ? 0.0 : d + 0.0)));
}

JSC_DEFINE_JIT_OPERATION(operationToIntegerOrInfinityUntyped, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue encodedValue))
{
VM& vm = globalObject->vm();
CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
JITOperationPrologueCallFrameTracer tracer(vm, callFrame);
JSValue value = JSValue::decode(encodedValue);
return JSValue::encode(jsNumber(value.toIntegerOrInfinity(globalObject)));
}

JSC_DEFINE_JIT_OPERATION(operationToLengthDouble, EncodedJSValue, (double argument))
{
double d = trunc(std::isnan(argument) ? 0.0 : argument + 0.0);
if (d <= 0)
return JSValue::encode(jsNumber(0));
return JSValue::encode(jsNumber(std::min(d, maxSafeInteger())));
}

JSC_DEFINE_JIT_OPERATION(operationToLengthUntyped, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue encodedValue))
{
VM& vm = globalObject->vm();
CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
JITOperationPrologueCallFrameTracer tracer(vm, callFrame);
JSValue value = JSValue::decode(encodedValue);
return JSValue::encode(jsNumber(value.toLength(globalObject)));
}

static ALWAYS_INLINE UCPUStrictInt32 arrayIndexOfString(JSGlobalObject* globalObject, Butterfly* butterfly, JSString* searchElement, int32_t index)
{
VM& vm = globalObject->vm();
Expand Down
4 changes: 4 additions & 0 deletions Source/JavaScriptCore/dfg/DFGOperations.h
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,10 @@ JSC_DECLARE_JIT_OPERATION(operationPutDynamicVarSloppy, void, (JSGlobalObject*,
JSC_DECLARE_JIT_OPERATION(operationNumberIsInteger, size_t, (JSGlobalObject*, EncodedJSValue));
JSC_DECLARE_JIT_OPERATION(operationNumberIsNaN, UCPUStrictInt32, (EncodedJSValue));
JSC_DECLARE_JIT_OPERATION(operationIsNaN, UCPUStrictInt32, (JSGlobalObject*, EncodedJSValue));
JSC_DECLARE_JIT_OPERATION(operationToIntegerOrInfinityDouble, EncodedJSValue, (double));
JSC_DECLARE_JIT_OPERATION(operationToIntegerOrInfinityUntyped, EncodedJSValue, (JSGlobalObject*, EncodedJSValue));
JSC_DECLARE_JIT_OPERATION(operationToLengthDouble, EncodedJSValue, (double));
JSC_DECLARE_JIT_OPERATION(operationToLengthUntyped, EncodedJSValue, (JSGlobalObject*, EncodedJSValue));

JSC_DECLARE_JIT_OPERATION(operationNewRawObject, char*, (VM*, Structure*, int32_t, Butterfly*));
JSC_DECLARE_JIT_OPERATION(operationNewObjectWithButterfly, JSCell*, (VM*, Structure*, Butterfly*));
Expand Down
10 changes: 10 additions & 0 deletions Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1415,6 +1415,16 @@ class PredictionPropagationPhase : public Phase {
break;
}

case ToIntegerOrInfinity:
case ToLength: {
// We expect this node to almost always produce an int32. However,
// it's possible it produces Infinity or integers out of int32 range. We
// rely on the heap prediction since the @toIntegerOrInfinity() call profiled
// its result.
setPrediction(m_currentNode->getHeapPrediction());
break;
}

case IdentityWithProfile: {
setPrediction(m_currentNode->getForcedPrediction());
break;
Expand Down
2 changes: 2 additions & 0 deletions Source/JavaScriptCore/dfg/DFGSafeToExecute.h
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,8 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node, bool igno
case SameValue:
case CheckTypeInfoFlags:
case ParseInt:
case ToIntegerOrInfinity:
case ToLength:
case OverridesHasInstance:
case IsEmpty:
case TypeOfIsUndefined:
Expand Down
Loading

0 comments on commit 17ba660

Please sign in to comment.