commit f82c81855d62f18927cdf8ba05f78d4355f68724
parent df4d7643944feb4612d77f6916455d05573dd922
Author: André Bargull <andre.bargull@gmail.com>
Date: Mon, 13 Oct 2025 12:55:25 +0000
Bug 1990248 - Part 5: Add GuardRuntimeFuse CacheIR instruction. r=spidermonkey-reviewers,jandem
This is similar to `GuardRealmFuse`, except it guards on a runtime-wide fuse.
The two current runtime fuses are handled in "CodeGenerator.cpp", so we don't
actually have to support them for this new instruction or in
`WarpOracle::addFuseDependency`. The next part adds a new runtime fuse
which uses this new instruction, though.
Differential Revision: https://phabricator.services.mozilla.com/D265827
Diffstat:
9 files changed, 128 insertions(+), 0 deletions(-)
diff --git a/js/src/jit/CacheIRCompiler.cpp b/js/src/jit/CacheIRCompiler.cpp
@@ -11430,6 +11430,18 @@ bool CacheIRCompiler::emitGuardFuse(RealmFuses::FuseIndex fuseIndex) {
return true;
}
+bool CacheIRCompiler::emitGuardRuntimeFuse(RuntimeFuses::FuseIndex fuseIndex) {
+ JitSpew(JitSpew_Codegen, "%s", __FUNCTION__);
+
+ FailurePath* failure;
+ if (!addFailurePath(&failure)) {
+ return false;
+ }
+
+ masm.guardRuntimeFuse(fuseIndex, failure->label());
+ return true;
+}
+
bool CacheIRCompiler::emitGuardObjectFuseProperty(
ObjOperandId objId, uint32_t objFuseOwnerOffset, uint32_t objFuseOffset,
uint32_t expectedGenerationOffset, uint32_t propIndexOffset,
diff --git a/js/src/jit/CacheIROps.yaml b/js/src/jit/CacheIROps.yaml
@@ -280,6 +280,14 @@
args:
fuseWord: RealmFuseIndexImm
+# Guard on a runtime fuse.
+- name: GuardRuntimeFuse
+ shared: true
+ transpile: true
+ cost_estimate: 1
+ args:
+ fuseWord: RuntimeFuseIndexImm
+
- name: GuardObjectFuseProperty
shared: true
transpile: true
diff --git a/js/src/jit/CacheIRReader.h b/js/src/jit/CacheIRReader.h
@@ -120,6 +120,9 @@ class MOZ_RAII CacheIRReader {
RealmFuses::FuseIndex realmFuseIndex() {
return RealmFuses::FuseIndex(buffer_.readByte());
}
+ RuntimeFuses::FuseIndex runtimeFuseIndex() {
+ return RuntimeFuses::FuseIndex(buffer_.readByte());
+ }
Scalar::Type scalarType() { return Scalar::Type(buffer_.readByte()); }
JSWhyMagic whyMagic() { return JSWhyMagic(buffer_.readByte()); }
diff --git a/js/src/jit/CacheIRSpewer.cpp b/js/src/jit/CacheIRSpewer.cpp
@@ -127,6 +127,11 @@ class MOZ_RAII CacheIROpsJitSpewer {
out_.printf("%s RealmFuseIndex(%u=%s)", name, unsigned(index),
RealmFuses::getFuseName(index));
}
+ void spewRuntimeFuseIndexImm(const char* name,
+ RuntimeFuses::FuseIndex index) {
+ out_.printf("%s RuntimeFuseIndex(%u=%s)", name, unsigned(index),
+ RuntimeFuses::getFuseName(index));
+ }
public:
CacheIROpsJitSpewer(GenericPrinter& out, const char* prefix)
@@ -273,6 +278,9 @@ class MOZ_RAII CacheIROpsJSONSpewer {
void spewRealmFuseIndexImm(const char* name, RealmFuses::FuseIndex kind) {
spewArgImpl(name, "Imm", unsigned(kind));
}
+ void spewRuntimeFuseIndexImm(const char* name, RuntimeFuses::FuseIndex kind) {
+ spewArgImpl(name, "Imm", unsigned(kind));
+ }
void spewWasmValTypeImm(const char* name, wasm::ValType::Kind kind) {
spewArgImpl(name, "Imm", unsigned(kind));
}
@@ -638,6 +646,11 @@ class MOZ_RAII CacheIROpsAotSpewer {
(void)name;
out_.printf("REALMFUSE(%u)", unsigned(index));
}
+ void spewRuntimeFuseIndexImm(const char* name,
+ RuntimeFuses::FuseIndex index) {
+ (void)name;
+ out_.printf("RUNTIMEFUSE(%u)", unsigned(index));
+ }
public:
CacheIROpsAotSpewer(GenericPrinter& out) : out_(out) {}
diff --git a/js/src/jit/CacheIRWriter.h b/js/src/jit/CacheIRWriter.h
@@ -42,6 +42,7 @@
#include "vm/List.h"
#include "vm/Opcodes.h"
#include "vm/RealmFuses.h"
+#include "vm/RuntimeFuses.h"
#include "vm/Shape.h"
#include "vm/TypeofEqOperand.h" // TypeofEqOperand
#include "wasm/WasmConstants.h"
@@ -304,6 +305,11 @@ class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter {
"RealmFuses::FuseIndex must fit in a byte");
buffer_.writeByte(uint8_t(realmFuseIndex));
}
+ void writeRuntimeFuseIndexImm(RuntimeFuses::FuseIndex runtimeFuseIndex) {
+ static_assert(sizeof(RuntimeFuses::FuseIndex) == sizeof(uint8_t),
+ "RuntimeFuses::FuseIndex must fit in a byte");
+ buffer_.writeByte(uint8_t(runtimeFuseIndex));
+ }
void writeByteImm(uint32_t b) {
MOZ_ASSERT(b <= UINT8_MAX);
diff --git a/js/src/jit/GenerateCacheIRFiles.py b/js/src/jit/GenerateCacheIRFiles.py
@@ -101,6 +101,7 @@ arg_writer_info = {
"AllocKindImm": ("gc::AllocKind", "writeAllocKindImm"),
"CompletionKindImm": ("CompletionKind", "writeCompletionKindImm"),
"RealmFuseIndexImm": ("RealmFuses::FuseIndex", "writeRealmFuseIndexImm"),
+ "RuntimeFuseIndexImm": ("RuntimeFuses::FuseIndex", "writeRuntimeFuseIndexImm"),
}
@@ -210,6 +211,7 @@ arg_reader_info = {
"AllocKindImm": ("gc::AllocKind", "", "reader.allocKind()"),
"CompletionKindImm": ("CompletionKind", "", "reader.completionKind()"),
"RealmFuseIndexImm": ("RealmFuses::FuseIndex", "", "reader.realmFuseIndex()"),
+ "RuntimeFuseIndexImm": ("RuntimeFuses::FuseIndex", "", "reader.runtimeFuseIndex()"),
}
@@ -350,6 +352,7 @@ arg_spewer_method = {
"AllocKindImm": "spewAllocKindImm",
"CompletionKindImm": "spewCompletionKindImm",
"RealmFuseIndexImm": "spewRealmFuseIndexImm",
+ "RuntimeFuseIndexImm": "spewRuntimeFuseIndexImm",
}
@@ -492,6 +495,7 @@ arg_length = {
"AllocKindImm": 1,
"CompletionKindImm": 1,
"RealmFuseIndexImm": 1,
+ "RuntimeFuseIndexImm": 1,
}
diff --git a/js/src/jit/WarpCacheIRTranspiler.cpp b/js/src/jit/WarpCacheIRTranspiler.cpp
@@ -491,6 +491,13 @@ bool WarpCacheIRTranspiler::emitGuardFuse(RealmFuses::FuseIndex fuseIndex) {
}
}
+bool WarpCacheIRTranspiler::emitGuardRuntimeFuse(
+ RuntimeFuses::FuseIndex fuseIndex) {
+ // This is a no-op because WarpOracle has added a compilation dependency.
+ MOZ_ASSERT(RuntimeFuses::isInvalidatingFuse(fuseIndex));
+ return true;
+}
+
bool WarpCacheIRTranspiler::emitGuardObjectFuseProperty(
ObjOperandId objId, uint32_t objFuseOwnerOffset, uint32_t objFuseOffset,
uint32_t expectedGenerationOffset, uint32_t propIndexOffset,
diff --git a/js/src/jit/WarpOracle.cpp b/js/src/jit/WarpOracle.cpp
@@ -874,6 +874,67 @@ bool WarpOracle::addFuseDependency(RealmFuses::FuseIndex fuseIndex,
}
}
+template <auto FuseMember, CompilationDependency::Type DepType>
+struct RuntimeFuseDependency final : public CompilationDependency {
+ explicit RuntimeFuseDependency() : CompilationDependency(DepType) {}
+
+ bool registerDependency(JSContext* cx,
+ const IonScriptKey& ionScript) override {
+ MOZ_ASSERT(checkDependency(cx));
+ return (cx->runtime()->runtimeFuses.ref().*FuseMember)
+ .addFuseDependency(cx, ionScript);
+ }
+
+ CompilationDependency* clone(TempAllocator& alloc) const override {
+ return new (alloc.fallible()) RuntimeFuseDependency<FuseMember, DepType>();
+ }
+
+ bool checkDependency(JSContext* cx) const override {
+ return (cx->runtime()->runtimeFuses.ref().*FuseMember).intact();
+ }
+
+ HashNumber hash() const override { return mozilla::HashGeneric(type); }
+
+ bool operator==(const CompilationDependency& dep) const override {
+ // Since this dependency is runtime wide, they are all equal.
+ return dep.type == type;
+ }
+};
+
+bool WarpOracle::addFuseDependency(RuntimeFuses::FuseIndex fuseIndex,
+ bool* stillValid) {
+ MOZ_ASSERT(RuntimeFuses::isInvalidatingFuse(fuseIndex),
+ "All current runtime fuses are invalidating");
+
+ auto addIfStillValid = [&](const auto& dep) {
+ if (!dep.checkDependency(cx_)) {
+ *stillValid = false;
+ return true;
+ }
+ *stillValid = true;
+ return mirGen().tracker.addDependency(alloc_, dep);
+ };
+
+ // Register a compilation dependency for all fuses that are still valid.
+ switch (fuseIndex) {
+ case RuntimeFuses::FuseIndex::HasSeenObjectEmulateUndefinedFuse: {
+ using Dependency = RuntimeFuseDependency<
+ &RuntimeFuses::hasSeenObjectEmulateUndefinedFuse,
+ CompilationDependency::Type::EmulatesUndefined>;
+ return addIfStillValid(Dependency());
+ }
+ case RuntimeFuses::FuseIndex::HasSeenArrayExceedsInt32LengthFuse: {
+ using Dependency = RuntimeFuseDependency<
+ &RuntimeFuses::hasSeenArrayExceedsInt32LengthFuse,
+ CompilationDependency::Type::ArrayExceedsInt32Length>;
+ return addIfStillValid(Dependency());
+ }
+ case RuntimeFuses::FuseIndex::LastFuseIndex:
+ break;
+ }
+ MOZ_CRASH("invalid runtime fuse index");
+}
+
class ObjectPropertyFuseDependency final : public CompilationDependency {
ObjectFuse* fuse_;
uint32_t expectedGeneration_;
@@ -1106,6 +1167,17 @@ AbortReasonOr<Ok> WarpScriptOracle::maybeInlineIC(WarpOpSnapshotList& snapshots,
}
break;
}
+ case CacheOp::GuardRuntimeFuse: {
+ auto [fuseIndex] = reader.argsForGuardRuntimeFuse();
+ bool stillValid;
+ if (!oracle_->addFuseDependency(fuseIndex, &stillValid)) {
+ return abort(AbortReason::Alloc);
+ }
+ if (!stillValid) {
+ hasInvalidFuseGuard = true;
+ }
+ break;
+ }
case CacheOp::GuardObjectFuseProperty: {
auto args = reader.argsForGuardObjectFuseProperty();
ObjectFuse* fuse = reinterpret_cast<ObjectFuse*>(
diff --git a/js/src/jit/WarpOracle.h b/js/src/jit/WarpOracle.h
@@ -63,6 +63,9 @@ class MOZ_STACK_CLASS WarpOracle {
[[nodiscard]] bool addFuseDependency(RealmFuses::FuseIndex fuseIndex,
bool* stillValid);
+ [[nodiscard]] bool addFuseDependency(RuntimeFuses::FuseIndex fuseIndex,
+ bool* stillValid);
+
AbortReasonOr<WarpSnapshot*> createSnapshot();
mozilla::GenericErrorResult<AbortReason> abort(HandleScript script,