diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 01:14:29 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 01:14:29 +0000 |
commit | fbaf0bb26397aa498eb9156f06d5a6fe34dd7dd8 (patch) | |
tree | 4c1ccaf5486d4f2009f9a338a98a83e886e29c97 /js/src/wasm | |
parent | Releasing progress-linux version 124.0.1-1~progress7.99u1. (diff) | |
download | firefox-fbaf0bb26397aa498eb9156f06d5a6fe34dd7dd8.tar.xz firefox-fbaf0bb26397aa498eb9156f06d5a6fe34dd7dd8.zip |
Merging upstream version 125.0.1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'js/src/wasm')
42 files changed, 1048 insertions, 721 deletions
diff --git a/js/src/wasm/GenerateBuiltinModules.py b/js/src/wasm/GenerateBuiltinModules.py index 17270bc46e..0bd17d8821 100644 --- a/js/src/wasm/GenerateBuiltinModules.py +++ b/js/src/wasm/GenerateBuiltinModules.py @@ -47,6 +47,58 @@ def cppBool(v): return "false" +def specTypeToMIRType(specType): + if specType == "i32" or specType == "i64" or specType == "f32" or specType == "f64": + return f"ValType::{specType}().toMIRType()" + if ( + specType == "externref" + or specType == "anyref" + or specType == "funcref" + or isinstance(specType, dict) + ): + return "MIRType::WasmAnyRef" + raise ValueError() + + +def specHeapTypeToTypeCode(specHeapType): + if specHeapType == "func": + return "Func" + if specHeapType == "any": + return "Any" + if specHeapType == "extern": + return "Extern" + if specHeapType == "array": + return "Array" + if specHeapType == "struct": + return "Struct" + raise ValueError() + + +def specTypeToValType(specType): + if specType == "i32" or specType == "i64" or specType == "f32" or specType == "f64": + return f"ValType::{specType}()" + + if specType == "externref": + return "ValType(RefType::extern_())" + + if specType == "anyref": + return "ValType(RefType::any())" + + if specType == "funcref": + return "ValType(RefType::func())" + + if isinstance(specType, dict): + nullable = cppBool(specType["nullable"]) + if "type" in specType: + ref = specType["type"] + return f"ValType(RefType::fromTypeDef({ref}, {nullable}))" + else: + code = specType["code"] + return f"ValType(RefType::fromTypeCode(TypeCode(RefType::{specHeapTypeToTypeCode(code)}), {nullable}))" + + raise ValueError() + + def main(c_out, yaml_path): data = load_yaml(yaml_path) @@ -64,34 +116,45 @@ def main(c_out, yaml_path): for op in data: # Define DECLARE_BUILTIN_MODULE_FUNC_PARAM_VALTYPES_<op> as: # `{ValType::I32, ValType::I32, ...}`. + valTypes = ", ".join(specTypeToValType(p) for p in op["params"]) contents += ( f"#define DECLARE_BUILTIN_MODULE_FUNC_PARAM_VALTYPES_{op['op']} " - f"{{{', '.join(op['params'])}}}\n" + f"{{{valTypes}}}\n" ) - # Define DECLARE_BUILTIN_MODULE_FUNC_PARAM_SASTYPES_<op> as: - # `<num_types>, {_PTR, _I32, ..., _PTR, _END}`. + # Define DECLARE_BUILTIN_MODULE_FUNC_PARAM_MIRTYPES_<op> as: + # `<num_types>, {MIRType::Pointer, _I32, ..., MIRType::Pointer, _END}`. num_types = len(op["params"]) + 1 - sas_types = ( - f"{{_PTR{''.join(', ' + (p + '.toMIRType()') for p in op['params'])}" - ) + mir_types = "{MIRType::Pointer" + mir_types += "".join(", " + specTypeToMIRType(p) for p in op["params"]) if op["uses_memory"]: - sas_types += ", _PTR" + mir_types += ", MIRType::Pointer" num_types += 1 - sas_types += ", _END}" + # Add the end marker + mir_types += ", MIRType::None}" - contents += f"#define DECLARE_BUILTIN_MODULE_FUNC_PARAM_SASTYPES_{op['op']} {num_types}, {sas_types}\n" + contents += f"#define DECLARE_BUILTIN_MODULE_FUNC_PARAM_MIRTYPES_{op['op']} {num_types}, {mir_types}\n" + # Define DECLARE_BUILTIN_MODULE_FUNC_RESULT_VALTYPE_<op> as: + # `Some(X)` if present, or else `Nothing()`. result_valtype = "" - result_sastype = "" if "result" in op: - result_valtype = f"Some({op['result']})\n" - result_sastype = f"{op['result']}.toMIRType()\n" + result_valtype = f"Some({specTypeToValType(op['result'])})\n" else: result_valtype = "Nothing()" - result_sastype = "_VOID" contents += f"#define DECLARE_BUILTIN_MODULE_FUNC_RESULT_VALTYPE_{op['op']} {result_valtype}\n" - contents += f"#define DECLARE_BUILTIN_MODULE_FUNC_RESULT_SASTYPE_{op['op']} {result_sastype}\n" - contents += f"#define DECLARE_BUILTIN_MODULE_FUNC_FAILMODE_{op['op']} _{op['fail_mode']}\n" + + # Define DECLARE_BUILTIN_MODULE_FUNC_RESULT_MIRTYPE_<op> as: + # `X` if present, or else `MIRType::None`. + result_mirtype = "" + if "result" in op: + result_mirtype = specTypeToMIRType(op["result"]) + "\n" + else: + result_mirtype = "MIRType::None" + contents += f"#define DECLARE_BUILTIN_MODULE_FUNC_RESULT_MIRTYPE_{op['op']} {result_mirtype}\n" + + # Define DECLARE_BUILTIN_MODULE_FUNC_FAILMODE_<op> as: + # `FailureMode::X`. + contents += f"#define DECLARE_BUILTIN_MODULE_FUNC_FAILMODE_{op['op']} FailureMode::{op['fail_mode']}\n" generate_header(c_out, "wasm_WasmBuiltinModuleGenerated_h", contents) diff --git a/js/src/wasm/WasmBCClass.h b/js/src/wasm/WasmBCClass.h index c216d0ffd5..844ae3381a 100644 --- a/js/src/wasm/WasmBCClass.h +++ b/js/src/wasm/WasmBCClass.h @@ -297,6 +297,10 @@ struct BaseCompiler final { // Flag indicating that the compiler is currently in a dead code region. bool deadCode_; + // Store previously finished note to know if we need to insert a nop in + // finishTryNote. + size_t mostRecentFinishedTryNoteIndex_; + /////////////////////////////////////////////////////////////////////////// // // State for bounds check elimination. @@ -973,7 +977,7 @@ struct BaseCompiler final { bool tailCall, CodeOffset* fastCallOffset, CodeOffset* slowCallOffset); CodeOffset callImport(unsigned instanceDataOffset, const FunctionCall& call); -#ifdef ENABLE_WASM_FUNCTION_REFERENCES +#ifdef ENABLE_WASM_GC void callRef(const Stk& calleeRef, const FunctionCall& call, CodeOffset* fastCallOffset, CodeOffset* slowCallOffset); # ifdef ENABLE_WASM_TAIL_CALLS @@ -1641,7 +1645,7 @@ struct BaseCompiler final { [[nodiscard]] bool emitRefFunc(); [[nodiscard]] bool emitRefNull(); [[nodiscard]] bool emitRefIsNull(); -#ifdef ENABLE_WASM_FUNCTION_REFERENCES +#ifdef ENABLE_WASM_GC [[nodiscard]] bool emitRefAsNonNull(); [[nodiscard]] bool emitBrOnNull(); [[nodiscard]] bool emitBrOnNonNull(); diff --git a/js/src/wasm/WasmBCMemory.cpp b/js/src/wasm/WasmBCMemory.cpp index f4e19d95e8..b6ef67f24d 100644 --- a/js/src/wasm/WasmBCMemory.cpp +++ b/js/src/wasm/WasmBCMemory.cpp @@ -1212,7 +1212,7 @@ static void PopAndAllocate(BaseCompiler* bc, ValType type, Scalar::Type viewType, AtomicOp op, RegI32* rd, RegI32* rv, Temps* temps) { bc->needI32(bc->specific_.eax); - if (op == AtomicFetchAddOp || op == AtomicFetchSubOp) { + if (op == AtomicOp::Add || op == AtomicOp::Sub) { // We use xadd, so source and destination are the same. Using // eax here is overconstraining, but for byte operations on x86 // we do need something with a byte register. @@ -1246,7 +1246,7 @@ static void Perform(BaseCompiler* bc, const MemoryAccessDesc& access, T srcAddr, # else RegI32 temp; ScratchI32 scratch(*bc); - if (op != AtomicFetchAddOp && op != AtomicFetchSubOp) { + if (op != AtomicOp::Add && op != AtomicOp::Sub) { temp = scratch; } # endif @@ -1401,7 +1401,7 @@ namespace atomic_rmw64 { static void PopAndAllocate(BaseCompiler* bc, AtomicOp op, RegI64* rd, RegI64* rv, RegI64* temp) { - if (op == AtomicFetchAddOp || op == AtomicFetchSubOp) { + if (op == AtomicOp::Add || op == AtomicOp::Sub) { // We use xaddq, so input and output must be the same register. *rv = bc->popI64(); *rd = *rv; @@ -1422,7 +1422,7 @@ static void Perform(BaseCompiler* bc, const MemoryAccessDesc& access, static void Deallocate(BaseCompiler* bc, AtomicOp op, RegI64 rv, RegI64 temp) { bc->maybeFree(temp); - if (op != AtomicFetchAddOp && op != AtomicFetchSubOp) { + if (op != AtomicOp::Add && op != AtomicOp::Sub) { bc->freeI64(rv); } } diff --git a/js/src/wasm/WasmBaselineCompile.cpp b/js/src/wasm/WasmBaselineCompile.cpp index 196e49f76e..cb0fbde6ec 100644 --- a/js/src/wasm/WasmBaselineCompile.cpp +++ b/js/src/wasm/WasmBaselineCompile.cpp @@ -1658,7 +1658,7 @@ bool BaseCompiler::callIndirect(uint32_t funcTypeIndex, uint32_t tableIndex, return true; } -#ifdef ENABLE_WASM_FUNCTION_REFERENCES +#ifdef ENABLE_WASM_GC void BaseCompiler::callRef(const Stk& calleeRef, const FunctionCall& call, CodeOffset* fastCallOffset, CodeOffset* slowCallOffset) { @@ -1788,15 +1788,25 @@ void BaseCompiler::finishTryNote(size_t tryNoteIndex) { masm.nop(); } - // Check the previous try note to ensure that we don't share an edge with - // it that could lead to ambiguity. Insert a nop, if required. - if (tryNotes.length() > 0) { - const TryNote& previous = tryNotes.back(); + // Check the most recent finished try note to ensure that we don't share an + // edge with it that could lead to ambiguity. Insert a nop, if required. + // + // Notice that finishTryNote is called in LIFO order -- using depth-first + // search numbering to see if we are traversing back from a nested try to a + // parent try, where we may need to ensure that the end offsets do not + // coincide. + // + // In the case the tryNodeIndex >= mostRecentFinishedTryNoteIndex_, we have + // finished a try that began after the most recent finished try, and so + // startTryNote will take care of any nops. + if (tryNoteIndex < mostRecentFinishedTryNoteIndex_) { + const TryNote& previous = tryNotes[mostRecentFinishedTryNoteIndex_]; uint32_t currentOffset = masm.currentOffset(); if (previous.tryBodyEnd() == currentOffset) { masm.nop(); } } + mostRecentFinishedTryNoteIndex_ = tryNoteIndex; // Don't set the end of the try note if we've OOM'ed, as the above nop's may // not have been placed. This is okay as this compilation will be thrown @@ -3875,7 +3885,7 @@ bool BaseCompiler::emitBrIf() { return emitBranchPerform(&b); } -#ifdef ENABLE_WASM_FUNCTION_REFERENCES +#ifdef ENABLE_WASM_GC bool BaseCompiler::emitBrOnNull() { MOZ_ASSERT(!hasLatentOp()); @@ -5286,7 +5296,7 @@ bool BaseCompiler::emitReturnCallIndirect() { } #endif -#ifdef ENABLE_WASM_FUNCTION_REFERENCES +#ifdef ENABLE_WASM_GC bool BaseCompiler::emitCallRef() { const FuncType* funcType; Nothing unused_callee; @@ -6289,7 +6299,7 @@ bool BaseCompiler::emitRefIsNull() { return true; } -#ifdef ENABLE_WASM_FUNCTION_REFERENCES +#ifdef ENABLE_WASM_GC bool BaseCompiler::emitRefAsNonNull() { Nothing nothing; if (!iter_.readRefAsNonNull(¬hing)) { @@ -9776,13 +9786,13 @@ bool BaseCompiler::emitCallBuiltinModuleFunc() { return true; } - if (builtinModuleFunc->usesMemory) { + if (builtinModuleFunc->usesMemory()) { // The final parameter of an builtinModuleFunc is implicitly the heap base pushHeapBase(0); } // Call the builtinModuleFunc - return emitInstanceCall(builtinModuleFunc->signature); + return emitInstanceCall(*builtinModuleFunc->sig()); } ////////////////////////////////////////////////////////////////////////////// @@ -9989,36 +9999,18 @@ bool BaseCompiler::emitBody() { case uint16_t(Op::Else): CHECK_NEXT(emitElse()); case uint16_t(Op::Try): - if (!moduleEnv_.exceptionsEnabled()) { - return iter_.unrecognizedOpcode(&op); - } CHECK_NEXT(emitTry()); case uint16_t(Op::Catch): - if (!moduleEnv_.exceptionsEnabled()) { - return iter_.unrecognizedOpcode(&op); - } CHECK_NEXT(emitCatch()); case uint16_t(Op::CatchAll): - if (!moduleEnv_.exceptionsEnabled()) { - return iter_.unrecognizedOpcode(&op); - } CHECK_NEXT(emitCatchAll()); case uint16_t(Op::Delegate): - if (!moduleEnv_.exceptionsEnabled()) { - return iter_.unrecognizedOpcode(&op); - } CHECK(emitDelegate()); iter_.popDelegate(); NEXT(); case uint16_t(Op::Throw): - if (!moduleEnv_.exceptionsEnabled()) { - return iter_.unrecognizedOpcode(&op); - } CHECK_NEXT(emitThrow()); case uint16_t(Op::Rethrow): - if (!moduleEnv_.exceptionsEnabled()) { - return iter_.unrecognizedOpcode(&op); - } CHECK_NEXT(emitRethrow()); case uint16_t(Op::ThrowRef): if (!moduleEnv_.exnrefEnabled()) { @@ -10063,16 +10055,15 @@ bool BaseCompiler::emitBody() { } CHECK_NEXT(emitReturnCallIndirect()); #endif -#ifdef ENABLE_WASM_FUNCTION_REFERENCES +#ifdef ENABLE_WASM_GC case uint16_t(Op::CallRef): - if (!moduleEnv_.functionReferencesEnabled()) { + if (!moduleEnv_.gcEnabled()) { return iter_.unrecognizedOpcode(&op); } CHECK_NEXT(emitCallRef()); # ifdef ENABLE_WASM_TAIL_CALLS case uint16_t(Op::ReturnCallRef): - if (!moduleEnv_.functionReferencesEnabled() || - !moduleEnv_.tailCallsEnabled()) { + if (!moduleEnv_.gcEnabled() || !moduleEnv_.tailCallsEnabled()) { return iter_.unrecognizedOpcode(&op); } CHECK_NEXT(emitReturnCallRef()); @@ -10609,19 +10600,19 @@ bool BaseCompiler::emitBody() { case uint16_t(Op::MemorySize): CHECK_NEXT(emitMemorySize()); -#ifdef ENABLE_WASM_FUNCTION_REFERENCES +#ifdef ENABLE_WASM_GC case uint16_t(Op::RefAsNonNull): - if (!moduleEnv_.functionReferencesEnabled()) { + if (!moduleEnv_.gcEnabled()) { return iter_.unrecognizedOpcode(&op); } CHECK_NEXT(emitRefAsNonNull()); case uint16_t(Op::BrOnNull): - if (!moduleEnv_.functionReferencesEnabled()) { + if (!moduleEnv_.gcEnabled()) { return iter_.unrecognizedOpcode(&op); } CHECK_NEXT(emitBrOnNull()); case uint16_t(Op::BrOnNonNull): - if (!moduleEnv_.functionReferencesEnabled()) { + if (!moduleEnv_.gcEnabled()) { return iter_.unrecognizedOpcode(&op); } CHECK_NEXT(emitBrOnNonNull()); @@ -11484,113 +11475,113 @@ bool BaseCompiler::emitBody() { case uint32_t(ThreadOp::I32AtomicAdd): CHECK_NEXT( - emitAtomicRMW(ValType::I32, Scalar::Int32, AtomicFetchAddOp)); + emitAtomicRMW(ValType::I32, Scalar::Int32, AtomicOp::Add)); case uint32_t(ThreadOp::I64AtomicAdd): CHECK_NEXT( - emitAtomicRMW(ValType::I64, Scalar::Int64, AtomicFetchAddOp)); + emitAtomicRMW(ValType::I64, Scalar::Int64, AtomicOp::Add)); case uint32_t(ThreadOp::I32AtomicAdd8U): CHECK_NEXT( - emitAtomicRMW(ValType::I32, Scalar::Uint8, AtomicFetchAddOp)); + emitAtomicRMW(ValType::I32, Scalar::Uint8, AtomicOp::Add)); case uint32_t(ThreadOp::I32AtomicAdd16U): CHECK_NEXT( - emitAtomicRMW(ValType::I32, Scalar::Uint16, AtomicFetchAddOp)); + emitAtomicRMW(ValType::I32, Scalar::Uint16, AtomicOp::Add)); case uint32_t(ThreadOp::I64AtomicAdd8U): CHECK_NEXT( - emitAtomicRMW(ValType::I64, Scalar::Uint8, AtomicFetchAddOp)); + emitAtomicRMW(ValType::I64, Scalar::Uint8, AtomicOp::Add)); case uint32_t(ThreadOp::I64AtomicAdd16U): CHECK_NEXT( - emitAtomicRMW(ValType::I64, Scalar::Uint16, AtomicFetchAddOp)); + emitAtomicRMW(ValType::I64, Scalar::Uint16, AtomicOp::Add)); case uint32_t(ThreadOp::I64AtomicAdd32U): CHECK_NEXT( - emitAtomicRMW(ValType::I64, Scalar::Uint32, AtomicFetchAddOp)); + emitAtomicRMW(ValType::I64, Scalar::Uint32, AtomicOp::Add)); case uint32_t(ThreadOp::I32AtomicSub): CHECK_NEXT( - emitAtomicRMW(ValType::I32, Scalar::Int32, AtomicFetchSubOp)); + emitAtomicRMW(ValType::I32, Scalar::Int32, AtomicOp::Sub)); case uint32_t(ThreadOp::I64AtomicSub): CHECK_NEXT( - emitAtomicRMW(ValType::I64, Scalar::Int64, AtomicFetchSubOp)); + emitAtomicRMW(ValType::I64, Scalar::Int64, AtomicOp::Sub)); case uint32_t(ThreadOp::I32AtomicSub8U): CHECK_NEXT( - emitAtomicRMW(ValType::I32, Scalar::Uint8, AtomicFetchSubOp)); + emitAtomicRMW(ValType::I32, Scalar::Uint8, AtomicOp::Sub)); case uint32_t(ThreadOp::I32AtomicSub16U): CHECK_NEXT( - emitAtomicRMW(ValType::I32, Scalar::Uint16, AtomicFetchSubOp)); + emitAtomicRMW(ValType::I32, Scalar::Uint16, AtomicOp::Sub)); case uint32_t(ThreadOp::I64AtomicSub8U): CHECK_NEXT( - emitAtomicRMW(ValType::I64, Scalar::Uint8, AtomicFetchSubOp)); + emitAtomicRMW(ValType::I64, Scalar::Uint8, AtomicOp::Sub)); case uint32_t(ThreadOp::I64AtomicSub16U): CHECK_NEXT( - emitAtomicRMW(ValType::I64, Scalar::Uint16, AtomicFetchSubOp)); + emitAtomicRMW(ValType::I64, Scalar::Uint16, AtomicOp::Sub)); case uint32_t(ThreadOp::I64AtomicSub32U): CHECK_NEXT( - emitAtomicRMW(ValType::I64, Scalar::Uint32, AtomicFetchSubOp)); + emitAtomicRMW(ValType::I64, Scalar::Uint32, AtomicOp::Sub)); case uint32_t(ThreadOp::I32AtomicAnd): CHECK_NEXT( - emitAtomicRMW(ValType::I32, Scalar::Int32, AtomicFetchAndOp)); + emitAtomicRMW(ValType::I32, Scalar::Int32, AtomicOp::And)); case uint32_t(ThreadOp::I64AtomicAnd): CHECK_NEXT( - emitAtomicRMW(ValType::I64, Scalar::Int64, AtomicFetchAndOp)); + emitAtomicRMW(ValType::I64, Scalar::Int64, AtomicOp::And)); case uint32_t(ThreadOp::I32AtomicAnd8U): CHECK_NEXT( - emitAtomicRMW(ValType::I32, Scalar::Uint8, AtomicFetchAndOp)); + emitAtomicRMW(ValType::I32, Scalar::Uint8, AtomicOp::And)); case uint32_t(ThreadOp::I32AtomicAnd16U): CHECK_NEXT( - emitAtomicRMW(ValType::I32, Scalar::Uint16, AtomicFetchAndOp)); + emitAtomicRMW(ValType::I32, Scalar::Uint16, AtomicOp::And)); case uint32_t(ThreadOp::I64AtomicAnd8U): CHECK_NEXT( - emitAtomicRMW(ValType::I64, Scalar::Uint8, AtomicFetchAndOp)); + emitAtomicRMW(ValType::I64, Scalar::Uint8, AtomicOp::And)); case uint32_t(ThreadOp::I64AtomicAnd16U): CHECK_NEXT( - emitAtomicRMW(ValType::I64, Scalar::Uint16, AtomicFetchAndOp)); + emitAtomicRMW(ValType::I64, Scalar::Uint16, AtomicOp::And)); case uint32_t(ThreadOp::I64AtomicAnd32U): CHECK_NEXT( - emitAtomicRMW(ValType::I64, Scalar::Uint32, AtomicFetchAndOp)); + emitAtomicRMW(ValType::I64, Scalar::Uint32, AtomicOp::And)); case uint32_t(ThreadOp::I32AtomicOr): CHECK_NEXT( - emitAtomicRMW(ValType::I32, Scalar::Int32, AtomicFetchOrOp)); + emitAtomicRMW(ValType::I32, Scalar::Int32, AtomicOp::Or)); case uint32_t(ThreadOp::I64AtomicOr): CHECK_NEXT( - emitAtomicRMW(ValType::I64, Scalar::Int64, AtomicFetchOrOp)); + emitAtomicRMW(ValType::I64, Scalar::Int64, AtomicOp::Or)); case uint32_t(ThreadOp::I32AtomicOr8U): CHECK_NEXT( - emitAtomicRMW(ValType::I32, Scalar::Uint8, AtomicFetchOrOp)); + emitAtomicRMW(ValType::I32, Scalar::Uint8, AtomicOp::Or)); case uint32_t(ThreadOp::I32AtomicOr16U): CHECK_NEXT( - emitAtomicRMW(ValType::I32, Scalar::Uint16, AtomicFetchOrOp)); + emitAtomicRMW(ValType::I32, Scalar::Uint16, AtomicOp::Or)); case uint32_t(ThreadOp::I64AtomicOr8U): CHECK_NEXT( - emitAtomicRMW(ValType::I64, Scalar::Uint8, AtomicFetchOrOp)); + emitAtomicRMW(ValType::I64, Scalar::Uint8, AtomicOp::Or)); case uint32_t(ThreadOp::I64AtomicOr16U): CHECK_NEXT( - emitAtomicRMW(ValType::I64, Scalar::Uint16, AtomicFetchOrOp)); + emitAtomicRMW(ValType::I64, Scalar::Uint16, AtomicOp::Or)); case uint32_t(ThreadOp::I64AtomicOr32U): CHECK_NEXT( - emitAtomicRMW(ValType::I64, Scalar::Uint32, AtomicFetchOrOp)); + emitAtomicRMW(ValType::I64, Scalar::Uint32, AtomicOp::Or)); case uint32_t(ThreadOp::I32AtomicXor): CHECK_NEXT( - emitAtomicRMW(ValType::I32, Scalar::Int32, AtomicFetchXorOp)); + emitAtomicRMW(ValType::I32, Scalar::Int32, AtomicOp::Xor)); case uint32_t(ThreadOp::I64AtomicXor): CHECK_NEXT( - emitAtomicRMW(ValType::I64, Scalar::Int64, AtomicFetchXorOp)); + emitAtomicRMW(ValType::I64, Scalar::Int64, AtomicOp::Xor)); case uint32_t(ThreadOp::I32AtomicXor8U): CHECK_NEXT( - emitAtomicRMW(ValType::I32, Scalar::Uint8, AtomicFetchXorOp)); + emitAtomicRMW(ValType::I32, Scalar::Uint8, AtomicOp::Xor)); case uint32_t(ThreadOp::I32AtomicXor16U): CHECK_NEXT( - emitAtomicRMW(ValType::I32, Scalar::Uint16, AtomicFetchXorOp)); + emitAtomicRMW(ValType::I32, Scalar::Uint16, AtomicOp::Xor)); case uint32_t(ThreadOp::I64AtomicXor8U): CHECK_NEXT( - emitAtomicRMW(ValType::I64, Scalar::Uint8, AtomicFetchXorOp)); + emitAtomicRMW(ValType::I64, Scalar::Uint8, AtomicOp::Xor)); case uint32_t(ThreadOp::I64AtomicXor16U): CHECK_NEXT( - emitAtomicRMW(ValType::I64, Scalar::Uint16, AtomicFetchXorOp)); + emitAtomicRMW(ValType::I64, Scalar::Uint16, AtomicOp::Xor)); case uint32_t(ThreadOp::I64AtomicXor32U): CHECK_NEXT( - emitAtomicRMW(ValType::I64, Scalar::Uint32, AtomicFetchXorOp)); + emitAtomicRMW(ValType::I64, Scalar::Uint32, AtomicOp::Xor)); case uint32_t(ThreadOp::I32AtomicXchg): CHECK_NEXT(emitAtomicXchg(ValType::I32, Scalar::Int32)); @@ -11851,6 +11842,8 @@ BaseCompiler::BaseCompiler(const ModuleEnvironment& moduleEnv, stackMapGenerator_(stackMaps, trapExitLayout, trapExitLayoutNumWords, *masm), deadCode_(false), + // Init value is selected to ensure proper logic in finishTryNote. + mostRecentFinishedTryNoteIndex_(0), bceSafe_(0), latentOp_(LatentOp::None), latentType_(ValType::I32), @@ -11979,10 +11972,7 @@ bool js::wasm::BaselineCompileFunctions(const ModuleEnvironment& moduleEnv, // Build the local types vector. ValTypeVector locals; - if (!locals.appendAll(moduleEnv.funcs[func.index].type->args())) { - return false; - } - if (!DecodeLocalEntries(d, *moduleEnv.types, moduleEnv.features, &locals)) { + if (!DecodeLocalEntriesWithParams(d, moduleEnv, func.index, &locals)) { return false; } diff --git a/js/src/wasm/WasmBinary.h b/js/src/wasm/WasmBinary.h index 2d41528157..da17a0a864 100644 --- a/js/src/wasm/WasmBinary.h +++ b/js/src/wasm/WasmBinary.h @@ -72,12 +72,18 @@ class Opcode { static_assert(size_t(SimdOp::Limit) <= 0xFFFFFF, "fits"); MOZ_ASSERT(size_t(op) < size_t(SimdOp::Limit)); } + MOZ_IMPLICIT Opcode(GcOp op) + : bits_((uint32_t(op) << 8) | uint32_t(Op::GcPrefix)) { + static_assert(size_t(SimdOp::Limit) <= 0xFFFFFF, "fits"); + MOZ_ASSERT(size_t(op) < size_t(SimdOp::Limit)); + } bool isOp() const { return bits_ < uint32_t(Op::FirstPrefix); } bool isMisc() const { return (bits_ & 255) == uint32_t(Op::MiscPrefix); } bool isThread() const { return (bits_ & 255) == uint32_t(Op::ThreadPrefix); } bool isMoz() const { return (bits_ & 255) == uint32_t(Op::MozPrefix); } bool isSimd() const { return (bits_ & 255) == uint32_t(Op::SimdPrefix); } + bool isGc() const { return (bits_ & 255) == uint32_t(Op::GcPrefix); } Op asOp() const { MOZ_ASSERT(isOp()); @@ -99,6 +105,10 @@ class Opcode { MOZ_ASSERT(isSimd()); return SimdOp(bits_ >> 8); } + GcOp asGc() const { + MOZ_ASSERT(isGc()); + return GcOp(bits_ >> 8); + } uint32_t bits() const { return bits_; } @@ -127,6 +137,7 @@ using MaybeSectionRange = Maybe<SectionRange>; class Encoder { Bytes& bytes_; + const TypeContext* types_; template <class T> [[nodiscard]] bool write(const T& v) { @@ -201,7 +212,13 @@ class Encoder { } public: - explicit Encoder(Bytes& bytes) : bytes_(bytes) { MOZ_ASSERT(empty()); } + explicit Encoder(Bytes& bytes) : bytes_(bytes), types_(nullptr) { + MOZ_ASSERT(empty()); + } + explicit Encoder(Bytes& bytes, const TypeContext& types) + : bytes_(bytes), types_(&types) { + MOZ_ASSERT(empty()); + } size_t currentOffset() const { return bytes_.length(); } bool empty() const { return currentOffset() == 0; } @@ -226,9 +243,17 @@ class Encoder { [[nodiscard]] bool writeVarS64(int64_t i) { return writeVarS<int64_t>(i); } [[nodiscard]] bool writeValType(ValType type) { static_assert(size_t(TypeCode::Limit) <= UINT8_MAX, "fits"); - // writeValType is only used by asm.js, which doesn't use type - // references - MOZ_RELEASE_ASSERT(!type.isTypeRef(), "NYI"); + if (type.isTypeRef()) { + MOZ_RELEASE_ASSERT(types_, + "writeValType is used, but types were not specified."); + if (!writeFixedU8(uint8_t(type.isNullable() ? TypeCode::NullableRef + : TypeCode::Ref))) { + return false; + } + uint32_t typeIndex = types_->indexOf(*type.typeDef()); + // Encode positive LEB S33 as S64. + return writeVarS64(typeIndex); + } TypeCode tc = type.packed().typeCode(); MOZ_ASSERT(size_t(tc) < size_t(TypeCode::Limit)); return writeFixedU8(uint8_t(tc)); @@ -693,9 +718,9 @@ inline bool Decoder::readPackedType(const TypeContext& types, } case uint8_t(TypeCode::Ref): case uint8_t(TypeCode::NullableRef): { -#ifdef ENABLE_WASM_FUNCTION_REFERENCES - if (!features.functionReferences) { - return fail("(ref T) types not enabled"); +#ifdef ENABLE_WASM_GC + if (!features.gc) { + return fail("gc not enabled"); } bool nullable = code == uint8_t(TypeCode::NullableRef); RefType refType; @@ -718,7 +743,7 @@ inline bool Decoder::readPackedType(const TypeContext& types, case uint8_t(TypeCode::NullAnyRef): { #ifdef ENABLE_WASM_GC if (!features.gc) { - return fail("gc types not enabled"); + return fail("gc not enabled"); } *type = RefType::fromTypeCode(TypeCode(code), true); return true; @@ -784,7 +809,7 @@ inline bool Decoder::readHeapType(const TypeContext& types, case uint8_t(TypeCode::NullExternRef): case uint8_t(TypeCode::NullAnyRef): if (!features.gc) { - return fail("gc types not enabled"); + return fail("gc not enabled"); } *type = RefType::fromTypeCode(TypeCode(code), nullable); return true; @@ -794,8 +819,8 @@ inline bool Decoder::readHeapType(const TypeContext& types, } } -#ifdef ENABLE_WASM_FUNCTION_REFERENCES - if (features.functionReferences) { +#ifdef ENABLE_WASM_GC + if (features.gc) { int32_t x; if (!readVarS32(&x) || x < 0 || uint32_t(x) >= types.length()) { return fail("invalid heap type index"); diff --git a/js/src/wasm/WasmBuiltinModule.cpp b/js/src/wasm/WasmBuiltinModule.cpp index 0748977c8b..044591224e 100644 --- a/js/src/wasm/WasmBuiltinModule.cpp +++ b/js/src/wasm/WasmBuiltinModule.cpp @@ -27,28 +27,29 @@ #include "wasm/WasmJS.h" #include "wasm/WasmModule.h" #include "wasm/WasmOpIter.h" +#include "wasm/WasmStaticTypeDefs.h" #include "wasm/WasmValidate.h" using namespace js; using namespace js::wasm; -#define VISIT_BUILTIN_FUNC(op, export, sa_name, abitype, entry, uses_memory, \ - idx) \ - static const ValType BuiltinModuleFunc##op##_Params[] = \ - DECLARE_BUILTIN_MODULE_FUNC_PARAM_VALTYPES_##op; \ - \ - const BuiltinModuleFunc BuiltinModuleFunc##op = { \ - export, \ - mozilla::Span<const ValType>(BuiltinModuleFunc##op##_Params), \ - DECLARE_BUILTIN_MODULE_FUNC_RESULT_VALTYPE_##op, \ - SASig##sa_name, \ - uses_memory, \ - }; - -FOR_EACH_BUILTIN_MODULE_FUNC(VISIT_BUILTIN_FUNC) -#undef VISIT_BUILTIN_FUNC +BuiltinModuleFuncs* BuiltinModuleFuncs::singleton_ = nullptr; + +[[nodiscard]] bool BuiltinModuleFunc::init(const RefPtr<TypeContext>& types, + mozilla::Span<const ValType> params, + Maybe<ValType> result, + bool usesMemory, + const SymbolicAddressSignature* sig, + const char* exportName) { + // This builtin must not have been initialized yet. + MOZ_ASSERT(!recGroup_); + + // Initialize the basic fields + exportName_ = exportName; + sig_ = sig; + usesMemory_ = usesMemory; -bool BuiltinModuleFunc::funcType(FuncType* type) const { + // Create a function type for the given params and result ValTypeVector paramVec; if (!paramVec.append(params.data(), params.data() + params.size())) { return false; @@ -57,21 +58,48 @@ bool BuiltinModuleFunc::funcType(FuncType* type) const { if (result.isSome() && !resultVec.append(*result)) { return false; } - *type = FuncType(std::move(paramVec), std::move(resultVec)); + const TypeDef* typeDef = + types->addType(FuncType(std::move(paramVec), std::move(resultVec))); + if (!typeDef) { + return false; + } + recGroup_ = &typeDef->recGroup(); return true; } -/* static */ -const BuiltinModuleFunc& BuiltinModuleFunc::getFromId(BuiltinModuleFuncId id) { - switch (id) { -#define VISIT_BUILTIN_FUNC(op, ...) \ - case BuiltinModuleFuncId::op: \ - return BuiltinModuleFunc##op; - FOR_EACH_BUILTIN_MODULE_FUNC(VISIT_BUILTIN_FUNC) +bool BuiltinModuleFuncs::init() { + singleton_ = js_new<BuiltinModuleFuncs>(); + if (!singleton_) { + return false; + } + + RefPtr<TypeContext> types = js_new<TypeContext>(); + if (!types) { + return false; + } + +#define VISIT_BUILTIN_FUNC(op, export, sa_name, abitype, entry, uses_memory, \ + ...) \ + const ValType op##Params[] = \ + DECLARE_BUILTIN_MODULE_FUNC_PARAM_VALTYPES_##op; \ + Maybe<ValType> op##Result = DECLARE_BUILTIN_MODULE_FUNC_RESULT_VALTYPE_##op; \ + if (!singleton_->funcs_[BuiltinModuleFuncId::op].init( \ + types, mozilla::Span<const ValType>(op##Params), op##Result, \ + uses_memory, &SASig##sa_name, export)) { \ + return false; \ + } + FOR_EACH_BUILTIN_MODULE_FUNC(VISIT_BUILTIN_FUNC) #undef VISIT_BUILTIN_FUNC - default: - MOZ_CRASH("unexpected builtinModuleFunc"); + + return true; +} + +void BuiltinModuleFuncs::destroy() { + if (!singleton_) { + return; } + js_delete(singleton_); + singleton_ = nullptr; } bool EncodeFuncBody(const BuiltinModuleFunc& builtinModuleFunc, @@ -80,7 +108,8 @@ bool EncodeFuncBody(const BuiltinModuleFunc& builtinModuleFunc, if (!EncodeLocalEntries(encoder, ValTypeVector())) { return false; } - for (uint32_t i = 0; i < builtinModuleFunc.params.size(); i++) { + const FuncType* funcType = builtinModuleFunc.funcType(); + for (uint32_t i = 0; i < funcType->args().length(); i++) { if (!encoder.writeOp(Op::LocalGet) || !encoder.writeVarU32(i)) { return false; } @@ -145,11 +174,11 @@ bool CompileBuiltinModule(JSContext* cx, for (uint32_t funcIndex = 0; funcIndex < ids.size(); funcIndex++) { const BuiltinModuleFuncId& id = ids[funcIndex]; const BuiltinModuleFunc& builtinModuleFunc = - BuiltinModuleFunc::getFromId(id); + BuiltinModuleFuncs::getFromId(id); - FuncType type; - if (!builtinModuleFunc.funcType(&type) || - !moduleEnv.types->addType(std::move(type))) { + SharedRecGroup recGroup = builtinModuleFunc.recGroup(); + MOZ_ASSERT(recGroup->numTypes() == 1); + if (!moduleEnv.types->addRecGroup(recGroup)) { ReportOutOfMemory(cx); return false; } @@ -170,10 +199,10 @@ bool CompileBuiltinModule(JSContext* cx, // Add (export "$name" (func $i)) declarations. for (uint32_t funcIndex = 0; funcIndex < ids.size(); funcIndex++) { const BuiltinModuleFunc& builtinModuleFunc = - BuiltinModuleFunc::getFromId(ids[funcIndex]); + BuiltinModuleFuncs::getFromId(ids[funcIndex]); CacheableName exportName; - if (!CacheableName::fromUTF8Chars(builtinModuleFunc.exportName, + if (!CacheableName::fromUTF8Chars(builtinModuleFunc.exportName(), &exportName) || !moduleEnv.exports.append(Export(std::move(exportName), funcIndex, DefinitionKind::Function))) { @@ -200,7 +229,7 @@ bool CompileBuiltinModule(JSContext* cx, for (uint32_t funcIndex = 0; funcIndex < ids.size(); funcIndex++) { BuiltinModuleFuncId id = ids[funcIndex]; const BuiltinModuleFunc& builtinModuleFunc = - BuiltinModuleFunc::getFromId(ids[funcIndex]); + BuiltinModuleFuncs::getFromId(ids[funcIndex]); // Compilation may be done using other threads, ModuleGenerator requires // that function bodies live until after finishFuncDefs(). @@ -267,14 +296,16 @@ static BuiltinModuleFuncId IntGemmFuncs[] = { #ifdef ENABLE_WASM_JS_STRING_BUILTINS static BuiltinModuleFuncId JSStringFuncs[] = { - BuiltinModuleFuncId::StringFromWTF16Array, - BuiltinModuleFuncId::StringToWTF16Array, + BuiltinModuleFuncId::StringTest, + BuiltinModuleFuncId::StringCast, + BuiltinModuleFuncId::StringFromCharCodeArray, + BuiltinModuleFuncId::StringIntoCharCodeArray, BuiltinModuleFuncId::StringFromCharCode, BuiltinModuleFuncId::StringFromCodePoint, BuiltinModuleFuncId::StringCharCodeAt, BuiltinModuleFuncId::StringCodePointAt, BuiltinModuleFuncId::StringLength, - BuiltinModuleFuncId::StringConcatenate, + BuiltinModuleFuncId::StringConcat, BuiltinModuleFuncId::StringSubstring, BuiltinModuleFuncId::StringEquals, BuiltinModuleFuncId::StringCompare}; @@ -300,8 +331,8 @@ Maybe<const BuiltinModuleFunc*> wasm::ImportMatchesBuiltinModuleFunc( // Not supported for implicit instantiation yet MOZ_RELEASE_ASSERT(module == BuiltinModuleId::JSString); for (BuiltinModuleFuncId funcId : JSStringFuncs) { - const BuiltinModuleFunc& func = BuiltinModuleFunc::getFromId(funcId); - if (importName == mozilla::MakeStringSpan(func.exportName)) { + const BuiltinModuleFunc& func = BuiltinModuleFuncs::getFromId(funcId); + if (importName == mozilla::MakeStringSpan(func.exportName())) { return Some(&func); } } diff --git a/js/src/wasm/WasmBuiltinModule.h b/js/src/wasm/WasmBuiltinModule.h index 42faffec73..8646e789e6 100644 --- a/js/src/wasm/WasmBuiltinModule.h +++ b/js/src/wasm/WasmBuiltinModule.h @@ -62,25 +62,60 @@ struct MOZ_STACK_CLASS BuiltinModuleInstances { // An builtin module func is a natively implemented function that may be // compiled into a 'builtin module', which may be instantiated with a provided // memory yielding an exported WebAssembly function wrapping the builtin module. -struct BuiltinModuleFunc { +class BuiltinModuleFunc { + private: + SharedRecGroup recGroup_; + const char* exportName_; + const SymbolicAddressSignature* sig_; + bool usesMemory_; + + public: + // Default constructor so this can be used in an EnumeratedArray. + BuiltinModuleFunc() = default; + + // Initialize this builtin. Must only be called once. + [[nodiscard]] bool init(const RefPtr<TypeContext>& types, + mozilla::Span<const ValType> params, + Maybe<ValType> result, bool usesMemory, + const SymbolicAddressSignature* sig, + const char* exportName); + + // The rec group for the function type for this builtin. + const RecGroup* recGroup() const { return recGroup_.get(); } + // The type definition for the function type for this builtin. + const TypeDef* typeDef() const { return &recGroup_->type(0); } + // The function type for this builtin. + const FuncType* funcType() const { return &typeDef()->funcType(); } + // The name of the func as it is exported - const char* exportName; - // The params taken by the func. - mozilla::Span<const ValType> params; - // The optional result returned by the func. - mozilla::Maybe<const ValType> result; - // The signature of the builtin that implements the func - const SymbolicAddressSignature& signature; + const char* exportName() const { return exportName_; } + // The signature of the builtin that implements this function. + const SymbolicAddressSignature* sig() const { return sig_; } // Whether this function takes a pointer to the memory base as a hidden final - // parameter. - bool usesMemory; + // parameter. This parameter will show up in the SymbolicAddressSignature, + // but not the function type. Compilers must pass the memoryBase to the + // function call as the last parameter. + bool usesMemory() const { return usesMemory_; } +}; + +// Static storage for all builtin module funcs in the system. +class BuiltinModuleFuncs { + using Storage = + mozilla::EnumeratedArray<BuiltinModuleFuncId, BuiltinModuleFunc, + size_t(BuiltinModuleFuncId::Limit)>; + Storage funcs_; - // Allocate a FuncType for this func, returning false for OOM - bool funcType(FuncType* type) const; + static BuiltinModuleFuncs* singleton_; + + public: + [[nodiscard]] static bool init(); + static void destroy(); // Get the BuiltinModuleFunc for an BuiltinModuleFuncId. BuiltinModuleFuncId // must be validated. - static const BuiltinModuleFunc& getFromId(BuiltinModuleFuncId id); + static const BuiltinModuleFunc& getFromId(BuiltinModuleFuncId id) { + return singleton_->funcs_[id]; + } }; Maybe<BuiltinModuleId> ImportMatchesBuiltinModule( diff --git a/js/src/wasm/WasmBuiltinModule.yaml b/js/src/wasm/WasmBuiltinModule.yaml index 88c2f5a575..755e0e5e74 100644 --- a/js/src/wasm/WasmBuiltinModule.yaml +++ b/js/src/wasm/WasmBuiltinModule.yaml @@ -12,10 +12,10 @@ entry: Instance::intrI8VecMul export: i8vecmul params: - - 'ValType::i32()' - - 'ValType::i32()' - - 'ValType::i32()' - - 'ValType::i32()' + - 'i32' + - 'i32' + - 'i32' + - 'i32' fail_mode: FailOnNegI32 uses_memory: true @@ -40,12 +40,12 @@ entry: intgemm::IntrI8PrepareB export: int8_prepare_b params: - - 'ValType::i32()' - - 'ValType::f32()' - - 'ValType::f32()' - - 'ValType::i32()' - - 'ValType::i32()' - - 'ValType::i32()' + - 'i32' + - 'f32' + - 'f32' + - 'i32' + - 'i32' + - 'i32' fail_mode: FailOnNegI32 uses_memory: true @@ -65,12 +65,12 @@ entry: intgemm::IntrI8PrepareBFromTransposed export: int8_prepare_b_from_transposed params: - - 'ValType::i32()' - - 'ValType::f32()' - - 'ValType::f32()' - - 'ValType::i32()' - - 'ValType::i32()' - - 'ValType::i32()' + - 'i32' + - 'f32' + - 'f32' + - 'i32' + - 'i32' + - 'i32' fail_mode: FailOnNegI32 uses_memory: true @@ -90,10 +90,10 @@ entry: intgemm::IntrI8PrepareBFromQuantizedTransposed export: int8_prepare_b_from_quantized_transposed params: - - 'ValType::i32()' - - 'ValType::i32()' - - 'ValType::i32()' - - 'ValType::i32()' + - 'i32' + - 'i32' + - 'i32' + - 'i32' fail_mode: FailOnNegI32 uses_memory: true @@ -116,12 +116,12 @@ entry: intgemm::IntrI8PrepareA export: int8_prepare_a params: - - 'ValType::i32()' - - 'ValType::f32()' - - 'ValType::f32()' - - 'ValType::i32()' - - 'ValType::i32()' - - 'ValType::i32()' + - 'i32' + - 'f32' + - 'f32' + - 'i32' + - 'i32' + - 'i32' fail_mode: FailOnNegI32 uses_memory: true @@ -142,15 +142,15 @@ entry: intgemm::IntrI8PrepareBias export: int8_prepare_bias params: - - 'ValType::i32()' - - 'ValType::f32()' - - 'ValType::f32()' - - 'ValType::f32()' - - 'ValType::f32()' - - 'ValType::i32()' - - 'ValType::i32()' - - 'ValType::i32()' - - 'ValType::i32()' + - 'i32' + - 'f32' + - 'f32' + - 'f32' + - 'f32' + - 'i32' + - 'i32' + - 'i32' + - 'i32' fail_mode: FailOnNegI32 uses_memory: true @@ -177,18 +177,18 @@ entry: intgemm::IntrI8MultiplyAndAddBias export: int8_multiply_and_add_bias params: - - 'ValType::i32()' - - 'ValType::f32()' - - 'ValType::f32()' - - 'ValType::i32()' - - 'ValType::f32()' - - 'ValType::f32()' - - 'ValType::i32()' - - 'ValType::f32()' - - 'ValType::i32()' - - 'ValType::i32()' - - 'ValType::i32()' - - 'ValType::i32()' + - 'i32' + - 'f32' + - 'f32' + - 'i32' + - 'f32' + - 'f32' + - 'i32' + - 'f32' + - 'i32' + - 'i32' + - 'i32' + - 'i32' fail_mode: FailOnNegI32 uses_memory: true @@ -206,12 +206,12 @@ entry: intgemm::IntrI8SelectColumnsOfB export: int8_select_columns_of_b params: - - 'ValType::i32()' - - 'ValType::i32()' - - 'ValType::i32()' - - 'ValType::i32()' - - 'ValType::i32()' - - 'ValType::i32()' + - 'i32' + - 'i32' + - 'i32' + - 'i32' + - 'i32' + - 'i32' fail_mode: FailOnNegI32 uses_memory: true @@ -219,31 +219,61 @@ #if defined(ENABLE_WASM_JS_STRING_BUILTINS) -- op: StringFromWTF16Array +- op: StringTest symbolic_address: - name: StringFromWTF16Array + name: StringTest + type: Args_Int32_GeneralGeneral + entry: Instance::stringTest + export: test + params: + - 'externref' + result: 'i32' + fail_mode: Infallible + uses_memory: false + +- op: StringCast + symbolic_address: + name: StringCast + type: Args_General2 + entry: Instance::stringCast + export: cast + params: + - 'externref' + result: + code: 'extern' + nullable: false + fail_mode: FailOnNullPtr + uses_memory: false + +- op: StringFromCharCodeArray + symbolic_address: + name: StringFromCharCodeArray type: Args_General_GeneralGeneralInt32Int32 - entry: Instance::stringFromWTF16Array - export: fromWTF16Array + entry: Instance::stringFromCharCodeArray + export: fromCharCodeArray params: - - 'ValType(RefType::any())' - - 'ValType::i32()' - - 'ValType::i32()' - result: 'ValType(RefType::extern_())' + - type: "StaticTypeDefs::arrayMutI16" + nullable: true + - 'i32' + - 'i32' + result: + code: 'extern' + nullable: false fail_mode: FailOnNullPtr uses_memory: false -- op: StringToWTF16Array +- op: StringIntoCharCodeArray symbolic_address: - name: StringToWTF16Array + name: StringIntoCharCodeArray type: Args_Int32_GeneralGeneralGeneralInt32 - entry: Instance::stringToWTF16Array - export: toWTF16Array + entry: Instance::stringIntoCharCodeArray + export: intoCharCodeArray params: - - 'ValType(RefType::extern_())' - - 'ValType(RefType::any())' - - 'ValType::i32()' - result: 'ValType::i32()' + - 'externref' + - type: "StaticTypeDefs::arrayMutI16" + nullable: true + - 'i32' + result: 'i32' fail_mode: FailOnNegI32 uses_memory: false @@ -254,8 +284,8 @@ entry: Instance::stringFromCharCode export: fromCharCode params: - - 'ValType::i32()' - result: 'ValType(RefType::extern_())' + - 'i32' + result: 'externref' fail_mode: FailOnNullPtr uses_memory: false @@ -266,8 +296,8 @@ entry: Instance::stringFromCodePoint export: fromCodePoint params: - - 'ValType::i32()' - result: 'ValType(RefType::extern_())' + - 'i32' + result: 'externref' fail_mode: FailOnNullPtr uses_memory: false @@ -278,9 +308,9 @@ entry: Instance::stringCharCodeAt export: charCodeAt params: - - 'ValType(RefType::extern_())' - - 'ValType::i32()' - result: 'ValType::i32()' + - 'externref' + - 'i32' + result: 'i32' fail_mode: FailOnNegI32 uses_memory: false @@ -291,9 +321,9 @@ entry: Instance::stringCodePointAt export: codePointAt params: - - 'ValType(RefType::extern_())' - - 'ValType::i32()' - result: 'ValType::i32()' + - 'externref' + - 'i32' + result: 'i32' fail_mode: FailOnNegI32 uses_memory: false @@ -304,21 +334,21 @@ entry: Instance::stringLength export: length params: - - 'ValType(RefType::extern_())' - result: 'ValType::i32()' + - 'externref' + result: 'i32' fail_mode: FailOnNegI32 uses_memory: false -- op: StringConcatenate +- op: StringConcat symbolic_address: - name: StringConcatenate + name: StringConcat type: Args_General3 - entry: Instance::stringConcatenate - export: concatenate + entry: Instance::stringConcat + export: concat params: - - 'ValType(RefType::extern_())' - - 'ValType(RefType::extern_())' - result: 'ValType(RefType::extern_())' + - 'externref' + - 'externref' + result: 'externref' fail_mode: FailOnNullPtr uses_memory: false @@ -329,10 +359,10 @@ entry: Instance::stringSubstring export: substring params: - - 'ValType(RefType::extern_())' - - 'ValType::i32()' - - 'ValType::i32()' - result: 'ValType(RefType::extern_())' + - 'externref' + - 'i32' + - 'i32' + result: 'externref' fail_mode: FailOnNullPtr uses_memory: false @@ -343,9 +373,9 @@ entry: Instance::stringEquals export: equals params: - - 'ValType(RefType::extern_())' - - 'ValType(RefType::extern_())' - result: 'ValType::i32()' + - 'externref' + - 'externref' + result: 'i32' fail_mode: FailOnNegI32 uses_memory: false @@ -356,9 +386,9 @@ entry: Instance::stringCompare export: compare params: - - 'ValType(RefType::extern_())' - - 'ValType(RefType::extern_())' - result: 'ValType::i32()' + - 'externref' + - 'externref' + result: 'i32' fail_mode: FailOnMaxI32 uses_memory: false diff --git a/js/src/wasm/WasmBuiltins.cpp b/js/src/wasm/WasmBuiltins.cpp index 08024c3dfe..7b03494bcd 100644 --- a/js/src/wasm/WasmBuiltins.cpp +++ b/js/src/wasm/WasmBuiltins.cpp @@ -397,9 +397,9 @@ const SymbolicAddressSignature SASigArrayCopy = { #define VISIT_BUILTIN_FUNC(op, export, sa_name, ...) \ const SymbolicAddressSignature SASig##sa_name = { \ SymbolicAddress::sa_name, \ - DECLARE_BUILTIN_MODULE_FUNC_RESULT_SASTYPE_##op, \ + DECLARE_BUILTIN_MODULE_FUNC_RESULT_MIRTYPE_##op, \ DECLARE_BUILTIN_MODULE_FUNC_FAILMODE_##op, \ - DECLARE_BUILTIN_MODULE_FUNC_PARAM_SASTYPES_##op}; + DECLARE_BUILTIN_MODULE_FUNC_PARAM_MIRTYPES_##op}; FOR_EACH_BUILTIN_MODULE_FUNC(VISIT_BUILTIN_FUNC) #undef VISIT_BUILTIN_FUNC @@ -1839,7 +1839,7 @@ using TypedNativeToCodeRangeMap = HashMap<TypedNative, uint32_t, TypedNative, SystemAllocPolicy>; using SymbolicAddressToCodeRangeArray = - EnumeratedArray<SymbolicAddress, SymbolicAddress::Limit, uint32_t>; + EnumeratedArray<SymbolicAddress, uint32_t, size_t(SymbolicAddress::Limit)>; struct BuiltinThunks { uint8_t* codeBase; diff --git a/js/src/wasm/WasmCode.cpp b/js/src/wasm/WasmCode.cpp index b7aaa1869c..7fe2562ab6 100644 --- a/js/src/wasm/WasmCode.cpp +++ b/js/src/wasm/WasmCode.cpp @@ -1085,6 +1085,23 @@ bool Code::lookupTrap(void* pc, Trap* trapOut, BytecodeOffset* bytecode) const { return false; } +bool Code::lookupFunctionTier(const CodeRange* codeRange, Tier* tier) const { + // This logic only works if the codeRange is a function, and therefore only + // exists in metadata and not a lazy stub tier. Generalizing to access lazy + // stubs would require taking a lock, which is undesirable for the profiler. + MOZ_ASSERT(codeRange->isFunction()); + for (Tier t : tiers()) { + const CodeTier& code = codeTier(t); + const MetadataTier& metadata = code.metadata(); + if (codeRange >= metadata.codeRanges.begin() && + codeRange < metadata.codeRanges.end()) { + *tier = t; + return true; + } + } + return false; +} + struct UnwindInfoPCOffset { const CodeRangeUnwindInfoVector& info; explicit UnwindInfoPCOffset(const CodeRangeUnwindInfoVector& info) diff --git a/js/src/wasm/WasmCode.h b/js/src/wasm/WasmCode.h index a34a462127..e03a2f596e 100644 --- a/js/src/wasm/WasmCode.h +++ b/js/src/wasm/WasmCode.h @@ -117,8 +117,8 @@ struct LinkData : LinkDataCacheablePod { }; using InternalLinkVector = Vector<InternalLink, 0, SystemAllocPolicy>; - struct SymbolicLinkArray - : EnumeratedArray<SymbolicAddress, SymbolicAddress::Limit, Uint32Vector> { + struct SymbolicLinkArray : EnumeratedArray<SymbolicAddress, Uint32Vector, + size_t(SymbolicAddress::Limit)> { size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const; }; @@ -857,6 +857,7 @@ class Code : public ShareableBase<Code> { bool containsCodePC(const void* pc) const; bool lookupTrap(void* pc, Trap* trap, BytecodeOffset* bytecode) const; const CodeRangeUnwindInfo* lookupUnwindInfo(void* pc) const; + bool lookupFunctionTier(const CodeRange* codeRange, Tier* tier) const; // To save memory, profilingLabels_ are generated lazily when profiling mode // is enabled. diff --git a/js/src/wasm/WasmCodegenTypes.h b/js/src/wasm/WasmCodegenTypes.h index 815292dd23..590572ae8a 100644 --- a/js/src/wasm/WasmCodegenTypes.h +++ b/js/src/wasm/WasmCodegenTypes.h @@ -273,7 +273,7 @@ WASM_DECLARE_CACHEABLE_POD(TrapSite); WASM_DECLARE_POD_VECTOR(TrapSite, TrapSiteVector) struct TrapSiteVectorArray - : EnumeratedArray<Trap, Trap::Limit, TrapSiteVector> { + : EnumeratedArray<Trap, TrapSiteVector, size_t(Trap::Limit)> { bool empty() const; void clear(); void swap(TrapSiteVectorArray& rhs); diff --git a/js/src/wasm/WasmCompile.cpp b/js/src/wasm/WasmCompile.cpp index 2418340684..fbf4df3e71 100644 --- a/js/src/wasm/WasmCompile.cpp +++ b/js/src/wasm/WasmCompile.cpp @@ -726,8 +726,8 @@ void CompilerEnvironment::computeParameters(Decoder& d) { state_ = Computed; } -template <class DecoderT> -static bool DecodeFunctionBody(DecoderT& d, ModuleGenerator& mg, +template <class DecoderT, class ModuleGeneratorT> +static bool DecodeFunctionBody(DecoderT& d, ModuleGeneratorT& mg, uint32_t funcIndex) { uint32_t bodySize; if (!d.readVarU32(&bodySize)) { @@ -751,9 +751,9 @@ static bool DecodeFunctionBody(DecoderT& d, ModuleGenerator& mg, bodyBegin + bodySize); } -template <class DecoderT> +template <class DecoderT, class ModuleGeneratorT> static bool DecodeCodeSection(const ModuleEnvironment& env, DecoderT& d, - ModuleGenerator& mg) { + ModuleGeneratorT& mg) { if (!env.codeSection) { if (env.numFuncDefs() != 0) { return d.fail("expected code section"); @@ -996,3 +996,46 @@ SharedModule wasm::CompileStreaming( return mg.finishModule(*bytecode, streamEnd.tier2Listener); } + +class DumpIonModuleGenerator { + private: + ModuleEnvironment& moduleEnv_; + uint32_t targetFuncIndex_; + IonDumpContents contents_; + GenericPrinter& out_; + UniqueChars* error_; + + public: + DumpIonModuleGenerator(ModuleEnvironment& moduleEnv, uint32_t targetFuncIndex, + IonDumpContents contents, GenericPrinter& out, + UniqueChars* error) + : moduleEnv_(moduleEnv), + targetFuncIndex_(targetFuncIndex), + contents_(contents), + out_(out), + error_(error) {} + + bool finishFuncDefs() { return true; } + bool compileFuncDef(uint32_t funcIndex, uint32_t lineOrBytecode, + const uint8_t* begin, const uint8_t* end) { + if (funcIndex != targetFuncIndex_) { + return true; + } + + FuncCompileInput input(funcIndex, lineOrBytecode, begin, end, + Uint32Vector()); + return IonDumpFunction(moduleEnv_, input, contents_, out_, error_); + } +}; + +bool wasm::DumpIonFunctionInModule(const ShareableBytes& bytecode, + uint32_t targetFuncIndex, + IonDumpContents contents, + GenericPrinter& out, UniqueChars* error) { + UniqueCharsVector warnings; + Decoder d(bytecode.bytes, 0, error, &warnings); + ModuleEnvironment moduleEnv(FeatureArgs::allEnabled()); + DumpIonModuleGenerator mg(moduleEnv, targetFuncIndex, contents, out, error); + return moduleEnv.init() && DecodeModuleEnvironment(d, &moduleEnv) && + DecodeCodeSection(moduleEnv, d, mg); +} diff --git a/js/src/wasm/WasmCompile.h b/js/src/wasm/WasmCompile.h index 2b07881eea..f39dc09fb9 100644 --- a/js/src/wasm/WasmCompile.h +++ b/js/src/wasm/WasmCompile.h @@ -93,6 +93,19 @@ SharedModule CompileStreaming(const CompileArgs& args, const Bytes& envBytes, const Atomic<bool>& cancelled, UniqueChars* error, UniqueCharsVector* warnings); +// What to print out from dumping a function from Ion. +enum class IonDumpContents { + UnoptimizedMIR, + OptimizedMIR, + LIR, + + Default = UnoptimizedMIR, +}; + +bool DumpIonFunctionInModule(const ShareableBytes& bytecode, + uint32_t targetFuncIndex, IonDumpContents contents, + GenericPrinter& out, UniqueChars* error); + } // namespace wasm } // namespace js diff --git a/js/src/wasm/WasmCompileArgs.h b/js/src/wasm/WasmCompileArgs.h index 1bf9e60d13..af85026b93 100644 --- a/js/src/wasm/WasmCompileArgs.h +++ b/js/src/wasm/WasmCompileArgs.h @@ -103,6 +103,15 @@ struct FeatureArgs { FeatureArgs(FeatureArgs&&) = default; static FeatureArgs build(JSContext* cx, const FeatureOptions& options); + static FeatureArgs allEnabled() { + FeatureArgs args; +#define WASM_FEATURE(NAME, LOWER_NAME, ...) args.LOWER_NAME = true; + JS_FOR_WASM_FEATURES(WASM_FEATURE) +#undef WASM_FEATURE + args.sharedMemory = Shareable::True; + args.simd = true; + return args; + } #define WASM_FEATURE(NAME, LOWER_NAME, ...) bool LOWER_NAME; JS_FOR_WASM_FEATURES(WASM_FEATURE) diff --git a/js/src/wasm/WasmFeatures.cpp b/js/src/wasm/WasmFeatures.cpp index 05804353ae..24ab1c7d51 100644 --- a/js/src/wasm/WasmFeatures.cpp +++ b/js/src/wasm/WasmFeatures.cpp @@ -21,6 +21,7 @@ #include "jit/AtomicOperations.h" #include "jit/JitContext.h" #include "jit/JitOptions.h" +#include "js/Prefs.h" #include "util/StringBuffer.h" #include "vm/JSContext.h" #include "vm/Realm.h" @@ -56,13 +57,13 @@ static inline bool WasmThreadsFlag(JSContext* cx) { JS_FOR_WASM_FEATURES(WASM_FEATURE); #undef WASM_FEATURE -#define WASM_FEATURE(NAME, LOWER_NAME, STAGE, COMPILE_PRED, COMPILER_PRED, \ - FLAG_PRED, FLAG_FORCE_ON, ...) \ - static inline bool Wasm##NAME##Flag(JSContext* cx) { \ - if (!(COMPILE_PRED)) { \ - return false; \ - } \ - return ((FLAG_PRED) && cx->options().wasm##NAME()) || (FLAG_FORCE_ON); \ +#define WASM_FEATURE(NAME, LOWER_NAME, COMPILE_PRED, COMPILER_PRED, FLAG_PRED, \ + FLAG_FORCE_ON, FLAG_FUZZ_ON, PREF) \ + static inline bool Wasm##NAME##Flag(JSContext* cx) { \ + if (!(COMPILE_PRED)) { \ + return false; \ + } \ + return ((FLAG_PRED) && JS::Prefs::wasm_##PREF()) || (FLAG_FORCE_ON); \ } JS_FOR_WASM_FEATURES(WASM_FEATURE); #undef WASM_FEATURE @@ -219,10 +220,9 @@ bool wasm::AnyCompilerAvailable(JSContext* cx) { // compiler that can support the feature. Subsequent compiler selection must // ensure that only compilers that actually support the feature are used. -#define WASM_FEATURE(NAME, LOWER_NAME, STAGE, COMPILE_PRED, COMPILER_PRED, \ - ...) \ - bool wasm::NAME##Available(JSContext* cx) { \ - return Wasm##NAME##Flag(cx) && (COMPILER_PRED); \ +#define WASM_FEATURE(NAME, LOWER_NAME, COMPILE_PRED, COMPILER_PRED, ...) \ + bool wasm::NAME##Available(JSContext* cx) { \ + return Wasm##NAME##Flag(cx) && (COMPILER_PRED); \ } JS_FOR_WASM_FEATURES(WASM_FEATURE) #undef WASM_FEATURE diff --git a/js/src/wasm/WasmFrameIter.cpp b/js/src/wasm/WasmFrameIter.cpp index 171ac285be..90555720da 100644 --- a/js/src/wasm/WasmFrameIter.cpp +++ b/js/src/wasm/WasmFrameIter.cpp @@ -1882,3 +1882,16 @@ const char* ProfilingFrameIterator::label() const { MOZ_CRASH("bad code range kind"); } + +ProfilingFrameIterator::Category ProfilingFrameIterator::category() const { + if (!exitReason_.isFixed() || !exitReason_.isNone() || + !codeRange_->isFunction()) { + return Category::Other; + } + + Tier tier; + if (!code_->lookupFunctionTier(codeRange_, &tier)) { + return Category::Other; + } + return tier == Tier::Optimized ? Category::Ion : Category::Baseline; +} diff --git a/js/src/wasm/WasmFrameIter.h b/js/src/wasm/WasmFrameIter.h index 014f5de0ef..59590b1b2a 100644 --- a/js/src/wasm/WasmFrameIter.h +++ b/js/src/wasm/WasmFrameIter.h @@ -196,6 +196,12 @@ class ProfilingFrameIterator { ProfilingFrameIterator(const jit::JitActivation& activation, const RegisterState& state); + enum Category { + Baseline, + Ion, + Other, + }; + void operator++(); bool done() const { @@ -213,6 +219,8 @@ class ProfilingFrameIterator { } const char* label() const; + Category category() const; + void* endStackAddress() const { return endStackAddress_; } }; diff --git a/js/src/wasm/WasmGcObject-inl.h b/js/src/wasm/WasmGcObject-inl.h index 17800f41f1..4714aafc06 100644 --- a/js/src/wasm/WasmGcObject-inl.h +++ b/js/src/wasm/WasmGcObject-inl.h @@ -342,8 +342,7 @@ MOZ_ALWAYS_INLINE WasmArrayObject* WasmArrayObject::createArray( calcStorageBytesChecked(typeDefData->arrayElemSize, numElements); if (!storageBytes.isValid() || storageBytes.value() > uint32_t(wasm::MaxArrayPayloadBytes)) { - JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, - JSMSG_WASM_ARRAY_IMP_LIMIT); + wasm::ReportTrapError(cx, JSMSG_WASM_ARRAY_IMP_LIMIT); return nullptr; } diff --git a/js/src/wasm/WasmGenerator.cpp b/js/src/wasm/WasmGenerator.cpp index a470626de4..338812e1d6 100644 --- a/js/src/wasm/WasmGenerator.cpp +++ b/js/src/wasm/WasmGenerator.cpp @@ -440,7 +440,7 @@ static bool InRange(uint32_t caller, uint32_t callee) { using OffsetMap = HashMap<uint32_t, uint32_t, DefaultHasher<uint32_t>, SystemAllocPolicy>; using TrapMaybeOffsetArray = - EnumeratedArray<Trap, Trap::Limit, Maybe<uint32_t>>; + EnumeratedArray<Trap, Maybe<uint32_t>, size_t(Trap::Limit)>; bool ModuleGenerator::linkCallSites() { AutoCreatedBy acb(masm_, "linkCallSites"); diff --git a/js/src/wasm/WasmInitExpr.cpp b/js/src/wasm/WasmInitExpr.cpp index e8c49cbc31..581eca2f62 100644 --- a/js/src/wasm/WasmInitExpr.cpp +++ b/js/src/wasm/WasmInitExpr.cpp @@ -74,20 +74,16 @@ class MOZ_STACK_CLASS InitExprInterpreter { return stack.append(Val(RefType::func(), ref)); } -#if defined(ENABLE_WASM_EXTENDED_CONST) || defined(ENABLE_WASM_GC) int32_t popI32() { uint32_t result = stack.back().i32(); stack.popBack(); return int32_t(result); } -#endif -#ifdef ENABLE_WASM_EXTENDED_CONST int64_t popI64() { uint64_t result = stack.back().i64(); stack.popBack(); return int64_t(result); } -#endif bool evalGlobalGet(JSContext* cx, uint32_t index) { RootedVal val(cx); @@ -107,7 +103,6 @@ class MOZ_STACK_CLASS InitExprInterpreter { return pushFuncRef(func); } bool evalRefNull(RefType type) { return pushRef(type, AnyRef::null()); } -#ifdef ENABLE_WASM_EXTENDED_CONST bool evalI32Add() { uint32_t b = popI32(); uint32_t a = popI32(); @@ -138,7 +133,6 @@ class MOZ_STACK_CLASS InitExprInterpreter { uint64_t a = popI64(); return pushI64(a * b); } -#endif // ENABLE_WASM_EXTENDED_CONST #ifdef ENABLE_WASM_GC bool evalStructNew(JSContext* cx, uint32_t typeIndex) { const TypeDef& typeDef = instance().metadata().types->type(typeIndex); @@ -320,7 +314,6 @@ bool InitExprInterpreter::evaluate(JSContext* cx, Decoder& d) { } CHECK(evalRefNull(type)); } -#ifdef ENABLE_WASM_EXTENDED_CONST case uint16_t(Op::I32Add): { if (!d.readBinary()) { return false; @@ -357,7 +350,6 @@ bool InitExprInterpreter::evaluate(JSContext* cx, Decoder& d) { } CHECK(evalI64Mul()); } -#endif #ifdef ENABLE_WASM_GC case uint16_t(Op::GcPrefix): { switch (op.b1) { @@ -449,9 +441,7 @@ bool wasm::DecodeConstantExpression(Decoder& d, ModuleEnvironment* env, return false; } -#if defined(ENABLE_WASM_EXTENDED_CONST) || defined(ENABLE_WASM_GC) Nothing nothing; -#endif NothingVector nothings{}; ResultType unusedType; @@ -542,13 +532,9 @@ bool wasm::DecodeConstantExpression(Decoder& d, ModuleEnvironment* env, *literal = Some(LitVal(ValType(type))); break; } -#ifdef ENABLE_WASM_EXTENDED_CONST case uint16_t(Op::I32Add): case uint16_t(Op::I32Sub): case uint16_t(Op::I32Mul): { - if (!env->extendedConstEnabled()) { - return iter.unrecognizedOpcode(&op); - } if (!iter.readBinary(ValType::I32, ¬hing, ¬hing)) { return false; } @@ -558,16 +544,12 @@ bool wasm::DecodeConstantExpression(Decoder& d, ModuleEnvironment* env, case uint16_t(Op::I64Add): case uint16_t(Op::I64Sub): case uint16_t(Op::I64Mul): { - if (!env->extendedConstEnabled()) { - return iter.unrecognizedOpcode(&op); - } if (!iter.readBinary(ValType::I64, ¬hing, ¬hing)) { return false; } *literal = Nothing(); break; } -#endif #ifdef ENABLE_WASM_GC case uint16_t(Op::GcPrefix): { if (!env->gcEnabled()) { @@ -663,6 +645,7 @@ bool InitExpr::decodeAndValidate(Decoder& d, ModuleEnvironment* env, expr->type_ = expected; if (literal) { + literal->unsafeSetType(expected); expr->kind_ = InitExprKind::Literal; expr->literal_ = *literal; return true; diff --git a/js/src/wasm/WasmInstance.cpp b/js/src/wasm/WasmInstance.cpp index bf25b58c14..d025c02c16 100644 --- a/js/src/wasm/WasmInstance.cpp +++ b/js/src/wasm/WasmInstance.cpp @@ -1531,8 +1531,10 @@ static bool ArrayCopyFromData(JSContext* cx, Handle<WasmArrayObject*> arrayObj, // Because `numBytesToCopy` is an in-range `CheckedUint32`, the cast to // `size_t` is safe even on a 32-bit target. - memcpy(arrayObj->data_, &seg->bytes[segByteOffset], - size_t(numBytesToCopy.value())); + if (numElements != 0) { + memcpy(arrayObj->data_, &seg->bytes[segByteOffset], + size_t(numBytesToCopy.value())); + } return true; } @@ -1948,35 +1950,42 @@ static bool ArrayCopyFromElem(JSContext* cx, Handle<WasmArrayObject*> arrayObj, // take into account the enclosing recursion group of the type. This is // temporary until builtin module functions can specify a precise array type // for params/results. -static WasmArrayObject* CastToI16Array(HandleAnyRef ref, bool needMutable) { - if (!ref.isJSObject()) { - return nullptr; - } +template <bool isMutable> +static WasmArrayObject* UncheckedCastToArrayI16(HandleAnyRef ref) { JSObject& object = ref.toJSObject(); - if (!object.is<WasmArrayObject>()) { - return nullptr; - } WasmArrayObject& array = object.as<WasmArrayObject>(); - const ArrayType& type = array.typeDef().arrayType(); - if (type.elementType_ != StorageType::I16) { - return nullptr; + DebugOnly<const ArrayType*> type(&array.typeDef().arrayType()); + MOZ_ASSERT(type->elementType_ == StorageType::I16); + MOZ_ASSERT(type->isMutable_ == isMutable); + return &array; +} + +/* static */ +int32_t Instance::stringTest(Instance* instance, void* stringArg) { + AnyRef string = AnyRef::fromCompiledCode(stringArg); + if (string.isNull() || !string.isJSString()) { + return 0; } - if (needMutable && !type.isMutable_) { + return 1; +} + +/* static */ +void* Instance::stringCast(Instance* instance, void* stringArg) { + AnyRef string = AnyRef::fromCompiledCode(stringArg); + if (string.isNull() || !string.isJSString()) { + ReportTrapError(instance->cx(), JSMSG_WASM_BAD_CAST); return nullptr; } - return &array; + return string.forCompiledCode(); } /* static */ -void* Instance::stringFromWTF16Array(Instance* instance, void* arrayArg, - uint32_t arrayStart, uint32_t arrayCount) { +void* Instance::stringFromCharCodeArray(Instance* instance, void* arrayArg, + uint32_t arrayStart, + uint32_t arrayCount) { JSContext* cx = instance->cx(); RootedAnyRef arrayRef(cx, AnyRef::fromCompiledCode(arrayArg)); - Rooted<WasmArrayObject*> array(cx); - if (!(array = CastToI16Array(arrayRef, false))) { - ReportTrapError(cx, JSMSG_WASM_BAD_CAST); - return nullptr; - } + Rooted<WasmArrayObject*> array(cx, UncheckedCastToArrayI16<true>(arrayRef)); CheckedUint32 lastIndexPlus1 = CheckedUint32(arrayStart) + CheckedUint32(arrayCount); @@ -1997,8 +2006,8 @@ void* Instance::stringFromWTF16Array(Instance* instance, void* arrayArg, } /* static */ -int32_t Instance::stringToWTF16Array(Instance* instance, void* stringArg, - void* arrayArg, uint32_t arrayStart) { +int32_t Instance::stringIntoCharCodeArray(Instance* instance, void* stringArg, + void* arrayArg, uint32_t arrayStart) { JSContext* cx = instance->cx(); AnyRef stringRef = AnyRef::fromCompiledCode(stringArg); if (!stringRef.isJSString()) { @@ -2009,11 +2018,7 @@ int32_t Instance::stringToWTF16Array(Instance* instance, void* stringArg, size_t stringLength = string->length(); RootedAnyRef arrayRef(cx, AnyRef::fromCompiledCode(arrayArg)); - Rooted<WasmArrayObject*> array(cx); - if (!(array = CastToI16Array(arrayRef, true))) { - ReportTrapError(cx, JSMSG_WASM_BAD_CAST); - return -1; - } + Rooted<WasmArrayObject*> array(cx, UncheckedCastToArrayI16<true>(arrayRef)); CheckedUint32 lastIndexPlus1 = CheckedUint32(arrayStart) + stringLength; if (!lastIndexPlus1.isValid() || @@ -2120,8 +2125,8 @@ int32_t Instance::stringLength(Instance* instance, void* stringArg) { return (int32_t)stringRef.toJSString()->length(); } -void* Instance::stringConcatenate(Instance* instance, void* firstStringArg, - void* secondStringArg) { +void* Instance::stringConcat(Instance* instance, void* firstStringArg, + void* secondStringArg) { JSContext* cx = instance->cx(); AnyRef firstStringRef = AnyRef::fromCompiledCode(firstStringArg); @@ -2444,11 +2449,10 @@ bool Instance::init(JSContext* cx, const JSObjectVector& funcImports, if (global.isIndirect()) { // Initialize the cell - wasm::GCPtrVal& cell = globalObjs[i]->val(); - cell = val.get(); + globalObjs[i]->setVal(val); + // Link to the cell - void* address = (void*)&cell.get().cell(); - *(void**)globalAddr = address; + *(void**)globalAddr = globalObjs[i]->addressOfCell(); } else { val.get().writeToHeapLocation(globalAddr); } @@ -2539,6 +2543,7 @@ bool Instance::init(JSContext* cx, const JSObjectVector& funcImports, size_t numWords = std::max<size_t>((numFuncs + 31) / 32, 1); debugFilter_ = (uint32_t*)js_calloc(numWords, sizeof(uint32_t)); if (!debugFilter_) { + ReportOutOfMemory(cx); return false; } } @@ -2552,6 +2557,7 @@ bool Instance::init(JSContext* cx, const JSObjectVector& funcImports, // Take references to the passive data segments if (!passiveDataSegments_.resize(dataSegments.length())) { + ReportOutOfMemory(cx); return false; } for (size_t i = 0; i < dataSegments.length(); i++) { @@ -2563,6 +2569,7 @@ bool Instance::init(JSContext* cx, const JSObjectVector& funcImports, // Create InstanceElemSegments for any passive element segments, since these // are the ones available at runtime. if (!passiveElemSegments_.resize(elemSegments.length())) { + ReportOutOfMemory(cx); return false; } for (size_t i = 0; i < elemSegments.length(); i++) { @@ -2571,6 +2578,7 @@ bool Instance::init(JSContext* cx, const JSObjectVector& funcImports, passiveElemSegments_[i] = InstanceElemSegment(); InstanceElemSegment& instanceSeg = passiveElemSegments_[i]; if (!instanceSeg.reserve(seg.numElements())) { + ReportOutOfMemory(cx); return false; } diff --git a/js/src/wasm/WasmInstance.h b/js/src/wasm/WasmInstance.h index dcc586b14f..074c6212df 100644 --- a/js/src/wasm/WasmInstance.h +++ b/js/src/wasm/WasmInstance.h @@ -571,10 +571,13 @@ class alignas(16) Instance { static int32_t intrI8VecMul(Instance* instance, uint32_t dest, uint32_t src1, uint32_t src2, uint32_t len, uint8_t* memBase); - static void* stringFromWTF16Array(Instance* instance, void* arrayArg, - uint32_t arrayStart, uint32_t arrayCount); - static int32_t stringToWTF16Array(Instance* instance, void* stringArg, - void* arrayArg, uint32_t start); + static int32_t stringTest(Instance* instance, void* stringArg); + static void* stringCast(Instance* instance, void* stringArg); + static void* stringFromCharCodeArray(Instance* instance, void* arrayArg, + uint32_t arrayStart, + uint32_t arrayCount); + static int32_t stringIntoCharCodeArray(Instance* instance, void* stringArg, + void* arrayArg, uint32_t arrayStart); static void* stringFromCharCode(Instance* instance, uint32_t charCode); static void* stringFromCodePoint(Instance* instance, uint32_t codePoint); static int32_t stringCharCodeAt(Instance* instance, void* stringArg, @@ -582,8 +585,8 @@ class alignas(16) Instance { static int32_t stringCodePointAt(Instance* instance, void* stringArg, uint32_t index); static int32_t stringLength(Instance* instance, void* stringArg); - static void* stringConcatenate(Instance* instance, void* firstStringArg, - void* secondStringArg); + static void* stringConcat(Instance* instance, void* firstStringArg, + void* secondStringArg); static void* stringSubstring(Instance* instance, void* stringArg, int32_t startIndex, int32_t endIndex); static int32_t stringEquals(Instance* instance, void* firstStringArg, diff --git a/js/src/wasm/WasmIonCompile.cpp b/js/src/wasm/WasmIonCompile.cpp index 6fbfeb3809..0568a95804 100644 --- a/js/src/wasm/WasmIonCompile.cpp +++ b/js/src/wasm/WasmIonCompile.cpp @@ -900,7 +900,7 @@ class FunctionCompiler { return true; } -#ifdef ENABLE_WASM_FUNCTION_REFERENCES +#ifdef ENABLE_WASM_GC [[nodiscard]] bool brOnNull(uint32_t relativeDepth, const DefVector& values, const ResultType& type, MDefinition* condition) { if (inDeadCode()) { @@ -963,7 +963,7 @@ class FunctionCompiler { return true; } -#endif // ENABLE_WASM_FUNCTION_REFERENCES +#endif // ENABLE_WASM_GC #ifdef ENABLE_WASM_GC MDefinition* refI31(MDefinition* input) { @@ -2006,10 +2006,10 @@ class FunctionCompiler { MOZ_CRASH("Unknown ABIArg kind."); } - template <typename SpanT> - [[nodiscard]] bool passArgs(const DefVector& argDefs, SpanT types, + template <typename VecT> + [[nodiscard]] bool passArgs(const DefVector& argDefs, const VecT& types, CallCompileState* call) { - MOZ_ASSERT(argDefs.length() == types.size()); + MOZ_ASSERT(argDefs.length() == types.length()); for (uint32_t i = 0; i < argDefs.length(); i++) { MDefinition* def = argDefs[i]; ValType type = types[i]; @@ -2447,7 +2447,7 @@ class FunctionCompiler { return collectUnaryCallResult(builtin.retType, def); } -#ifdef ENABLE_WASM_FUNCTION_REFERENCES +#ifdef ENABLE_WASM_GC [[nodiscard]] bool callRef(const FuncType& funcType, MDefinition* ref, uint32_t lineOrBytecode, const CallCompileState& call, DefVector* results) { @@ -2489,7 +2489,7 @@ class FunctionCompiler { # endif // ENABLE_WASM_TAIL_CALLS -#endif // ENABLE_WASM_FUNCTION_REFERENCES +#endif // ENABLE_WASM_GC /*********************************************** Control flow generation */ @@ -2788,7 +2788,8 @@ class FunctionCompiler { // patches around. for (uint32_t depth = 0; depth < iter().controlStackDepth(); depth++) { LabelKind kind = iter().controlKind(depth); - if (kind != LabelKind::Try && kind != LabelKind::Body) { + if (kind != LabelKind::Try && kind != LabelKind::TryTable && + kind != LabelKind::Body) { continue; } Control& control = iter().controlItem(depth); @@ -5440,7 +5441,7 @@ static bool EmitReturnCallIndirect(FunctionCompiler& f) { } #endif -#if defined(ENABLE_WASM_TAIL_CALLS) && defined(ENABLE_WASM_FUNCTION_REFERENCES) +#if defined(ENABLE_WASM_TAIL_CALLS) && defined(ENABLE_WASM_GC) static bool EmitReturnCallRef(FunctionCompiler& f) { uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode(); @@ -7090,7 +7091,11 @@ static bool EmitLoadSplatSimd128(FunctionCompiler& f, Scalar::Type viewType, return false; } - f.iter().setResult(f.loadSplatSimd128(viewType, addr, splatOp)); + auto* ins = f.loadSplatSimd128(viewType, addr, splatOp); + if (!f.inDeadCode() && !ins) { + return false; + } + f.iter().setResult(ins); return true; } @@ -7100,7 +7105,11 @@ static bool EmitLoadExtendSimd128(FunctionCompiler& f, wasm::SimdOp op) { return false; } - f.iter().setResult(f.loadExtendSimd128(addr, op)); + auto* ins = f.loadExtendSimd128(addr, op); + if (!f.inDeadCode() && !ins) { + return false; + } + f.iter().setResult(ins); return true; } @@ -7111,7 +7120,11 @@ static bool EmitLoadZeroSimd128(FunctionCompiler& f, Scalar::Type viewType, return false; } - f.iter().setResult(f.loadZeroSimd128(viewType, numBytes, addr)); + auto* ins = f.loadZeroSimd128(viewType, numBytes, addr); + if (!f.inDeadCode() && !ins) { + return false; + } + f.iter().setResult(ins); return true; } @@ -7123,7 +7136,11 @@ static bool EmitLoadLaneSimd128(FunctionCompiler& f, uint32_t laneSize) { return false; } - f.iter().setResult(f.loadLaneSimd128(laneSize, addr, laneIndex, src)); + auto* ins = f.loadLaneSimd128(laneSize, addr, laneIndex, src); + if (!f.inDeadCode() && !ins) { + return false; + } + f.iter().setResult(ins); return true; } @@ -7141,7 +7158,7 @@ static bool EmitStoreLaneSimd128(FunctionCompiler& f, uint32_t laneSize) { #endif // ENABLE_WASM_SIMD -#ifdef ENABLE_WASM_FUNCTION_REFERENCES +#ifdef ENABLE_WASM_GC static bool EmitRefAsNonNull(FunctionCompiler& f) { MDefinition* ref; if (!f.iter().readRefAsNonNull(&ref)) { @@ -7204,7 +7221,7 @@ static bool EmitCallRef(FunctionCompiler& f) { return true; } -#endif // ENABLE_WASM_FUNCTION_REFERENCES +#endif // ENABLE_WASM_GC #ifdef ENABLE_WASM_GC @@ -7917,18 +7934,18 @@ static bool EmitCallBuiltinModuleFunc(FunctionCompiler& f) { } uint32_t bytecodeOffset = f.readBytecodeOffset(); - const SymbolicAddressSignature& callee = builtinModuleFunc->signature; + const SymbolicAddressSignature& callee = *builtinModuleFunc->sig(); CallCompileState args; if (!f.passInstance(callee.argTypes[0], &args)) { return false; } - if (!f.passArgs(params, builtinModuleFunc->params, &args)) { + if (!f.passArgs(params, builtinModuleFunc->funcType()->args(), &args)) { return false; } - if (builtinModuleFunc->usesMemory) { + if (builtinModuleFunc->usesMemory()) { MDefinition* memoryBase = f.memoryBase(0); if (!f.passArg(memoryBase, MIRType::Pointer, &args)) { return false; @@ -7939,7 +7956,7 @@ static bool EmitCallBuiltinModuleFunc(FunctionCompiler& f) { return false; } - bool hasResult = builtinModuleFunc->result.isSome(); + bool hasResult = !builtinModuleFunc->funcType()->results().empty(); MDefinition* result = nullptr; MDefinition** resultOutParam = hasResult ? &result : nullptr; if (!f.builtinInstanceMethodCall(callee, bytecodeOffset, args, @@ -7996,37 +8013,19 @@ static bool EmitBodyExprs(FunctionCompiler& f) { case uint16_t(Op::Else): CHECK(EmitElse(f)); case uint16_t(Op::Try): - if (!f.moduleEnv().exceptionsEnabled()) { - return f.iter().unrecognizedOpcode(&op); - } CHECK(EmitTry(f)); case uint16_t(Op::Catch): - if (!f.moduleEnv().exceptionsEnabled()) { - return f.iter().unrecognizedOpcode(&op); - } CHECK(EmitCatch(f)); case uint16_t(Op::CatchAll): - if (!f.moduleEnv().exceptionsEnabled()) { - return f.iter().unrecognizedOpcode(&op); - } CHECK(EmitCatchAll(f)); case uint16_t(Op::Delegate): - if (!f.moduleEnv().exceptionsEnabled()) { - return f.iter().unrecognizedOpcode(&op); - } if (!EmitDelegate(f)) { return false; } break; case uint16_t(Op::Throw): - if (!f.moduleEnv().exceptionsEnabled()) { - return f.iter().unrecognizedOpcode(&op); - } CHECK(EmitThrow(f)); case uint16_t(Op::Rethrow): - if (!f.moduleEnv().exceptionsEnabled()) { - return f.iter().unrecognizedOpcode(&op); - } CHECK(EmitRethrow(f)); case uint16_t(Op::ThrowRef): if (!f.moduleEnv().exnrefEnabled()) { @@ -8474,36 +8473,35 @@ static bool EmitBodyExprs(FunctionCompiler& f) { } #endif -#ifdef ENABLE_WASM_FUNCTION_REFERENCES +#ifdef ENABLE_WASM_GC case uint16_t(Op::RefAsNonNull): - if (!f.moduleEnv().functionReferencesEnabled()) { + if (!f.moduleEnv().gcEnabled()) { return f.iter().unrecognizedOpcode(&op); } CHECK(EmitRefAsNonNull(f)); case uint16_t(Op::BrOnNull): { - if (!f.moduleEnv().functionReferencesEnabled()) { + if (!f.moduleEnv().gcEnabled()) { return f.iter().unrecognizedOpcode(&op); } CHECK(EmitBrOnNull(f)); } case uint16_t(Op::BrOnNonNull): { - if (!f.moduleEnv().functionReferencesEnabled()) { + if (!f.moduleEnv().gcEnabled()) { return f.iter().unrecognizedOpcode(&op); } CHECK(EmitBrOnNonNull(f)); } case uint16_t(Op::CallRef): { - if (!f.moduleEnv().functionReferencesEnabled()) { + if (!f.moduleEnv().gcEnabled()) { return f.iter().unrecognizedOpcode(&op); } CHECK(EmitCallRef(f)); } #endif -#if defined(ENABLE_WASM_TAIL_CALLS) && defined(ENABLE_WASM_FUNCTION_REFERENCES) +#if defined(ENABLE_WASM_TAIL_CALLS) && defined(ENABLE_WASM_GC) case uint16_t(Op::ReturnCallRef): { - if (!f.moduleEnv().functionReferencesEnabled() || - !f.moduleEnv().tailCallsEnabled()) { + if (!f.moduleEnv().gcEnabled() || !f.moduleEnv().tailCallsEnabled()) { return f.iter().unrecognizedOpcode(&op); } CHECK(EmitReturnCallRef(f)); @@ -9025,114 +9023,91 @@ static bool EmitBodyExprs(FunctionCompiler& f) { CHECK(EmitAtomicStore(f, ValType::I64, Scalar::Uint32)); case uint32_t(ThreadOp::I32AtomicAdd): - CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Int32, - AtomicFetchAddOp)); + CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Int32, AtomicOp::Add)); case uint32_t(ThreadOp::I64AtomicAdd): - CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Int64, - AtomicFetchAddOp)); + CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Int64, AtomicOp::Add)); case uint32_t(ThreadOp::I32AtomicAdd8U): - CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Uint8, - AtomicFetchAddOp)); + CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Uint8, AtomicOp::Add)); case uint32_t(ThreadOp::I32AtomicAdd16U): - CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Uint16, - AtomicFetchAddOp)); + CHECK( + EmitAtomicRMW(f, ValType::I32, Scalar::Uint16, AtomicOp::Add)); case uint32_t(ThreadOp::I64AtomicAdd8U): - CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Uint8, - AtomicFetchAddOp)); + CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Uint8, AtomicOp::Add)); case uint32_t(ThreadOp::I64AtomicAdd16U): - CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Uint16, - AtomicFetchAddOp)); + CHECK( + EmitAtomicRMW(f, ValType::I64, Scalar::Uint16, AtomicOp::Add)); case uint32_t(ThreadOp::I64AtomicAdd32U): - CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Uint32, - AtomicFetchAddOp)); + CHECK( + EmitAtomicRMW(f, ValType::I64, Scalar::Uint32, AtomicOp::Add)); case uint32_t(ThreadOp::I32AtomicSub): - CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Int32, - AtomicFetchSubOp)); + CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Int32, AtomicOp::Sub)); case uint32_t(ThreadOp::I64AtomicSub): - CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Int64, - AtomicFetchSubOp)); + CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Int64, AtomicOp::Sub)); case uint32_t(ThreadOp::I32AtomicSub8U): - CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Uint8, - AtomicFetchSubOp)); + CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Uint8, AtomicOp::Sub)); case uint32_t(ThreadOp::I32AtomicSub16U): - CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Uint16, - AtomicFetchSubOp)); + CHECK( + EmitAtomicRMW(f, ValType::I32, Scalar::Uint16, AtomicOp::Sub)); case uint32_t(ThreadOp::I64AtomicSub8U): - CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Uint8, - AtomicFetchSubOp)); + CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Uint8, AtomicOp::Sub)); case uint32_t(ThreadOp::I64AtomicSub16U): - CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Uint16, - AtomicFetchSubOp)); + CHECK( + EmitAtomicRMW(f, ValType::I64, Scalar::Uint16, AtomicOp::Sub)); case uint32_t(ThreadOp::I64AtomicSub32U): - CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Uint32, - AtomicFetchSubOp)); + CHECK( + EmitAtomicRMW(f, ValType::I64, Scalar::Uint32, AtomicOp::Sub)); case uint32_t(ThreadOp::I32AtomicAnd): - CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Int32, - AtomicFetchAndOp)); + CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Int32, AtomicOp::And)); case uint32_t(ThreadOp::I64AtomicAnd): - CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Int64, - AtomicFetchAndOp)); + CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Int64, AtomicOp::And)); case uint32_t(ThreadOp::I32AtomicAnd8U): - CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Uint8, - AtomicFetchAndOp)); + CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Uint8, AtomicOp::And)); case uint32_t(ThreadOp::I32AtomicAnd16U): - CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Uint16, - AtomicFetchAndOp)); + CHECK( + EmitAtomicRMW(f, ValType::I32, Scalar::Uint16, AtomicOp::And)); case uint32_t(ThreadOp::I64AtomicAnd8U): - CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Uint8, - AtomicFetchAndOp)); + CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Uint8, AtomicOp::And)); case uint32_t(ThreadOp::I64AtomicAnd16U): - CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Uint16, - AtomicFetchAndOp)); + CHECK( + EmitAtomicRMW(f, ValType::I64, Scalar::Uint16, AtomicOp::And)); case uint32_t(ThreadOp::I64AtomicAnd32U): - CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Uint32, - AtomicFetchAndOp)); + CHECK( + EmitAtomicRMW(f, ValType::I64, Scalar::Uint32, AtomicOp::And)); case uint32_t(ThreadOp::I32AtomicOr): - CHECK( - EmitAtomicRMW(f, ValType::I32, Scalar::Int32, AtomicFetchOrOp)); + CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Int32, AtomicOp::Or)); case uint32_t(ThreadOp::I64AtomicOr): - CHECK( - EmitAtomicRMW(f, ValType::I64, Scalar::Int64, AtomicFetchOrOp)); + CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Int64, AtomicOp::Or)); case uint32_t(ThreadOp::I32AtomicOr8U): - CHECK( - EmitAtomicRMW(f, ValType::I32, Scalar::Uint8, AtomicFetchOrOp)); + CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Uint8, AtomicOp::Or)); case uint32_t(ThreadOp::I32AtomicOr16U): - CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Uint16, - AtomicFetchOrOp)); + CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Uint16, AtomicOp::Or)); case uint32_t(ThreadOp::I64AtomicOr8U): - CHECK( - EmitAtomicRMW(f, ValType::I64, Scalar::Uint8, AtomicFetchOrOp)); + CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Uint8, AtomicOp::Or)); case uint32_t(ThreadOp::I64AtomicOr16U): - CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Uint16, - AtomicFetchOrOp)); + CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Uint16, AtomicOp::Or)); case uint32_t(ThreadOp::I64AtomicOr32U): - CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Uint32, - AtomicFetchOrOp)); + CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Uint32, AtomicOp::Or)); case uint32_t(ThreadOp::I32AtomicXor): - CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Int32, - AtomicFetchXorOp)); + CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Int32, AtomicOp::Xor)); case uint32_t(ThreadOp::I64AtomicXor): - CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Int64, - AtomicFetchXorOp)); + CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Int64, AtomicOp::Xor)); case uint32_t(ThreadOp::I32AtomicXor8U): - CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Uint8, - AtomicFetchXorOp)); + CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Uint8, AtomicOp::Xor)); case uint32_t(ThreadOp::I32AtomicXor16U): - CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Uint16, - AtomicFetchXorOp)); + CHECK( + EmitAtomicRMW(f, ValType::I32, Scalar::Uint16, AtomicOp::Xor)); case uint32_t(ThreadOp::I64AtomicXor8U): - CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Uint8, - AtomicFetchXorOp)); + CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Uint8, AtomicOp::Xor)); case uint32_t(ThreadOp::I64AtomicXor16U): - CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Uint16, - AtomicFetchXorOp)); + CHECK( + EmitAtomicRMW(f, ValType::I64, Scalar::Uint16, AtomicOp::Xor)); case uint32_t(ThreadOp::I64AtomicXor32U): - CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Uint32, - AtomicFetchXorOp)); + CHECK( + EmitAtomicRMW(f, ValType::I64, Scalar::Uint32, AtomicOp::Xor)); case uint32_t(ThreadOp::I32AtomicXchg): CHECK(EmitAtomicXchg(f, ValType::I32, Scalar::Int32)); @@ -9267,6 +9242,41 @@ static bool EmitBodyExprs(FunctionCompiler& f) { #undef CHECK } +static bool IonBuildMIR(Decoder& d, const ModuleEnvironment& moduleEnv, + const FuncCompileInput& func, + const ValTypeVector& locals, MIRGenerator& mir, + TryNoteVector& tryNotes, FeatureUsage* observedFeatures, + UniqueChars* error) { + // Initialize MIR global information used for optimization + if (moduleEnv.numMemories() > 0) { + if (moduleEnv.memories[0].indexType() == IndexType::I32) { + mir.initMinWasmMemory0Length(moduleEnv.memories[0].initialLength32()); + } else { + mir.initMinWasmMemory0Length(moduleEnv.memories[0].initialLength64()); + } + } + + // Build MIR graph + FunctionCompiler f(moduleEnv, d, func, locals, mir, tryNotes); + if (!f.init()) { + return false; + } + + if (!f.startBlock()) { + return false; + } + + if (!EmitBodyExprs(f)) { + return false; + } + + f.finish(); + + *observedFeatures = f.featureUsage(); + + return true; +} + bool wasm::IonCompileFunctions(const ModuleEnvironment& moduleEnv, const CompilerEnvironment& compilerEnv, LifoAlloc& lifo, @@ -9307,52 +9317,28 @@ bool wasm::IonCompileFunctions(const ModuleEnvironment& moduleEnv, Decoder d(func.begin, func.end, func.lineOrBytecode, error); // Build the local types vector. - - const FuncType& funcType = *moduleEnv.funcs[func.index].type; ValTypeVector locals; - if (!locals.appendAll(funcType.args())) { - return false; - } - if (!DecodeLocalEntries(d, *moduleEnv.types, moduleEnv.features, &locals)) { + if (!DecodeLocalEntriesWithParams(d, moduleEnv, func.index, &locals)) { return false; } // Set up for Ion compilation. - const JitCompileOptions options; MIRGraph graph(&alloc); CompileInfo compileInfo(locals.length()); MIRGenerator mir(nullptr, options, &alloc, &graph, &compileInfo, IonOptimizations.get(OptimizationLevel::Wasm)); - if (moduleEnv.numMemories() > 0) { - if (moduleEnv.memories[0].indexType() == IndexType::I32) { - mir.initMinWasmMemory0Length(moduleEnv.memories[0].initialLength32()); - } else { - mir.initMinWasmMemory0Length(moduleEnv.memories[0].initialLength64()); - } - } // Build MIR graph - { - FunctionCompiler f(moduleEnv, d, func, locals, mir, masm.tryNotes()); - if (!f.init()) { - return false; - } - - if (!f.startBlock()) { - return false; - } - - if (!EmitBodyExprs(f)) { - return false; - } - - f.finish(); - - // Record observed feature usage - code->featureUsage |= f.featureUsage(); + FeatureUsage observedFeatures; + if (!IonBuildMIR(d, moduleEnv, func, locals, mir, masm.tryNotes(), + &observedFeatures, error)) { + return false; } + // Record observed feature usage + code->featureUsage |= observedFeatures; + // Compile MIR graph { jit::SpewBeginWasmFunction(&mir, func.index); @@ -9373,7 +9359,7 @@ bool wasm::IonCompileFunctions(const ModuleEnvironment& moduleEnv, BytecodeOffset prologueTrapOffset(func.lineOrBytecode); FuncOffsets offsets; - ArgTypeVector args(funcType); + ArgTypeVector args(*moduleEnv.funcs[func.index].type); if (!codegen.generateWasm(CallIndirectId::forFunc(moduleEnv, func.index), prologueTrapOffset, args, trapExitLayout, trapExitLayoutNumWords, &offsets, @@ -9407,6 +9393,66 @@ bool wasm::IonCompileFunctions(const ModuleEnvironment& moduleEnv, return code->swap(masm); } +bool wasm::IonDumpFunction(const ModuleEnvironment& moduleEnv, + const FuncCompileInput& func, + IonDumpContents contents, GenericPrinter& out, + UniqueChars* error) { + LifoAlloc lifo(TempAllocator::PreferredLifoChunkSize); + TempAllocator alloc(&lifo); + JitContext jitContext; + Decoder d(func.begin, func.end, func.lineOrBytecode, error); + + // Decode the locals. + ValTypeVector locals; + if (!DecodeLocalEntriesWithParams(d, moduleEnv, func.index, &locals)) { + return false; + } + + // Set up for Ion compilation. + const JitCompileOptions options; + MIRGraph graph(&alloc); + CompileInfo compileInfo(locals.length()); + MIRGenerator mir(nullptr, options, &alloc, &graph, &compileInfo, + IonOptimizations.get(OptimizationLevel::Wasm)); + + // Build MIR graph + TryNoteVector tryNotes; + FeatureUsage observedFeatures; + if (!IonBuildMIR(d, moduleEnv, func, locals, mir, tryNotes, &observedFeatures, + error)) { + return false; + } + + if (contents == IonDumpContents::UnoptimizedMIR) { + graph.dump(out); + return true; + } + + // Optimize the MIR graph + if (!OptimizeMIR(&mir)) { + return false; + } + + if (contents == IonDumpContents::OptimizedMIR) { + graph.dump(out); + return true; + } + +#ifdef JS_JITSPEW + // Generate the LIR graph + LIRGraph* lir = GenerateLIR(&mir); + if (!lir) { + return false; + } + + MOZ_ASSERT(contents == IonDumpContents::LIR); + lir->dump(out); +#else + out.printf("cannot dump LIR without --enable-jitspew"); +#endif + return true; +} + bool js::wasm::IonPlatformSupport() { #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86) || \ defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS64) || \ diff --git a/js/src/wasm/WasmIonCompile.h b/js/src/wasm/WasmIonCompile.h index f583cbad1f..4597c08be1 100644 --- a/js/src/wasm/WasmIonCompile.h +++ b/js/src/wasm/WasmIonCompile.h @@ -22,6 +22,9 @@ #include "wasm/WasmGenerator.h" namespace js { + +class GenericPrinter; + namespace wasm { // Return whether IonCompileFunction() can generate code on the current device. @@ -35,6 +38,11 @@ namespace wasm { const FuncCompileInputVector& inputs, CompiledCode* code, UniqueChars* error); +[[nodiscard]] bool IonDumpFunction(const ModuleEnvironment& moduleEnv, + const FuncCompileInput& func, + IonDumpContents contents, + GenericPrinter& out, UniqueChars* error); + } // namespace wasm } // namespace js diff --git a/js/src/wasm/WasmJS.cpp b/js/src/wasm/WasmJS.cpp index 6cc9528415..2eb5e355d9 100644 --- a/js/src/wasm/WasmJS.cpp +++ b/js/src/wasm/WasmJS.cpp @@ -768,8 +768,14 @@ static JSObject* GetWasmConstructorPrototype(JSContext* cx, } #ifdef ENABLE_WASM_TYPE_REFLECTIONS -static JSString* UTF8CharsToString(JSContext* cx, const char* chars) { - return NewStringCopyUTF8Z(cx, JS::ConstUTF8CharsZ(chars, strlen(chars))); +template <typename T> +static JSString* TypeToString(JSContext* cx, T type) { + UniqueChars chars = ToString(type, nullptr); + if (!chars) { + return nullptr; + } + return NewStringCopyUTF8Z( + cx, JS::ConstUTF8CharsZ(chars.get(), strlen(chars.get()))); } [[nodiscard]] static JSObject* ValTypesToArray(JSContext* cx, @@ -779,8 +785,7 @@ static JSString* UTF8CharsToString(JSContext* cx, const char* chars) { return nullptr; } for (ValType valType : valTypes) { - RootedString type(cx, - UTF8CharsToString(cx, ToString(valType, nullptr).get())); + RootedString type(cx, TypeToString(cx, valType)); if (!type) { return nullptr; } @@ -809,15 +814,14 @@ static JSObject* FuncTypeToObject(JSContext* cx, const FuncType& type) { return nullptr; } - return NewPlainObjectWithUniqueNames(cx, props.begin(), props.length()); + return NewPlainObjectWithUniqueNames(cx, props); } static JSObject* TableTypeToObject(JSContext* cx, RefType type, uint32_t initial, Maybe<uint32_t> maximum) { Rooted<IdValueVector> props(cx, IdValueVector(cx)); - RootedString elementType( - cx, UTF8CharsToString(cx, ToString(type, nullptr).get())); + RootedString elementType(cx, TypeToString(cx, type)); if (!elementType || !props.append(IdValuePair(NameToId(cx->names().element), StringValue(elementType)))) { ReportOutOfMemory(cx); @@ -838,7 +842,7 @@ static JSObject* TableTypeToObject(JSContext* cx, RefType type, return nullptr; } - return NewPlainObjectWithUniqueNames(cx, props.begin(), props.length()); + return NewPlainObjectWithUniqueNames(cx, props); } static JSObject* MemoryTypeToObject(JSContext* cx, bool shared, @@ -892,7 +896,7 @@ static JSObject* MemoryTypeToObject(JSContext* cx, bool shared, return nullptr; } - return NewPlainObjectWithUniqueNames(cx, props.begin(), props.length()); + return NewPlainObjectWithUniqueNames(cx, props); } static JSObject* GlobalTypeToObject(JSContext* cx, ValType type, @@ -905,15 +909,14 @@ static JSObject* GlobalTypeToObject(JSContext* cx, ValType type, return nullptr; } - RootedString valueType(cx, - UTF8CharsToString(cx, ToString(type, nullptr).get())); + RootedString valueType(cx, TypeToString(cx, type)); if (!valueType || !props.append(IdValuePair(NameToId(cx->names().value), StringValue(valueType)))) { ReportOutOfMemory(cx); return nullptr; } - return NewPlainObjectWithUniqueNames(cx, props.begin(), props.length()); + return NewPlainObjectWithUniqueNames(cx, props); } static JSObject* TagTypeToObject(JSContext* cx, @@ -928,7 +931,7 @@ static JSObject* TagTypeToObject(JSContext* cx, return nullptr; } - return NewPlainObjectWithUniqueNames(cx, props.begin(), props.length()); + return NewPlainObjectWithUniqueNames(cx, props); } #endif // ENABLE_WASM_TYPE_REFLECTIONS @@ -1184,8 +1187,7 @@ bool WasmModuleObject::imports(JSContext* cx, unsigned argc, Value* vp) { } #endif // ENABLE_WASM_TYPE_REFLECTIONS - JSObject* obj = - NewPlainObjectWithUniqueNames(cx, props.begin(), props.length()); + JSObject* obj = NewPlainObjectWithUniqueNames(cx, props); if (!obj) { return false; } @@ -1288,8 +1290,7 @@ bool WasmModuleObject::exports(JSContext* cx, unsigned argc, Value* vp) { } #endif // ENABLE_WASM_TYPE_REFLECTIONS - JSObject* obj = - NewPlainObjectWithUniqueNames(cx, props.begin(), props.length()); + JSObject* obj = NewPlainObjectWithUniqueNames(cx, props); if (!obj) { return false; } @@ -3227,7 +3228,7 @@ void WasmGlobalObject::finalize(JS::GCContext* gcx, JSObject* obj) { // Release the strong reference to the type definitions this global could // be referencing. global->type().Release(); - gcx->delete_(obj, &global->val(), MemoryUse::WasmGlobalCell); + gcx->delete_(obj, &global->mutableVal(), MemoryUse::WasmGlobalCell); } } @@ -3253,7 +3254,9 @@ WasmGlobalObject* WasmGlobalObject::create(JSContext* cx, HandleVal value, // It's simpler to initialize the cell after the object has been created, // to avoid needing to root the cell before the object creation. - obj->val() = value.get(); + // We don't use `setVal` here because the assumes the cell has already + // been initialized. + obj->mutableVal() = value.get(); // Acquire a strong reference to a type definition this global could // be referencing. obj->type().AddRef(); @@ -3384,7 +3387,7 @@ bool WasmGlobalObject::valueSetterImpl(JSContext* cx, const CallArgs& args) { if (!Val::fromJSValue(cx, global->type(), args.get(0), &val)) { return false; } - global->val() = val.get(); + global->setVal(val); args.rval().setUndefined(); return true; @@ -3417,10 +3420,23 @@ bool WasmGlobalObject::isMutable() const { ValType WasmGlobalObject::type() const { return val().get().type(); } -GCPtrVal& WasmGlobalObject::val() const { +GCPtrVal& WasmGlobalObject::mutableVal() { + return *reinterpret_cast<GCPtrVal*>(getReservedSlot(VAL_SLOT).toPrivate()); +} + +const GCPtrVal& WasmGlobalObject::val() const { return *reinterpret_cast<GCPtrVal*>(getReservedSlot(VAL_SLOT).toPrivate()); } +void WasmGlobalObject::setVal(wasm::HandleVal value) { + MOZ_ASSERT(type() == value.get().type()); + mutableVal() = value; +} + +void* WasmGlobalObject::addressOfCell() const { + return (void*)&val().get().cell(); +} + #ifdef ENABLE_WASM_TYPE_REFLECTIONS /* static */ bool WasmGlobalObject::typeImpl(JSContext* cx, const CallArgs& args) { @@ -4652,6 +4668,10 @@ static bool WebAssembly_validate(JSContext* cx, unsigned argc, Value* vp) { } FeatureOptions options; + if (!options.init(cx, callArgs.get(1))) { + return false; + } + UniqueChars error; bool validated = Validate(cx, *bytecode, options, &error); @@ -5351,15 +5371,13 @@ static bool WebAssemblyClassFinish(JSContext* cx, HandleObject object, } } - if (ExceptionsAvailable(cx)) { - constexpr NameAndProtoKey exceptionEntries[] = { - {"Tag", JSProto_WasmTag}, - {"Exception", JSProto_WasmException}, - }; - for (const auto& entry : exceptionEntries) { - if (!WebAssemblyDefineConstructor(cx, wasm, entry, &ctorValue, &id)) { - return false; - } + constexpr NameAndProtoKey exceptionEntries[] = { + {"Tag", JSProto_WasmTag}, + {"Exception", JSProto_WasmException}, + }; + for (const auto& entry : exceptionEntries) { + if (!WebAssemblyDefineConstructor(cx, wasm, entry, &ctorValue, &id)) { + return false; } } diff --git a/js/src/wasm/WasmJS.h b/js/src/wasm/WasmJS.h index 10c71b436b..27d49701a9 100644 --- a/js/src/wasm/WasmJS.h +++ b/js/src/wasm/WasmJS.h @@ -167,6 +167,8 @@ class WasmGlobalObject : public NativeObject { static bool valueSetterImpl(JSContext* cx, const CallArgs& args); static bool valueSetter(JSContext* cx, unsigned argc, Value* vp); + wasm::GCPtrVal& mutableVal(); + public: static const unsigned RESERVED_SLOTS = 2; static const JSClass class_; @@ -182,7 +184,9 @@ class WasmGlobalObject : public NativeObject { bool isMutable() const; wasm::ValType type() const; - wasm::GCPtrVal& val() const; + const wasm::GCPtrVal& val() const; + void setVal(wasm::HandleVal value); + void* addressOfCell() const; }; // The class of WebAssembly.Instance. Each WasmInstanceObject owns a diff --git a/js/src/wasm/WasmModule.cpp b/js/src/wasm/WasmModule.cpp index c2de0429d3..a297e81ad3 100644 --- a/js/src/wasm/WasmModule.cpp +++ b/js/src/wasm/WasmModule.cpp @@ -867,7 +867,7 @@ static bool GetGlobalExport(JSContext* cx, MOZ_RELEASE_ASSERT(!global.isImport()); RootedVal globalVal(cx); instanceObj->instance().constantGlobalGet(globalIndex, &globalVal); - globalObj->val() = globalVal; + globalObj->setVal(globalVal); return true; } diff --git a/js/src/wasm/WasmOpIter.cpp b/js/src/wasm/WasmOpIter.cpp index 102d39639c..d60a87dc12 100644 --- a/js/src/wasm/WasmOpIter.cpp +++ b/js/src/wasm/WasmOpIter.cpp @@ -25,14 +25,14 @@ using namespace js::jit; using namespace js::wasm; #ifdef ENABLE_WASM_GC -# ifndef ENABLE_WASM_FUNCTION_REFERENCES +# ifndef ENABLE_WASM_GC # error "GC types require the function-references feature" # endif #endif #ifdef DEBUG -# ifdef ENABLE_WASM_FUNCTION_REFERENCES +# ifdef ENABLE_WASM_GC # define WASM_FUNCTION_REFERENCES_OP(code) return code # else # define WASM_FUNCTION_REFERENCES_OP(code) break diff --git a/js/src/wasm/WasmOpIter.h b/js/src/wasm/WasmOpIter.h index 1711cc3926..59d494bfbf 100644 --- a/js/src/wasm/WasmOpIter.h +++ b/js/src/wasm/WasmOpIter.h @@ -165,7 +165,7 @@ enum class OpKind { ReturnCall, CallIndirect, ReturnCallIndirect, -# ifdef ENABLE_WASM_FUNCTION_REFERENCES +# ifdef ENABLE_WASM_GC CallRef, ReturnCallRef, # endif @@ -493,7 +493,8 @@ class MOZ_STACK_CLASS OpIter : private Policy { [[nodiscard]] bool getControl(uint32_t relativeDepth, Control** controlEntry); [[nodiscard]] bool checkBranchValueAndPush(uint32_t relativeDepth, ResultType* type, - ValueVector* values); + ValueVector* values, + bool rewriteStackTypes); [[nodiscard]] bool checkBrTableEntryAndPush(uint32_t* relativeDepth, ResultType prevBranchType, ResultType* branchType, @@ -533,7 +534,7 @@ class MOZ_STACK_CLASS OpIter : private Policy { inline bool checkIsSubtypeOf(ResultType params, ResultType results); -#ifdef ENABLE_WASM_FUNCTION_REFERENCES +#ifdef ENABLE_WASM_GC inline bool checkIsSubtypeOf(uint32_t actualTypeIndex, uint32_t expectedTypeIndex); #endif @@ -703,7 +704,7 @@ class MOZ_STACK_CLASS OpIter : private Policy { uint32_t* tableIndex, Value* callee, ValueVector* argValues); #endif -#ifdef ENABLE_WASM_FUNCTION_REFERENCES +#ifdef ENABLE_WASM_GC [[nodiscard]] bool readCallRef(const FuncType** funcType, Value* callee, ValueVector* argValues); @@ -932,7 +933,7 @@ inline bool OpIter<Policy>::checkIsSubtypeOf(ResultType params, return true; } -#ifdef ENABLE_WASM_FUNCTION_REFERENCES +#ifdef ENABLE_WASM_GC template <typename Policy> inline bool OpIter<Policy>::checkIsSubtypeOf(uint32_t actualTypeIndex, uint32_t expectedTypeIndex) { @@ -1480,14 +1481,15 @@ inline void OpIter<Policy>::popEnd() { template <typename Policy> inline bool OpIter<Policy>::checkBranchValueAndPush(uint32_t relativeDepth, ResultType* type, - ValueVector* values) { + ValueVector* values, + bool rewriteStackTypes) { Control* block = nullptr; if (!getControl(relativeDepth, &block)) { return false; } *type = block->branchTargetType(); - return checkTopTypeMatches(*type, values, /*rewriteStackTypes=*/false); + return checkTopTypeMatches(*type, values, rewriteStackTypes); } template <typename Policy> @@ -1499,7 +1501,8 @@ inline bool OpIter<Policy>::readBr(uint32_t* relativeDepth, ResultType* type, return fail("unable to read br depth"); } - if (!checkBranchValueAndPush(*relativeDepth, type, values)) { + if (!checkBranchValueAndPush(*relativeDepth, type, values, + /*rewriteStackTypes=*/false)) { return false; } @@ -1520,7 +1523,8 @@ inline bool OpIter<Policy>::readBrIf(uint32_t* relativeDepth, ResultType* type, return false; } - return checkBranchValueAndPush(*relativeDepth, type, values); + return checkBranchValueAndPush(*relativeDepth, type, values, + /*rewriteStackTypes=*/true); } #define UNKNOWN_ARITY UINT32_MAX @@ -2392,10 +2396,10 @@ inline bool OpIter<Policy>::readRefFunc(uint32_t* funcIndex) { "function index is not declared in a section before the code section"); } -#ifdef ENABLE_WASM_FUNCTION_REFERENCES +#ifdef ENABLE_WASM_GC // When function references enabled, push type index on the stack, e.g. for // validation of the call_ref instruction. - if (env_.functionReferencesEnabled()) { + if (env_.gcEnabled()) { const uint32_t typeIndex = env_.funcs[*funcIndex].typeIndex; const TypeDef& typeDef = env_.types->type(typeIndex); return push(RefType::fromTypeDef(&typeDef, false)); @@ -2457,7 +2461,8 @@ inline bool OpIter<Policy>::readBrOnNull(uint32_t* relativeDepth, return false; } - if (!checkBranchValueAndPush(*relativeDepth, type, values)) { + if (!checkBranchValueAndPush(*relativeDepth, type, values, + /*rewriteStackTypes=*/true)) { return false; } @@ -2505,7 +2510,7 @@ inline bool OpIter<Policy>::readBrOnNonNull(uint32_t* relativeDepth, } // Check if the type stack matches the branch target type. - if (!checkTopTypeMatches(*type, values, /*rewriteStackTypes=*/false)) { + if (!checkTopTypeMatches(*type, values, /*rewriteStackTypes=*/true)) { return false; } @@ -2693,7 +2698,7 @@ inline bool OpIter<Policy>::readReturnCallIndirect(uint32_t* funcTypeIndex, } #endif -#ifdef ENABLE_WASM_FUNCTION_REFERENCES +#ifdef ENABLE_WASM_GC template <typename Policy> inline bool OpIter<Policy>::readCallRef(const FuncType** funcType, Value* callee, ValueVector* argValues) { @@ -2719,7 +2724,7 @@ inline bool OpIter<Policy>::readCallRef(const FuncType** funcType, } #endif -#if defined(ENABLE_WASM_TAIL_CALLS) && defined(ENABLE_WASM_FUNCTION_REFERENCES) +#if defined(ENABLE_WASM_TAIL_CALLS) && defined(ENABLE_WASM_GC) template <typename Policy> inline bool OpIter<Policy>::readReturnCallRef(const FuncType** funcType, Value* callee, @@ -4001,7 +4006,7 @@ inline bool OpIter<Policy>::readBrOnCast(bool onSuccess, fallthroughTypes[labelTypeNumValues - 1] = typeOnFallthrough; return checkTopTypeMatches(ResultType::Vector(fallthroughTypes), values, - /*rewriteStackTypes=*/false); + /*rewriteStackTypes=*/true); } template <typename Policy> @@ -4228,18 +4233,18 @@ inline bool OpIter<Policy>::readCallBuiltinModuleFunc( return fail("index out of range"); } - *builtinModuleFunc = &BuiltinModuleFunc::getFromId(BuiltinModuleFuncId(id)); + *builtinModuleFunc = &BuiltinModuleFuncs::getFromId(BuiltinModuleFuncId(id)); - if ((*builtinModuleFunc)->usesMemory && env_.numMemories() == 0) { + if ((*builtinModuleFunc)->usesMemory() && env_.numMemories() == 0) { return fail("can't touch memory without memory"); } - if (!popWithTypes((*builtinModuleFunc)->params, params)) { + + const FuncType& funcType = *(*builtinModuleFunc)->funcType(); + if (!popCallArgs(funcType.args(), params)) { return false; } - if ((*builtinModuleFunc)->result.isNothing()) { - return true; - } - return push(*(*builtinModuleFunc)->result); + + return push(ResultType::Vector(funcType.results())); } } // namespace wasm diff --git a/js/src/wasm/WasmProcess.cpp b/js/src/wasm/WasmProcess.cpp index 427ba42d9d..0436e0a23f 100644 --- a/js/src/wasm/WasmProcess.cpp +++ b/js/src/wasm/WasmProcess.cpp @@ -26,10 +26,12 @@ #include "threading/ExclusiveData.h" #include "vm/MutexIDs.h" #include "vm/Runtime.h" +#include "wasm/WasmBuiltinModule.h" #include "wasm/WasmBuiltins.h" #include "wasm/WasmCode.h" #include "wasm/WasmInstance.h" #include "wasm/WasmModuleTypes.h" +#include "wasm/WasmStaticTypeDefs.h" using namespace js; using namespace wasm; @@ -438,6 +440,15 @@ bool wasm::Init() { oomUnsafe.crash("js::wasm::Init"); } + if (!StaticTypeDefs::init()) { + oomUnsafe.crash("js::wasm::Init"); + } + + // This uses StaticTypeDefs + if (!BuiltinModuleFuncs::init()) { + oomUnsafe.crash("js::wasm::Init"); + } + sProcessCodeSegmentMap = map; if (!InitTagForJSValue()) { @@ -455,6 +466,8 @@ void wasm::ShutDown() { return; } + BuiltinModuleFuncs::destroy(); + StaticTypeDefs::destroy(); PurgeCanonicalTypes(); if (sWrappedJSValueTagType) { diff --git a/js/src/wasm/WasmSerialize.cpp b/js/src/wasm/WasmSerialize.cpp index 62a68c5aff..35f437688c 100644 --- a/js/src/wasm/WasmSerialize.cpp +++ b/js/src/wasm/WasmSerialize.cpp @@ -957,7 +957,7 @@ CoderResult CodeSymbolicLinkArray( template <CoderMode mode> CoderResult CodeLinkData(Coder<mode>& coder, CoderArg<mode, wasm::LinkData> item) { - WASM_VERIFY_SERIALIZATION_FOR_SIZE(wasm::LinkData, 8832); + WASM_VERIFY_SERIALIZATION_FOR_SIZE(wasm::LinkData, 8976); if constexpr (mode == MODE_ENCODE) { MOZ_ASSERT(item->tier == Tier::Serialized); } diff --git a/js/src/wasm/WasmStaticTypeDefs.cpp b/js/src/wasm/WasmStaticTypeDefs.cpp new file mode 100644 index 0000000000..2306339087 --- /dev/null +++ b/js/src/wasm/WasmStaticTypeDefs.cpp @@ -0,0 +1,50 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * + * Copyright 2023 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wasm/WasmStaticTypeDefs.h" + +#include "wasm/WasmTypeDef.h" + +using namespace js; +using namespace js::wasm; + +const TypeDef* StaticTypeDefs::arrayMutI16 = nullptr; + +bool StaticTypeDefs::init() { + RefPtr<TypeContext> types = js_new<TypeContext>(); + if (!types) { + return false; + } + +#ifdef ENABLE_WASM_GC + arrayMutI16 = types->addType(ArrayType(StorageType::I16, true)); + if (!arrayMutI16) { + return false; + } + arrayMutI16->recGroup().AddRef(); +#endif + + return true; +} + +void StaticTypeDefs::destroy() { + if (arrayMutI16) { + arrayMutI16->recGroup().Release(); + arrayMutI16 = nullptr; + } +} diff --git a/js/src/wasm/WasmStaticTypeDefs.h b/js/src/wasm/WasmStaticTypeDefs.h new file mode 100644 index 0000000000..d3a01ad26c --- /dev/null +++ b/js/src/wasm/WasmStaticTypeDefs.h @@ -0,0 +1,41 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * + * Copyright 2015 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef wasm_static_type_defs +#define wasm_static_type_defs + +namespace js { +namespace wasm { + +class TypeDef; + +// Simple type definitions used in builtins with a static lifetime. +// +// TODO: this class is very simple and won't scale well with many type +// definitions. Rethink this if we have more than several type definitions. +struct StaticTypeDefs { + static const TypeDef* arrayMutI16; + + [[nodiscard]] static bool init(); + static void destroy(); +}; + +} // namespace wasm +} // namespace js + +#endif // wasm_static_type_defs diff --git a/js/src/wasm/WasmTypeDef.h b/js/src/wasm/WasmTypeDef.h index 7aedbed1f8..3426647095 100644 --- a/js/src/wasm/WasmTypeDef.h +++ b/js/src/wasm/WasmTypeDef.h @@ -1228,13 +1228,16 @@ class TypeContext : public AtomicRefCounted<TypeContext> { } template <typename T> - [[nodiscard]] bool addType(T&& type) { + [[nodiscard]] const TypeDef* addType(T&& type) { MutableRecGroup recGroup = startRecGroup(1); if (!recGroup) { - return false; + return nullptr; } recGroup->type(0) = std::move(type); - return endRecGroup(); + if (!endRecGroup()) { + return nullptr; + } + return &this->type(length() - 1); } const TypeDef& type(uint32_t index) const { return *types_[index]; } diff --git a/js/src/wasm/WasmValType.cpp b/js/src/wasm/WasmValType.cpp index d1874b7131..64ef8ff85a 100644 --- a/js/src/wasm/WasmValType.cpp +++ b/js/src/wasm/WasmValType.cpp @@ -150,94 +150,7 @@ enum class RefTypeResult { Unparsed, }; -static RefTypeResult MaybeToRefType(JSContext* cx, HandleObject obj, - RefType* out) { -#ifdef ENABLE_WASM_FUNCTION_REFERENCES - if (!wasm::FunctionReferencesAvailable(cx)) { - return RefTypeResult::Unparsed; - } - - JSAtom* refAtom = Atomize(cx, "ref", strlen("ref")); - if (!refAtom) { - return RefTypeResult::Failure; - } - RootedId refId(cx, AtomToId(refAtom)); - - RootedValue refVal(cx); - if (!GetProperty(cx, obj, obj, refId, &refVal)) { - return RefTypeResult::Failure; - } - - RootedString typeStr(cx, ToString(cx, refVal)); - if (!typeStr) { - return RefTypeResult::Failure; - } - - Rooted<JSLinearString*> typeLinearStr(cx, typeStr->ensureLinear(cx)); - if (!typeLinearStr) { - return RefTypeResult::Failure; - } - - if (StringEqualsLiteral(typeLinearStr, "func")) { - *out = RefType::func(); - } else if (StringEqualsLiteral(typeLinearStr, "extern")) { - *out = RefType::extern_(); -# ifdef ENABLE_WASM_EXNREF - } else if (ExnRefAvailable(cx) && StringEqualsLiteral(typeLinearStr, "exn")) { - *out = RefType::exn(); -# endif -# ifdef ENABLE_WASM_GC - } else if (GcAvailable(cx) && StringEqualsLiteral(typeLinearStr, "any")) { - *out = RefType::any(); - } else if (GcAvailable(cx) && StringEqualsLiteral(typeLinearStr, "eq")) { - *out = RefType::eq(); - } else if (GcAvailable(cx) && StringEqualsLiteral(typeLinearStr, "i31")) { - *out = RefType::i31(); - } else if (GcAvailable(cx) && StringEqualsLiteral(typeLinearStr, "struct")) { - *out = RefType::struct_(); - } else if (GcAvailable(cx) && StringEqualsLiteral(typeLinearStr, "array")) { - *out = RefType::array(); -# endif - } else { - return RefTypeResult::Unparsed; - } - - JSAtom* nullableAtom = Atomize(cx, "nullable", strlen("nullable")); - if (!nullableAtom) { - return RefTypeResult::Failure; - } - RootedId nullableId(cx, AtomToId(nullableAtom)); - RootedValue nullableVal(cx); - if (!GetProperty(cx, obj, obj, nullableId, &nullableVal)) { - return RefTypeResult::Failure; - } - - bool nullable = ToBoolean(nullableVal); - if (!nullable) { - *out = out->asNonNullable(); - } - MOZ_ASSERT(out->isNullable() == nullable); - return RefTypeResult::Parsed; -#else - return RefTypeResult::Unparsed; -#endif -} - bool wasm::ToValType(JSContext* cx, HandleValue v, ValType* out) { - if (v.isObject()) { - RootedObject obj(cx, &v.toObject()); - RefType refType; - switch (MaybeToRefType(cx, obj, &refType)) { - case RefTypeResult::Failure: - return false; - case RefTypeResult::Parsed: - *out = ValType(refType); - return true; - case RefTypeResult::Unparsed: - break; - } - } - RootedString typeStr(cx, ToString(cx, v)); if (!typeStr) { return false; @@ -274,18 +187,6 @@ bool wasm::ToValType(JSContext* cx, HandleValue v, ValType* out) { } bool wasm::ToRefType(JSContext* cx, HandleValue v, RefType* out) { - if (v.isObject()) { - RootedObject obj(cx, &v.toObject()); - switch (MaybeToRefType(cx, obj, out)) { - case RefTypeResult::Failure: - return false; - case RefTypeResult::Parsed: - return true; - case RefTypeResult::Unparsed: - break; - } - } - RootedString typeStr(cx, ToString(cx, v)); if (!typeStr) { return false; diff --git a/js/src/wasm/WasmValType.h b/js/src/wasm/WasmValType.h index 0821ee5df9..c98eda28dd 100644 --- a/js/src/wasm/WasmValType.h +++ b/js/src/wasm/WasmValType.h @@ -479,7 +479,7 @@ class StorageTypeTraits { case TypeCode::NullExternRef: case TypeCode::NullAnyRef: #endif -#ifdef ENABLE_WASM_FUNCTION_REFERENCES +#ifdef ENABLE_WASM_GC case AbstractTypeRefCode: #endif return true; @@ -557,7 +557,7 @@ class ValTypeTraits { case TypeCode::NullExternRef: case TypeCode::NullAnyRef: #endif -#ifdef ENABLE_WASM_FUNCTION_REFERENCES +#ifdef ENABLE_WASM_GC case AbstractTypeRefCode: #endif return true; diff --git a/js/src/wasm/WasmValidate.cpp b/js/src/wasm/WasmValidate.cpp index e964c11d04..98a1423a41 100644 --- a/js/src/wasm/WasmValidate.cpp +++ b/js/src/wasm/WasmValidate.cpp @@ -89,14 +89,19 @@ bool wasm::EncodeLocalEntries(Encoder& e, const ValTypeVector& locals) { return true; } -bool wasm::DecodeLocalEntries(Decoder& d, const TypeContext& types, - const FeatureArgs& features, - ValTypeVector* locals) { +bool wasm::DecodeLocalEntriesWithParams(Decoder& d, + const ModuleEnvironment& env, + uint32_t funcIndex, + ValTypeVector* locals) { uint32_t numLocalEntries; if (!d.readVarU32(&numLocalEntries)) { return d.fail("failed to read number of local entries"); } + if (!locals->appendAll(env.funcs[funcIndex].type->args())) { + return false; + } + for (uint32_t i = 0; i < numLocalEntries; i++) { uint32_t count; if (!d.readVarU32(&count)) { @@ -108,7 +113,7 @@ bool wasm::DecodeLocalEntries(Decoder& d, const TypeContext& types, } ValType type; - if (!d.readValType(types, features, &type)) { + if (!d.readValType(*env.types, env.features, &type)) { return false; } @@ -235,9 +240,9 @@ static bool DecodeFunctionBodyExprs(const ModuleEnvironment& env, &unusedArgs)); } #endif -#ifdef ENABLE_WASM_FUNCTION_REFERENCES +#ifdef ENABLE_WASM_GC case uint16_t(Op::CallRef): { - if (!env.functionReferencesEnabled()) { + if (!env.gcEnabled()) { return iter.unrecognizedOpcode(&op); } const FuncType* unusedType; @@ -246,7 +251,7 @@ static bool DecodeFunctionBodyExprs(const ModuleEnvironment& env, } # ifdef ENABLE_WASM_TAIL_CALLS case uint16_t(Op::ReturnCallRef): { - if (!env.functionReferencesEnabled() || !env.tailCallsEnabled()) { + if (!env.gcEnabled() || !env.tailCallsEnabled()) { return iter.unrecognizedOpcode(&op); } const FuncType* unusedType; @@ -1240,15 +1245,15 @@ static bool DecodeFunctionBodyExprs(const ModuleEnvironment& env, } break; } -#ifdef ENABLE_WASM_FUNCTION_REFERENCES +#ifdef ENABLE_WASM_GC case uint16_t(Op::RefAsNonNull): { - if (!env.functionReferencesEnabled()) { + if (!env.gcEnabled()) { return iter.unrecognizedOpcode(&op); } CHECK(iter.readRefAsNonNull(¬hing)); } case uint16_t(Op::BrOnNull): { - if (!env.functionReferencesEnabled()) { + if (!env.gcEnabled()) { return iter.unrecognizedOpcode(&op); } uint32_t unusedDepth; @@ -1256,7 +1261,7 @@ static bool DecodeFunctionBodyExprs(const ModuleEnvironment& env, iter.readBrOnNull(&unusedDepth, &unusedType, ¬hings, ¬hing)); } case uint16_t(Op::BrOnNonNull): { - if (!env.functionReferencesEnabled()) { + if (!env.gcEnabled()) { return iter.unrecognizedOpcode(&op); } uint32_t unusedDepth; @@ -1285,31 +1290,19 @@ static bool DecodeFunctionBodyExprs(const ModuleEnvironment& env, CHECK(iter.readRefIsNull(¬hing)); } case uint16_t(Op::Try): - if (!env.exceptionsEnabled()) { - return iter.unrecognizedOpcode(&op); - } CHECK(iter.readTry(&unusedType)); case uint16_t(Op::Catch): { - if (!env.exceptionsEnabled()) { - return iter.unrecognizedOpcode(&op); - } LabelKind unusedKind; uint32_t unusedIndex; CHECK(iter.readCatch(&unusedKind, &unusedIndex, &unusedType, &unusedType, ¬hings)); } case uint16_t(Op::CatchAll): { - if (!env.exceptionsEnabled()) { - return iter.unrecognizedOpcode(&op); - } LabelKind unusedKind; CHECK(iter.readCatchAll(&unusedKind, &unusedType, &unusedType, ¬hings)); } case uint16_t(Op::Delegate): { - if (!env.exceptionsEnabled()) { - return iter.unrecognizedOpcode(&op); - } uint32_t unusedDepth; if (!iter.readDelegate(&unusedDepth, &unusedType, ¬hings)) { return false; @@ -1318,16 +1311,10 @@ static bool DecodeFunctionBodyExprs(const ModuleEnvironment& env, break; } case uint16_t(Op::Throw): { - if (!env.exceptionsEnabled()) { - return iter.unrecognizedOpcode(&op); - } uint32_t unusedIndex; CHECK(iter.readThrow(&unusedIndex, ¬hings)); } case uint16_t(Op::Rethrow): { - if (!env.exceptionsEnabled()) { - return iter.unrecognizedOpcode(&op); - } uint32_t unusedDepth; CHECK(iter.readRethrow(&unusedDepth)); } @@ -1541,14 +1528,10 @@ static bool DecodeFunctionBodyExprs(const ModuleEnvironment& env, bool wasm::ValidateFunctionBody(const ModuleEnvironment& env, uint32_t funcIndex, uint32_t bodySize, Decoder& d) { - ValTypeVector locals; - if (!locals.appendAll(env.funcs[funcIndex].type->args())) { - return false; - } - const uint8_t* bodyBegin = d.currentPosition(); - if (!DecodeLocalEntries(d, *env.types, env.features, &locals)) { + ValTypeVector locals; + if (!DecodeLocalEntriesWithParams(d, env, funcIndex, &locals)) { return false; } @@ -1624,7 +1607,7 @@ static bool DecodeFuncType(Decoder& d, ModuleEnvironment* env, static bool DecodeStructType(Decoder& d, ModuleEnvironment* env, StructType* structType) { if (!env->gcEnabled()) { - return d.fail("Structure types not enabled"); + return d.fail("gc not enabled"); } uint32_t numFields; @@ -1668,7 +1651,7 @@ static bool DecodeStructType(Decoder& d, ModuleEnvironment* env, static bool DecodeArrayType(Decoder& d, ModuleEnvironment* env, ArrayType* arrayType) { if (!env->gcEnabled()) { - return d.fail("gc types not enabled"); + return d.fail("gc not enabled"); } StorageType elementType; @@ -2247,13 +2230,6 @@ static bool CheckImportsAgainstBuiltinModules(Decoder& d, return true; } - // Allocate a type context for builtin types so we can canonicalize them - // and use them in type comparisons - RefPtr<TypeContext> builtinTypes = js_new<TypeContext>(); - if (!builtinTypes) { - return false; - } - uint32_t importFuncIndex = 0; for (auto& import : env->imports) { Maybe<BuiltinModuleId> builtinModule = @@ -2278,21 +2254,9 @@ static bool CheckImportsAgainstBuiltinModules(Decoder& d, return d.fail("unrecognized builtin module field"); } - // Get a canonicalized type definition for this builtin so we can - // accurately compare it against the import type. - FuncType builtinFuncType; - if (!(*builtinFunc)->funcType(&builtinFuncType)) { - return false; - } - if (!builtinTypes->addType(builtinFuncType)) { - return false; - } - const TypeDef& builtinTypeDef = - builtinTypes->type(builtinTypes->length() - 1); - const TypeDef& importTypeDef = (*env->types)[func.typeIndex]; - if (!TypeDef::isSubTypeOf(&builtinTypeDef, &importTypeDef)) { - return d.failf("type mismatch in %s", (*builtinFunc)->exportName); + if (!TypeDef::isSubTypeOf((*builtinFunc)->typeDef(), &importTypeDef)) { + return d.failf("type mismatch in %s", (*builtinFunc)->exportName()); } break; } @@ -2479,10 +2443,6 @@ static bool DecodeTagSection(Decoder& d, ModuleEnvironment* env) { return true; } - if (!env->exceptionsEnabled()) { - return d.fail("exceptions not enabled"); - } - uint32_t numDefs; if (!d.readVarU32(&numDefs)) { return d.fail("expected number of tags"); diff --git a/js/src/wasm/WasmValidate.h b/js/src/wasm/WasmValidate.h index 3254e7b74a..8ba08fd088 100644 --- a/js/src/wasm/WasmValidate.h +++ b/js/src/wasm/WasmValidate.h @@ -285,11 +285,13 @@ using ValidatingOpIter = OpIter<ValidatingPolicy>; Decoder& d, ValTypeVector* locals); -// This validates the entries. +// This validates the entries. Function params are inserted before the locals +// to generate the full local entries for use in validation -[[nodiscard]] bool DecodeLocalEntries(Decoder& d, const TypeContext& types, - const FeatureArgs& features, - ValTypeVector* locals); +[[nodiscard]] bool DecodeLocalEntriesWithParams(Decoder& d, + const ModuleEnvironment& env, + uint32_t funcIndex, + ValTypeVector* locals); // Returns whether the given [begin, end) prefix of a module's bytecode starts a // code section and, if so, returns the SectionRange of that code section. diff --git a/js/src/wasm/WasmValue.cpp b/js/src/wasm/WasmValue.cpp index 3798f6c3e8..6039b00517 100644 --- a/js/src/wasm/WasmValue.cpp +++ b/js/src/wasm/WasmValue.cpp @@ -642,7 +642,7 @@ bool wasm::ToWebAssemblyValue(JSContext* cx, HandleValue val, ValType type, case ValType::V128: break; case ValType::Ref: -#ifdef ENABLE_WASM_FUNCTION_REFERENCES +#ifdef ENABLE_WASM_GC if (!type.isNullable() && val.isNull()) { JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_REF_NONNULLABLE_VALUE); diff --git a/js/src/wasm/WasmValue.h b/js/src/wasm/WasmValue.h index 66de690537..79e20285b9 100644 --- a/js/src/wasm/WasmValue.h +++ b/js/src/wasm/WasmValue.h @@ -224,6 +224,11 @@ class LitVal { Cell& cell() { return cell_; } const Cell& cell() const { return cell_; } + // Updates the type of the LitVal. Does not check that the type is valid for the + // actual value, so make sure the type is definitely correct via validation or + // something. + void unsafeSetType(ValType type) { type_ = type; } + uint32_t i32() const { MOZ_ASSERT(type_ == ValType::I32); return cell_.i32_; @@ -309,11 +314,6 @@ class MOZ_NON_PARAM Val : public LitVal { return cell_.ref_; } - // Updates the type of the Val. Does not check that the type is valid for the - // actual value, so make sure the type is definitely correct via validation or - // something. - void unsafeSetType(ValType type) { type_ = type; } - // Initialize from `loc` which is a rooted location and needs no barriers. void initFromRootedLocation(ValType type, const void* loc); void initFromHeapLocation(ValType type, const void* loc); diff --git a/js/src/wasm/moz.build b/js/src/wasm/moz.build index 8aa23e3516..83fea3b81b 100644 --- a/js/src/wasm/moz.build +++ b/js/src/wasm/moz.build @@ -45,6 +45,7 @@ UNIFIED_SOURCES += [ "WasmRealm.cpp", "WasmSerialize.cpp", "WasmSignalHandlers.cpp", + "WasmStaticTypeDefs.cpp", "WasmStubs.cpp", "WasmSummarizeInsn.cpp", "WasmTable.cpp", |