commit 62273e72609efc2f03a1a562befb22e93b3c329f
parent 2cfc9651020f7415b97d36eda8a4d26210b7e507
Author: André Bargull <andre.bargull@gmail.com>
Date: Mon, 20 Oct 2025 12:27:44 +0000
Bug 1991402 - Part 18: Make ArrayJoinResult a shared CacheIR instruction. r=jandem
Use `AutoCallVM` to move the implementation into `CacheIRCompiler`.
Differential Revision: https://phabricator.services.mozilla.com/D266774
Diffstat:
5 files changed, 74 insertions(+), 73 deletions(-)
diff --git a/js/src/jit-test/tests/cacheir/inlinable-native-accessor-6.js b/js/src/jit-test/tests/cacheir/inlinable-native-accessor-6.js
@@ -56,3 +56,13 @@ function testArraySliceArguments() {
}
}
testArraySliceArguments();
+
+function testArrayJoin() {
+ Object.defineProperty(Array.prototype, "joined", {get: Array.prototype.join});
+
+ for (var i = 0; i < 100; ++i) {
+ assertEq([].joined, "");
+ assertEq(["a"].joined, "a");
+ }
+}
+testArrayJoin();
diff --git a/js/src/jit/BaselineCacheIRCompiler.cpp b/js/src/jit/BaselineCacheIRCompiler.cpp
@@ -1040,73 +1040,6 @@ bool BaselineCacheIRCompiler::emitAllocateAndStoreDynamicSlot(
newShapeOffset, mozilla::Some(numNewSlotsOffset), preserveWrapper);
}
-bool BaselineCacheIRCompiler::emitArrayJoinResult(ObjOperandId objId,
- StringOperandId sepId) {
- JitSpew(JitSpew_Codegen, "%s", __FUNCTION__);
-
- AutoOutputRegister output(*this);
- Register obj = allocator.useRegister(masm, objId);
- Register sep = allocator.useRegister(masm, sepId);
- AutoScratchRegisterMaybeOutput scratch(allocator, masm, output);
-
- allocator.discardStack(masm);
-
- // Load obj->elements in scratch.
- masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), scratch);
- Address lengthAddr(scratch, ObjectElements::offsetOfLength());
-
- // If array length is 0, return empty string.
- Label finished;
-
- {
- Label arrayNotEmpty;
- masm.branch32(Assembler::NotEqual, lengthAddr, Imm32(0), &arrayNotEmpty);
- masm.movePtr(ImmGCPtr(cx_->names().empty_), scratch);
- masm.tagValue(JSVAL_TYPE_STRING, scratch, output.valueReg());
- masm.jump(&finished);
- masm.bind(&arrayNotEmpty);
- }
-
- Label vmCall;
-
- // Otherwise, handle array length 1 case.
- masm.branch32(Assembler::NotEqual, lengthAddr, Imm32(1), &vmCall);
-
- // But only if initializedLength is also 1.
- Address initLength(scratch, ObjectElements::offsetOfInitializedLength());
- masm.branch32(Assembler::NotEqual, initLength, Imm32(1), &vmCall);
-
- // And only if elem0 is a string.
- Address elementAddr(scratch, 0);
- masm.branchTestString(Assembler::NotEqual, elementAddr, &vmCall);
-
- // Store the value.
- masm.loadValue(elementAddr, output.valueReg());
- masm.jump(&finished);
-
- // Otherwise call into the VM.
- {
- masm.bind(&vmCall);
-
- AutoStubFrame stubFrame(*this);
- stubFrame.enter(masm, scratch);
-
- masm.Push(sep);
- masm.Push(obj);
-
- using Fn = JSString* (*)(JSContext*, HandleObject, HandleString);
- callVM<Fn, jit::ArrayJoin>(masm);
-
- stubFrame.leave(masm);
-
- masm.tagValue(JSVAL_TYPE_STRING, ReturnReg, output.valueReg());
- }
-
- masm.bind(&finished);
-
- return true;
-}
-
bool BaselineCacheIRCompiler::emitIsArrayResult(ValOperandId inputId) {
JitSpew(JitSpew_Codegen, "%s", __FUNCTION__);
diff --git a/js/src/jit/CacheIRCompiler.cpp b/js/src/jit/CacheIRCompiler.cpp
@@ -7390,6 +7390,69 @@ bool CacheIRCompiler::emitArgumentsSliceResult(uint32_t templateObjectOffset,
return true;
}
+bool CacheIRCompiler::emitArrayJoinResult(ObjOperandId objId,
+ StringOperandId sepId) {
+ JitSpew(JitSpew_Codegen, "%s", __FUNCTION__);
+
+ AutoCallVM callvm(masm, this, allocator);
+
+ Register obj = allocator.useRegister(masm, objId);
+ Register sep = allocator.useRegister(masm, sepId);
+ AutoScratchRegisterMaybeOutput scratch(allocator, masm, callvm.output());
+
+ // Discard the stack to ensure it's balanced when we skip the vm-call.
+ allocator.discardStack(masm);
+
+ // Load obj->elements in scratch.
+ masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), scratch);
+ Address lengthAddr(scratch, ObjectElements::offsetOfLength());
+
+ // If array length is 0, return empty string.
+ Label finished;
+
+ {
+ Label arrayNotEmpty;
+ masm.branch32(Assembler::NotEqual, lengthAddr, Imm32(0), &arrayNotEmpty);
+ masm.movePtr(ImmGCPtr(cx_->names().empty_), scratch);
+ masm.tagValue(JSVAL_TYPE_STRING, scratch, callvm.outputValueReg());
+ masm.jump(&finished);
+ masm.bind(&arrayNotEmpty);
+ }
+
+ Label vmCall;
+
+ // Otherwise, handle array length 1 case.
+ masm.branch32(Assembler::NotEqual, lengthAddr, Imm32(1), &vmCall);
+
+ // But only if initializedLength is also 1.
+ Address initLength(scratch, ObjectElements::offsetOfInitializedLength());
+ masm.branch32(Assembler::NotEqual, initLength, Imm32(1), &vmCall);
+
+ // And only if elem0 is a string.
+ Address elementAddr(scratch, 0);
+ masm.branchTestString(Assembler::NotEqual, elementAddr, &vmCall);
+
+ // Store the value.
+ masm.loadValue(elementAddr, callvm.outputValueReg());
+ masm.jump(&finished);
+
+ // Otherwise call into the VM.
+ {
+ masm.bind(&vmCall);
+
+ callvm.prepare();
+
+ masm.Push(sep);
+ masm.Push(obj);
+
+ using Fn = JSString* (*)(JSContext*, HandleObject, HandleString);
+ callvm.call<Fn, jit::ArrayJoin>();
+ }
+
+ masm.bind(&finished);
+ return true;
+}
+
bool CacheIRCompiler::emitStoreTypedArrayElement(ObjOperandId objId,
Scalar::Type elementType,
IntPtrOperandId indexId,
diff --git a/js/src/jit/CacheIROps.yaml b/js/src/jit/CacheIROps.yaml
@@ -1175,7 +1175,7 @@
rhs: ValId
- name: ArrayJoinResult
- shared: false
+ shared: true
transpile: true
cost_estimate: 5
args:
diff --git a/js/src/jit/IonCacheIRCompiler.cpp b/js/src/jit/IonCacheIRCompiler.cpp
@@ -2330,11 +2330,6 @@ bool IonCacheIRCompiler::emitLoadArgumentDynamicSlot(ValOperandId resultId,
MOZ_CRASH("Call ICs not used in ion");
}
-bool IonCacheIRCompiler::emitArrayJoinResult(ObjOperandId objId,
- StringOperandId sepId) {
- MOZ_CRASH("Call ICs not used in ion");
-}
-
bool IonCacheIRCompiler::emitIsArrayResult(ValOperandId inputId) {
MOZ_CRASH("Call ICs not used in ion");
}