commit d116ad129f4007f7da989de8846fd8c9e80d6c06
parent 25879025a5e12d78b64c644e3872bed7d98938d0
Author: André Bargull <andre.bargull@gmail.com>
Date: Tue, 21 Oct 2025 07:05:43 +0000
Bug 1991402 - Part 16: Support LoadStringAtResult in Ion ICs. r=jandem
This follows the baseline IC implementation.
Differential Revision: https://phabricator.services.mozilla.com/D266772
Diffstat:
3 files changed, 54 insertions(+), 16 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
@@ -29,3 +29,12 @@ function testNumberToString() {
}
}
testNumberToString();
+
+function testStringAt() {
+ Object.defineProperty(String.prototype, "at_", {get: String.prototype.at});
+
+ for (var i = 0; i < 100; ++i) {
+ assertEq("a".at_, "a");
+ }
+}
+testStringAt();
diff --git a/js/src/jit/IonCacheIRCompiler.cpp b/js/src/jit/IonCacheIRCompiler.cpp
@@ -1564,9 +1564,9 @@ bool IonCacheIRCompiler::emitAllocateAndStoreDynamicSlot(
newShapeOffset, mozilla::Some(numNewSlotsOffset), preserveWrapper);
}
-bool IonCacheIRCompiler::emitLoadStringCharResult(StringOperandId strId,
- Int32OperandId indexId,
- bool handleOOB) {
+bool IonCacheIRCompiler::emitLoadStringCharResult(
+ StringOperandId strId, Int32OperandId indexId,
+ StringCharOutOfBounds outOfBounds) {
JitSpew(JitSpew_Codegen, "%s", __FUNCTION__);
AutoOutputRegister output(*this);
Register str = allocator.useRegister(masm, strId);
@@ -1582,15 +1582,21 @@ bool IonCacheIRCompiler::emitLoadStringCharResult(StringOperandId strId,
// Bounds check, load string char.
Label done;
+ Label tagResult;
Label loadFailed;
- if (!handleOOB) {
+ if (outOfBounds == StringCharOutOfBounds::Failure) {
masm.spectreBoundsCheck32(index, Address(str, JSString::offsetOfLength()),
scratch1, failure->label());
masm.loadStringChar(str, index, scratch1, scratch2, scratch3,
failure->label());
} else {
- // Return the empty string for out-of-bounds access.
- masm.movePtr(ImmGCPtr(cx_->runtime()->emptyString), scratch2);
+ if (outOfBounds == StringCharOutOfBounds::EmptyString) {
+ // Return the empty string for out-of-bounds access.
+ masm.movePtr(ImmGCPtr(cx_->runtime()->emptyString), scratch2);
+ } else {
+ // Return |undefined| for out-of-bounds access.
+ masm.moveValue(UndefinedValue(), output.valueReg());
+ }
// This CacheIR op is always preceded by |LinearizeForCharAccess|, so we're
// guaranteed to see no nested ropes.
@@ -1602,9 +1608,9 @@ bool IonCacheIRCompiler::emitLoadStringCharResult(StringOperandId strId,
// Load StaticString for this char. For larger code units perform a VM call.
Label vmCall;
masm.lookupStaticString(scratch1, scratch2, cx_->staticStrings(), &vmCall);
- masm.jump(&done);
+ masm.jump(&tagResult);
- if (handleOOB) {
+ if (outOfBounds != StringCharOutOfBounds::Failure) {
masm.bind(&loadFailed);
masm.assumeUnreachable("loadStringChar can't fail for linear strings");
}
@@ -1636,11 +1642,36 @@ bool IonCacheIRCompiler::emitLoadStringCharResult(StringOperandId strId,
masm.branchPtr(Assembler::Equal, scratch2, ImmWord(0), failure->label());
}
- masm.bind(&done);
- masm.tagValue(JSVAL_TYPE_STRING, scratch2, output.valueReg());
+ if (outOfBounds != StringCharOutOfBounds::UndefinedValue) {
+ masm.bind(&tagResult);
+ masm.bind(&done);
+ masm.tagValue(JSVAL_TYPE_STRING, scratch2, output.valueReg());
+ } else {
+ masm.bind(&tagResult);
+ masm.tagValue(JSVAL_TYPE_STRING, scratch2, output.valueReg());
+ masm.bind(&done);
+ }
return true;
}
+bool IonCacheIRCompiler::emitLoadStringCharResult(StringOperandId strId,
+ Int32OperandId indexId,
+ bool handleOOB) {
+ JitSpew(JitSpew_Codegen, "%s", __FUNCTION__);
+ auto outOfBounds = handleOOB ? StringCharOutOfBounds::EmptyString
+ : StringCharOutOfBounds::Failure;
+ return emitLoadStringCharResult(strId, indexId, outOfBounds);
+}
+
+bool IonCacheIRCompiler::emitLoadStringAtResult(StringOperandId strId,
+ Int32OperandId indexId,
+ bool handleOOB) {
+ JitSpew(JitSpew_Codegen, "%s", __FUNCTION__);
+ auto outOfBounds = handleOOB ? StringCharOutOfBounds::UndefinedValue
+ : StringCharOutOfBounds::Failure;
+ return emitLoadStringCharResult(strId, indexId, outOfBounds);
+}
+
bool IonCacheIRCompiler::emitCallNativeSetter(ObjOperandId receiverId,
uint32_t setterOffset,
ValOperandId rhsId,
@@ -2415,9 +2446,3 @@ bool IonCacheIRCompiler::emitRegExpHasCaptureGroupsResult(
ObjOperandId regexpId, StringOperandId inputId) {
MOZ_CRASH("Call ICs not used in ion");
}
-
-bool IonCacheIRCompiler::emitLoadStringAtResult(StringOperandId strId,
- Int32OperandId indexId,
- bool handleOOB) {
- MOZ_CRASH("Call ICs not used in ion");
-}
diff --git a/js/src/jit/IonCacheIRCompiler.h b/js/src/jit/IonCacheIRCompiler.h
@@ -93,6 +93,10 @@ class MOZ_RAII IonCacheIRCompiler : public CacheIRCompiler {
ValOperandId targetId, ObjOperandId receiverId, ObjOperandId handlerId,
ObjOperandId trapId, IdType id, uint32_t nargsAndFlags);
+ enum class StringCharOutOfBounds { Failure, EmptyString, UndefinedValue };
+ bool emitLoadStringCharResult(StringOperandId strId, Int32OperandId indexId,
+ StringCharOutOfBounds outOfBounds);
+
void pushStubCodePointer();
CACHE_IR_COMPILER_UNSHARED_GENERATED