diff --git a/js/src/devtools/automation/variants/msan b/js/src/devtools/automation/variants/msan index 486e71c184f5..7a0f2296e1af 100644 --- a/js/src/devtools/automation/variants/msan +++ b/js/src/devtools/automation/variants/msan @@ -9,6 +9,6 @@ "MSAN_OPTIONS": "external_symbolizer_path={TOOLTOOL_CHECKOUT}/clang/bin/llvm-symbolizer:log_path={OUTDIR}/sanitize_log" }, "ignore-test-failures": "true", - "max-errors": 6, + "max-errors": 7, "use_minidump": false } diff --git a/js/src/jit-test/tests/wasm/gc/anyref-val-tracing.js b/js/src/jit-test/tests/wasm/gc/anyref-val-tracing.js new file mode 100644 index 000000000000..b1a689491881 --- /dev/null +++ b/js/src/jit-test/tests/wasm/gc/anyref-val-tracing.js @@ -0,0 +1,14 @@ +if (!wasmGcEnabled()) { + quit(0); +} + +gczeal(14, 1); +let { exports } = wasmEvalText(`(module + (global $anyref (import "glob" "anyref") anyref) + (func (export "get") (result anyref) get_global $anyref) +)`, { + glob: { + anyref: { sentinel: "lol" }, + } +}); +assertEq(exports.get().sentinel, "lol"); diff --git a/js/src/wasm/AsmJS.cpp b/js/src/wasm/AsmJS.cpp index a4e775c0eb0a..291ae5fad91c 100644 --- a/js/src/wasm/AsmJS.cpp +++ b/js/src/wasm/AsmJS.cpp @@ -7671,11 +7671,12 @@ HasPureCoercion(JSContext* cx, HandleValue v) } static bool -ValidateGlobalVariable(JSContext* cx, const AsmJSGlobal& global, HandleValue importVal, LitVal* val) +ValidateGlobalVariable(JSContext* cx, const AsmJSGlobal& global, HandleValue importVal, + Maybe* val) { switch (global.varInitKind()) { case AsmJSGlobal::InitConstant: - *val = global.varInitVal(); + val->emplace(global.varInitVal()); return true; case AsmJSGlobal::InitImport: { @@ -7691,7 +7692,7 @@ ValidateGlobalVariable(JSContext* cx, const AsmJSGlobal& global, HandleValue imp int32_t i32; if (!ToInt32(cx, v, &i32)) return false; - *val = LitVal(uint32_t(i32)); + val->emplace(uint32_t(i32)); return true; } case ValType::I64: @@ -7700,42 +7701,42 @@ ValidateGlobalVariable(JSContext* cx, const AsmJSGlobal& global, HandleValue imp float f; if (!RoundFloat32(cx, v, &f)) return false; - *val = LitVal(f); + val->emplace(f); return true; } case ValType::F64: { double d; if (!ToNumber(cx, v, &d)) return false; - *val = LitVal(d); + val->emplace(d); return true; } case ValType::I8x16: { SimdConstant simdConstant; if (!ToSimdConstant(cx, v, &simdConstant)) return false; - *val = LitVal(simdConstant.asInt8x16()); + val->emplace(simdConstant.asInt8x16()); return true; } case ValType::I16x8: { SimdConstant simdConstant; if (!ToSimdConstant(cx, v, &simdConstant)) return false; - *val = LitVal(simdConstant.asInt16x8()); + val->emplace(simdConstant.asInt16x8()); return true; } case ValType::I32x4: { SimdConstant simdConstant; if (!ToSimdConstant(cx, v, &simdConstant)) return false; - *val = LitVal(simdConstant.asInt32x4()); + val->emplace(simdConstant.asInt32x4()); return true; } case ValType::F32x4: { SimdConstant simdConstant; if (!ToSimdConstant(cx, v, &simdConstant)) return false; - *val = LitVal(simdConstant.asFloat32x4()); + val->emplace(simdConstant.asFloat32x4()); return true; } case ValType::B8x16: { @@ -7743,7 +7744,7 @@ ValidateGlobalVariable(JSContext* cx, const AsmJSGlobal& global, HandleValue imp if (!ToSimdConstant(cx, v, &simdConstant)) return false; // Bool8x16 uses the same data layout as Int8x16. - *val = LitVal(simdConstant.asInt8x16()); + val->emplace(simdConstant.asInt8x16()); return true; } case ValType::B16x8: { @@ -7751,7 +7752,7 @@ ValidateGlobalVariable(JSContext* cx, const AsmJSGlobal& global, HandleValue imp if (!ToSimdConstant(cx, v, &simdConstant)) return false; // Bool16x8 uses the same data layout as Int16x8. - *val = LitVal(simdConstant.asInt16x8()); + val->emplace(simdConstant.asInt16x8()); return true; } case ValType::B32x4: { @@ -7759,7 +7760,7 @@ ValidateGlobalVariable(JSContext* cx, const AsmJSGlobal& global, HandleValue imp if (!ToSimdConstant(cx, v, &simdConstant)) return false; // Bool32x4 uses the same data layout as Int32x4. - *val = LitVal(simdConstant.asInt32x4()); + val->emplace(simdConstant.asInt32x4()); return true; } case ValType::Ref: @@ -8134,7 +8135,7 @@ CheckBuffer(JSContext* cx, const AsmJSMetadata& metadata, HandleValue bufferVal, static bool GetImports(JSContext* cx, const AsmJSMetadata& metadata, HandleValue globalVal, HandleValue importVal, MutableHandle funcImports, - LitValVector* valImports) + MutableHandleValVector valImports) { Rooted ffis(cx, FunctionVector(cx)); if (!ffis.resize(metadata.numFFIs)) @@ -8143,10 +8144,10 @@ GetImports(JSContext* cx, const AsmJSMetadata& metadata, HandleValue globalVal, for (const AsmJSGlobal& global : metadata.asmJSGlobals) { switch (global.which()) { case AsmJSGlobal::Variable: { - LitVal val; - if (!ValidateGlobalVariable(cx, global, importVal, &val)) + Maybe litVal; + if (!ValidateGlobalVariable(cx, global, importVal, &litVal)) return false; - if (!valImports->append(val)) + if (!valImports.append(Val(*litVal))) return false; break; } @@ -8209,7 +8210,7 @@ TryInstantiate(JSContext* cx, CallArgs args, Module& module, const AsmJSMetadata return false; } - LitValVector valImports; + RootedValVector valImports(cx); Rooted funcs(cx, FunctionVector(cx)); if (!GetImports(cx, metadata, globalVal, importVal, &funcs, &valImports)) return false; @@ -8217,7 +8218,8 @@ TryInstantiate(JSContext* cx, CallArgs args, Module& module, const AsmJSMetadata Rooted globalObjs(cx); RootedWasmTableObject table(cx); - if (!module.instantiate(cx, funcs, table, memory, valImports, globalObjs.get(), nullptr, instanceObj)) + if (!module.instantiate(cx, funcs, table, memory, valImports, globalObjs.get(), nullptr, + instanceObj)) return false; exportObj.set(&instanceObj->exportsObj()); diff --git a/js/src/wasm/WasmBaselineCompile.cpp b/js/src/wasm/WasmBaselineCompile.cpp index b60791483280..34cb5b22fdd4 100644 --- a/js/src/wasm/WasmBaselineCompile.cpp +++ b/js/src/wasm/WasmBaselineCompile.cpp @@ -8343,7 +8343,7 @@ BaseCompiler::emitGetGlobal() pushF64(value.f64()); break; case ValType::AnyRef: - pushRef(value.ptr()); + pushRef(intptr_t(value.ptr())); break; default: MOZ_CRASH("Global constant type"); diff --git a/js/src/wasm/WasmInstance.cpp b/js/src/wasm/WasmInstance.cpp index 1fffae9b4045..51c01adf6ee6 100644 --- a/js/src/wasm/WasmInstance.cpp +++ b/js/src/wasm/WasmInstance.cpp @@ -413,15 +413,12 @@ Instance::memCopy(Instance* instance, uint32_t destByteOffset, uint32_t srcByteO // Knowing that len > 0 below simplifies the wraparound checks. if (len == 0) { - // Even though the length is zero, we must check for a valid offset. if (destByteOffset < memLen && srcByteOffset < memLen) return 0; // else fall through to failure case - } else { - ArrayBufferObjectMaybeShared& arrBuf = mem->buffer(); uint8_t* rawBuf = arrBuf.dataPointerEither().unwrap(); @@ -454,15 +451,12 @@ Instance::memFill(Instance* instance, uint32_t byteOffset, uint32_t value, uint3 // Knowing that len > 0 below simplifies the wraparound check. if (len == 0) { - // Even though the length is zero, we must check for a valid offset. if (byteOffset < memLen) return 0; // else fall through to failure case - } else { - ArrayBufferObjectMaybeShared& arrBuf = mem->buffer(); uint8_t* rawBuf = arrBuf.dataPointerEither().unwrap(); @@ -477,7 +471,6 @@ Instance::memFill(Instance* instance, uint32_t byteOffset, uint32_t value, uint3 return 0; } // else fall through to failure case - } JSContext* cx = TlsContext.get(); @@ -515,7 +508,7 @@ Instance::Instance(JSContext* cx, HandleWasmMemoryObject memory, SharedTableVector&& tables, Handle funcImports, - const LitValVector& globalImportValues, + HandleValVector globalImportValues, const WasmGlobalObjectVector& globalObjs) : realm_(cx->realm()), object_(object), @@ -598,7 +591,7 @@ Instance::Instance(JSContext* cx, if (global.isIndirect()) *(void**)globalAddr = globalObjs[imported]->cell(); else - globalImportValues[imported].writePayload(globalAddr); + globalImportValues[imported].get().writePayload(globalAddr); break; } case GlobalKind::Variable: { @@ -608,7 +601,7 @@ Instance::Instance(JSContext* cx, if (global.isIndirect()) *(void**)globalAddr = globalObjs[i]->cell(); else - init.val().writePayload(globalAddr); + Val(init.val()).writePayload(globalAddr); break; } case InitExpr::Kind::GetGlobal: { @@ -618,12 +611,13 @@ Instance::Instance(JSContext* cx, // the source global should never be indirect. MOZ_ASSERT(!imported.isIndirect()); + RootedVal dest(cx, globalImportValues[imported.importIndex()].get()); if (global.isIndirect()) { void* address = globalObjs[i]->cell(); *(void**)globalAddr = address; - globalImportValues[imported.importIndex()].writePayload((uint8_t*)address); + dest.get().writePayload((uint8_t*)address); } else { - globalImportValues[imported.importIndex()].writePayload(globalAddr); + dest.get().writePayload(globalAddr); } break; } diff --git a/js/src/wasm/WasmInstance.h b/js/src/wasm/WasmInstance.h index e29c2fd6d235..495c8bdcfe12 100644 --- a/js/src/wasm/WasmInstance.h +++ b/js/src/wasm/WasmInstance.h @@ -78,7 +78,7 @@ class Instance HandleWasmMemoryObject memory, SharedTableVector&& tables, Handle funcImports, - const LitValVector& globalImportValues, + HandleValVector globalImportValues, const WasmGlobalObjectVector& globalObjs); ~Instance(); bool init(JSContext* cx); diff --git a/js/src/wasm/WasmJS.cpp b/js/src/wasm/WasmJS.cpp index e5c419d73118..9893f866d21d 100644 --- a/js/src/wasm/WasmJS.cpp +++ b/js/src/wasm/WasmJS.cpp @@ -45,6 +45,16 @@ #include "vm/JSObject-inl.h" #include "vm/NativeObject-inl.h" +#define WASM_CRASH_IF_SIMD_TYPES \ + case ValType::I8x16: \ + case ValType::B8x16: \ + case ValType::I16x8: \ + case ValType::B16x8: \ + case ValType::I32x4: \ + case ValType::B32x4: \ + case ValType::F32x4: \ + MOZ_CRASH("unexpected SIMD type") + using namespace js; using namespace js::jit; using namespace js::wasm; @@ -110,49 +120,53 @@ wasm::HasSupport(JSContext* cx) } static bool -ToWebAssemblyValue(JSContext* cx, ValType targetType, HandleValue v, LitVal* val) +ToWebAssemblyValue(JSContext* cx, ValType targetType, HandleValue v, MutableHandleVal val) { switch (targetType.code()) { case ValType::I32: { int32_t i32; if (!ToInt32(cx, v, &i32)) return false; - *val = LitVal(uint32_t(i32)); + val.set(Val(uint32_t(i32))); return true; } case ValType::F32: { double d; if (!ToNumber(cx, v, &d)) return false; - *val = LitVal(float(d)); + val.set(Val(float(d))); return true; } case ValType::F64: { double d; if (!ToNumber(cx, v, &d)) return false; - *val = LitVal(d); + val.set(Val(d)); return true; } case ValType::AnyRef: { if (v.isNull()) { - *val = LitVal(ValType::AnyRef, nullptr); + val.set(Val(nullptr)); } else { JSObject* obj = ToObject(cx, v); if (!obj) return false; - *val = LitVal(ValType::AnyRef, obj); + MOZ_ASSERT(obj->compartment() == cx->compartment()); + val.set(Val(obj)); } return true; } - default: { - MOZ_CRASH("unexpected import value type, caller must guard"); + WASM_CRASH_IF_SIMD_TYPES; + case ValType::Ref: + case ValType::I64: { + break; } } + MOZ_CRASH("unexpected import value type, caller must guard"); } static Value -ToJSValue(const LitVal& val) +ToJSValue(const Val& val) { switch (val.type().code()) { case ValType::I32: @@ -165,9 +179,12 @@ ToJSValue(const LitVal& val) if (!val.ptr()) return NullValue(); return ObjectValue(*(JSObject*)val.ptr()); - default: - MOZ_CRASH("unexpected type when translating to a JS value"); + WASM_CRASH_IF_SIMD_TYPES; + case ValType::Ref: + case ValType::I64: + break; } + MOZ_CRASH("unexpected type when translating to a JS value"); } // ============================================================================ @@ -206,7 +223,7 @@ GetImports(JSContext* cx, MutableHandleWasmTableObject tableImport, MutableHandleWasmMemoryObject memoryImport, WasmGlobalObjectVector& globalObjs, - LitValVector* globalImportValues) + MutableHandleValVector globalImportValues) { const ImportVector& imports = module.imports(); if (!imports.empty() && !importObj) @@ -258,11 +275,11 @@ GetImports(JSContext* cx, break; } case DefinitionKind::Global: { - LitVal val; const uint32_t index = globalIndex++; const GlobalDesc& global = globals[index]; MOZ_ASSERT(global.importIndex() == index); + RootedVal val(cx); if (v.isObject() && v.toObject().is()) { RootedWasmGlobalObject obj(cx, &v.toObject().as()); @@ -280,7 +297,7 @@ GetImports(JSContext* cx, return false; } globalObjs[index] = obj; - val = obj->val(); + obj->val(&val); } else { if (IsNumberType(global.type())) { if (!v.isNumber()) @@ -305,7 +322,7 @@ GetImports(JSContext* cx, return false; } - if (!globalImportValues->append(val)) + if (!globalImportValues.append(val)) return false; break; @@ -380,11 +397,12 @@ wasm::Eval(JSContext* cx, Handle code, HandleObject importObj RootedWasmMemoryObject memory(cx); Rooted globalObjs(cx); - LitValVector globals; + RootedValVector globals(cx); if (!GetImports(cx, *module, importObj, &funcs, &table, &memory, globalObjs.get(), &globals)) return false; - return module->instantiate(cx, funcs, table, memory, globals, globalObjs.get(), nullptr, instanceObj); + return module->instantiate(cx, funcs, table, memory, globals, globalObjs.get(), nullptr, + instanceObj); } // ============================================================================ @@ -1070,7 +1088,7 @@ WasmInstanceObject::create(JSContext* cx, SharedTableVector&& tables, Handle funcImports, const GlobalDescVector& globals, - const LitValVector& globalImportValues, + HandleValVector globalImportValues, const WasmGlobalObjectVector& globalObjs, HandleObject proto) { @@ -1177,11 +1195,12 @@ Instantiate(JSContext* cx, const Module& module, HandleObject importObj, RootedWasmMemoryObject memory(cx); Rooted globalObjs(cx); - LitValVector globals; + RootedValVector globals(cx); if (!GetImports(cx, module, importObj, &funcs, &table, &memory, globalObjs.get(), &globals)) return false; - return module.instantiate(cx, funcs, table, memory, globals, globalObjs.get(), instanceProto, instanceObj); + return module.instantiate(cx, funcs, table, memory, globals, globalObjs.get(), instanceProto, + instanceObj); } /* static */ bool @@ -2140,10 +2159,17 @@ WasmGlobalObject::trace(JSTracer* trc, JSObject* obj) WasmGlobalObject* global = reinterpret_cast(obj); switch (global->type().code()) { case ValType::AnyRef: - TraceNullableEdge(trc, &global->cell()->ptr, "wasm anyref global"); + if (global->cell()->ptr) + TraceManuallyBarrieredEdge(trc, &global->cell()->ptr, "wasm anyref global"); break; - default: + case ValType::I32: + case ValType::F32: + case ValType::I64: + case ValType::F64: break; + WASM_CRASH_IF_SIMD_TYPES; + case ValType::Ref: + MOZ_CRASH("Ref NYI"); } } @@ -2155,21 +2181,8 @@ WasmGlobalObject::finalize(FreeOp*, JSObject* obj) } /* static */ WasmGlobalObject* -WasmGlobalObject::create(JSContext* cx, const LitVal& val, bool isMutable) +WasmGlobalObject::create(JSContext* cx, HandleVal hval, bool isMutable) { - UniquePtr cell = js::MakeUnique(); - if (!cell) - return nullptr; - - switch (val.type().code()) { - case ValType::I32: cell->i32 = val.i32(); break; - case ValType::I64: cell->i64 = val.i64(); break; - case ValType::F32: cell->f32 = val.f32(); break; - case ValType::F64: cell->f64 = val.f64(); break; - case ValType::AnyRef: cell->ptr = (JSObject*)val.ptr(); break; - default: MOZ_CRASH(); - } - RootedObject proto(cx, &cx->global()->getPrototype(JSProto_WasmGlobal).toObject()); AutoSetNewObjectMetadata metadata(cx); @@ -2179,9 +2192,41 @@ WasmGlobalObject::create(JSContext* cx, const LitVal& val, bool isMutable) MOZ_ASSERT(obj->isTenured(), "assumed by set_global post barriers"); + // It's simpler to initialize the cell after the object has been created, + // to avoid needing to root the cell before the object creation. + + Cell* cell = js_new(); + if (!cell) + return nullptr; + + const Val& val = hval.get(); + switch (val.type().code()) { + case ValType::I32: + cell->i32 = val.i32(); + break; + case ValType::I64: + cell->i64 = val.i64(); + break; + case ValType::F32: + cell->f32 = val.f32(); + break; + case ValType::F64: + cell->f64 = val.f64(); + break; + case ValType::AnyRef: + MOZ_ASSERT(!cell->ptr, "no prebarriers needed"); + cell->ptr = val.ptr(); + if (cell->ptr) + JSObject::writeBarrierPost(&cell->ptr, nullptr, cell->ptr); + break; + WASM_CRASH_IF_SIMD_TYPES; + case ValType::Ref: + MOZ_CRASH("Ref NYI"); + } + obj->initReservedSlot(TYPE_SLOT, Int32Value(int32_t(val.type().bitsUnsafe()))); obj->initReservedSlot(MUTABLE_SLOT, JS::BooleanValue(isMutable)); - obj->initReservedSlot(CELL_SLOT, PrivateValue(cell.release())); + obj->initReservedSlot(CELL_SLOT, PrivateValue(cell)); return obj; } @@ -2243,20 +2288,20 @@ WasmGlobalObject::construct(JSContext* cx, unsigned argc, Value* vp) bool isMutable = ToBoolean(mutableVal); // Extract the initial value, or provide a suitable default. - // Guard against control flow mistakes below failing to set |globalVal|. - LitVal globalVal = LitVal(uint32_t(0)); + RootedVal globalVal(cx); if (args.length() >= 2) { RootedValue valueVal(cx, args.get(1)); if (!ToWebAssemblyValue(cx, globalType, valueVal, &globalVal)) return false; } else { switch (globalType.code()) { - case ValType::I32: /* set above */ break; - case ValType::I64: globalVal = LitVal(uint64_t(0)); break; - case ValType::F32: globalVal = LitVal(float(0.0)); break; - case ValType::F64: globalVal = LitVal(double(0.0)); break; - case ValType::AnyRef: globalVal = LitVal(ValType::AnyRef, nullptr); break; - default: MOZ_CRASH(); + case ValType::I32: globalVal = Val(uint32_t(0)); break; + case ValType::I64: globalVal = Val(uint64_t(0)); break; + case ValType::F32: globalVal = Val(float(0.0)); break; + case ValType::F64: globalVal = Val(double(0.0)); break; + case ValType::AnyRef: globalVal = Val(nullptr); break; + WASM_CRASH_IF_SIMD_TYPES; + case ValType::Ref: MOZ_CRASH("Ref NYI"); } } @@ -2282,14 +2327,16 @@ WasmGlobalObject::valueGetterImpl(JSContext* cx, const CallArgs& args) case ValType::F32: case ValType::F64: case ValType::AnyRef: - args.rval().set(args.thisv().toObject().as().value()); + args.rval().set(args.thisv().toObject().as().value(cx)); return true; case ValType::I64: JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_I64_TYPE); return false; - default: - MOZ_CRASH(); + WASM_CRASH_IF_SIMD_TYPES; + case ValType::Ref: + MOZ_CRASH("Ref NYI"); } + MOZ_CRASH(); } /* static */ bool @@ -2313,17 +2360,34 @@ WasmGlobalObject::valueSetterImpl(JSContext* cx, const CallArgs& args) return false; } - LitVal val; + RootedVal val(cx); if (!ToWebAssemblyValue(cx, global->type(), args.get(0), &val)) return false; Cell* cell = global->cell(); switch (global->type().code()) { - case ValType::I32: cell->i32 = val.i32(); break; - case ValType::F32: cell->f32 = val.f32(); break; - case ValType::F64: cell->f64 = val.f64(); break; - case ValType::AnyRef: cell->ptr = (JSObject*)val.ptr(); break; - default: MOZ_CRASH(); + case ValType::I32: + cell->i32 = val.get().i32(); + break; + case ValType::F32: + cell->f32 = val.get().f32(); + break; + case ValType::F64: + cell->f64 = val.get().f64(); + break; + case ValType::AnyRef: { + JSObject* prevPtr = cell->ptr; + JSObject::writeBarrierPre(prevPtr); + cell->ptr = val.get().ptr(); + if (cell->ptr) + JSObject::writeBarrierPost(&cell->ptr, prevPtr, cell->ptr); + break; + } + WASM_CRASH_IF_SIMD_TYPES; + case ValType::I64: + MOZ_CRASH("unexpected i64 when setting global's value"); + case ValType::Ref: + MOZ_CRASH("Ref NYI"); } args.rval().setUndefined(); @@ -2365,27 +2429,29 @@ WasmGlobalObject::isMutable() const return getReservedSlot(MUTABLE_SLOT).toBoolean(); } -LitVal -WasmGlobalObject::val() const +void +WasmGlobalObject::val(MutableHandleVal outval) const { Cell* cell = this->cell(); - LitVal val; switch (type().code()) { - case ValType::I32: val = LitVal(uint32_t(cell->i32)); break; - case ValType::I64: val = LitVal(uint64_t(cell->i64)); break; - case ValType::F32: val = LitVal(cell->f32); break; - case ValType::F64: val = LitVal(cell->f64); break; - case ValType::AnyRef: val = LitVal(ValType::AnyRef, (void*)cell->ptr); break; - default: MOZ_CRASH(); + case ValType::I32: outval.set(Val(uint32_t(cell->i32))); return; + case ValType::I64: outval.set(Val(uint64_t(cell->i64))); return; + case ValType::F32: outval.set(Val(cell->f32)); return; + case ValType::F64: outval.set(Val(cell->f64)); return; + case ValType::AnyRef: outval.set(Val(cell->ptr)); return; + WASM_CRASH_IF_SIMD_TYPES; + case ValType::Ref: MOZ_CRASH("Ref NYI"); } - return val; + MOZ_CRASH("unexpected Global type"); } Value -WasmGlobalObject::value() const +WasmGlobalObject::value(JSContext* cx) const { // ToJSValue crashes on I64; this is desirable. - return ToJSValue(val()); + RootedVal result(cx); + val(&result); + return ToJSValue(result.get()); } WasmGlobalObject::Cell* diff --git a/js/src/wasm/WasmJS.h b/js/src/wasm/WasmJS.h index 6f80265e1374..50f9afb004d6 100644 --- a/js/src/wasm/WasmJS.h +++ b/js/src/wasm/WasmJS.h @@ -137,12 +137,11 @@ class WasmGlobalObject : public NativeObject // For exposed globals the Cell holds the value of the global; the // instance's global area holds a pointer to the Cell. union Cell { - int32_t i32; - int64_t i64; - float f32; - double f64; - GCPtrObject ptr; - + int32_t i32; + int64_t i64; + float f32; + double f64; + JSObject* ptr; Cell() : i64(0) {} ~Cell() {} }; @@ -154,13 +153,13 @@ class WasmGlobalObject : public NativeObject static const JSFunctionSpec static_methods[]; static bool construct(JSContext*, unsigned, Value*); - static WasmGlobalObject* create(JSContext* cx, const wasm::LitVal& value, bool isMutable); + static WasmGlobalObject* create(JSContext* cx, wasm::HandleVal value, bool isMutable); wasm::ValType type() const; - wasm::LitVal val() const; + void val(wasm::MutableHandleVal outval) const; bool isMutable() const; // value() will MOZ_CRASH if the type is int64 - Value value() const; + Value value(JSContext* cx) const; Cell* cell() const; }; @@ -219,7 +218,7 @@ class WasmInstanceObject : public NativeObject Vector, 0, SystemAllocPolicy>&& tables, Handle funcImports, const wasm::GlobalDescVector& globals, - const wasm::LitValVector& globalImportValues, + wasm::HandleValVector globalImportValues, const WasmGlobalObjectVector& globalObjs, HandleObject proto); void initExportsObj(JSObject& exportsObj); diff --git a/js/src/wasm/WasmModule.cpp b/js/src/wasm/WasmModule.cpp index 7c768b66e11c..c1f9d0ce6907 100644 --- a/js/src/wasm/WasmModule.cpp +++ b/js/src/wasm/WasmModule.cpp @@ -721,13 +721,13 @@ Module::extractCode(JSContext* cx, Tier tier, MutableHandleValue vp) const } static uint32_t -EvaluateInitExpr(const LitValVector& globalImportValues, InitExpr initExpr) +EvaluateInitExpr(HandleValVector globalImportValues, InitExpr initExpr) { switch (initExpr.kind()) { case InitExpr::Kind::Constant: return initExpr.val().i32(); case InitExpr::Kind::GetGlobal: - return globalImportValues[initExpr.globalIndex()].i32(); + return globalImportValues[initExpr.globalIndex()].get().i32(); } MOZ_CRASH("bad initializer expression"); @@ -738,7 +738,7 @@ Module::initSegments(JSContext* cx, HandleWasmInstanceObject instanceObj, Handle funcImports, HandleWasmMemoryObject memoryObj, - const LitValVector& globalImportValues) const + HandleValVector globalImportValues) const { Instance& instance = instanceObj->instance(); const SharedTableVector& tables = instance.tables(); @@ -1010,39 +1010,44 @@ Module::instantiateTable(JSContext* cx, MutableHandleWasmTableObject tableObj, return true; } -static LitVal -ExtractGlobalValue(const LitValVector& globalImportValues, uint32_t globalIndex, - const GlobalDesc& global) +static void +ExtractGlobalValue(HandleValVector globalImportValues, uint32_t globalIndex, + const GlobalDesc& global, MutableHandleVal result) { switch (global.kind()) { case GlobalKind::Import: { - return globalImportValues[globalIndex]; + result.set(Val(globalImportValues[globalIndex])); + return; } case GlobalKind::Variable: { const InitExpr& init = global.initExpr(); switch (init.kind()) { case InitExpr::Kind::Constant: - return init.val(); + result.set(Val(init.val())); + return; case InitExpr::Kind::GetGlobal: - return globalImportValues[init.globalIndex()]; + result.set(Val(globalImportValues[init.globalIndex()])); + return; } break; } case GlobalKind::Constant: { - return global.constantValue(); + result.set(Val(global.constantValue())); + return; } } MOZ_CRASH("Not a global value"); } static bool -EnsureGlobalObject(JSContext* cx, const LitValVector& globalImportValues, size_t globalIndex, +EnsureGlobalObject(JSContext* cx, HandleValVector globalImportValues, size_t globalIndex, const GlobalDesc& global, WasmGlobalObjectVector& globalObjs) { if (globalIndex < globalObjs.length() && globalObjs[globalIndex]) return true; - LitVal val = ExtractGlobalValue(globalImportValues, globalIndex, global); + RootedVal val(cx); + ExtractGlobalValue(globalImportValues, globalIndex, global, &val); RootedWasmGlobalObject go(cx, WasmGlobalObject::create(cx, val, global.isMutable())); if (!go) return false; @@ -1057,7 +1062,7 @@ EnsureGlobalObject(JSContext* cx, const LitValVector& globalImportValues, size_t } bool -Module::instantiateGlobals(JSContext* cx, const LitValVector& globalImportValues, +Module::instantiateGlobals(JSContext* cx, HandleValVector globalImportValues, WasmGlobalObjectVector& globalObjs) const { // If there are exported globals that aren't in globalObjs because they @@ -1189,7 +1194,7 @@ Module::instantiate(JSContext* cx, Handle funcImports, HandleWasmTableObject tableImport, HandleWasmMemoryObject memoryImport, - const LitValVector& globalImportValues, + HandleValVector globalImportValues, WasmGlobalObjectVector& globalObjs, HandleObject instanceProto, MutableHandleWasmInstanceObject instance) const diff --git a/js/src/wasm/WasmModule.h b/js/src/wasm/WasmModule.h index 42d7de0612e4..21a804b3a69c 100644 --- a/js/src/wasm/WasmModule.h +++ b/js/src/wasm/WasmModule.h @@ -151,13 +151,13 @@ class Module : public JS::WasmModule bool instantiateTable(JSContext* cx, MutableHandleWasmTableObject table, SharedTableVector* tables) const; - bool instantiateGlobals(JSContext* cx, const LitValVector& globalImportValues, + bool instantiateGlobals(JSContext* cx, HandleValVector globalImportValues, WasmGlobalObjectVector& globalObjs) const; bool initSegments(JSContext* cx, HandleWasmInstanceObject instance, Handle funcImports, HandleWasmMemoryObject memory, - const LitValVector& globalImportValues) const; + HandleValVector globalImportValues) const; class Tier2GeneratorTaskImpl; void notifyCompilationListeners(); @@ -207,7 +207,7 @@ class Module : public JS::WasmModule Handle funcImports, HandleWasmTableObject tableImport, HandleWasmMemoryObject memoryImport, - const LitValVector& globalImportValues, + HandleValVector globalImportValues, WasmGlobalObjectVector& globalObjs, HandleObject instanceProto, MutableHandleWasmInstanceObject instanceObj) const; diff --git a/js/src/wasm/WasmTypes.cpp b/js/src/wasm/WasmTypes.cpp index 0fc34746b040..6e49ea127af5 100644 --- a/js/src/wasm/WasmTypes.cpp +++ b/js/src/wasm/WasmTypes.cpp @@ -60,8 +60,29 @@ static_assert(MaxMemoryAccessSize <= 64, "MaxMemoryAccessSize too high"); static_assert((MaxMemoryAccessSize & (MaxMemoryAccessSize-1)) == 0, "MaxMemoryAccessSize is not a power of two"); +Val::Val(const LitVal& val) +{ + type_ = val.type(); + switch (type_.code()) { + case ValType::I32: u.i32_ = val.i32(); return; + case ValType::F32: u.f32_ = val.f32(); return; + case ValType::I64: u.i64_ = val.i64(); return; + case ValType::F64: u.f64_ = val.f64(); return; + case ValType::I8x16: + case ValType::B8x16: + case ValType::I16x8: + case ValType::B16x8: + case ValType::I32x4: + case ValType::F32x4: + case ValType::B32x4: memcpy(&u, val.rawSimd(), jit::Simd128DataSize); return; + case ValType::AnyRef: u.ptr_ = val.ptr(); return; + case ValType::Ref: break; + } + MOZ_CRASH(); +} + void -LitVal::writePayload(uint8_t* dst) const +Val::writePayload(uint8_t* dst) const { switch (type_.code()) { case ValType::I32: @@ -83,10 +104,27 @@ LitVal::writePayload(uint8_t* dst) const return; case ValType::Ref: case ValType::AnyRef: - memcpy(dst, &u.ptr_, sizeof(intptr_t)); + MOZ_ASSERT(*(JSObject**)dst == nullptr, "should be null so no need for a pre-barrier"); + memcpy(dst, &u.ptr_, sizeof(JSObject*)); + // Either the written location is in the global data section in the + // WasmInstanceObject, or the Cell of a WasmGlobalObject: + // - WasmInstanceObjects are always tenured and u.ptr_ may point to a + // nursery object, so we need a post-barrier since the global data of + // an instance is effectively a field of the WasmInstanceObject. + // - WasmGlobalObjects are always tenured, and they have a Cell field, + // so a post-barrier may be needed for the same reason as above. + if (u.ptr_) + JSObject::writeBarrierPost((JSObject**)dst, nullptr, u.ptr_); return; } - MOZ_CRASH("unexpected LitVal type"); + MOZ_CRASH("unexpected Val type"); +} + +void +Val::trace(JSTracer* trc) +{ + if (type_.isValid() && type_ == ValType::AnyRef && u.ptr_) + TraceManuallyBarrieredEdge(trc, &u.ptr_, "wasm anyref global"); } bool diff --git a/js/src/wasm/WasmTypes.h b/js/src/wasm/WasmTypes.h index dbd4fe60daad..ad36d62a11e8 100644 --- a/js/src/wasm/WasmTypes.h +++ b/js/src/wasm/WasmTypes.h @@ -775,21 +775,22 @@ enum class HasGcTypes class LitVal { + protected: ValType type_; union U { - uint32_t i32_; - uint64_t i64_; - float f32_; - double f64_; - I8x16 i8x16_; - I16x8 i16x8_; - I32x4 i32x4_; - F32x4 f32x4_; - intptr_t ptr_; + uint32_t i32_; + uint64_t i64_; + float f32_; + double f64_; + I8x16 i8x16_; + I16x8 i16x8_; + I32x4 i32x4_; + F32x4 f32x4_; + JSObject* ptr_; } u; public: - LitVal() = default; + LitVal() : type_(), u{} {} explicit LitVal(uint32_t i32) : type_(ValType::I32) { u.i32_ = i32; } explicit LitVal(uint64_t i64) : type_(ValType::I64) { u.i64_ = i64; } @@ -797,9 +798,10 @@ class LitVal explicit LitVal(float f32) : type_(ValType::F32) { u.f32_ = f32; } explicit LitVal(double f64) : type_(ValType::F64) { u.f64_ = f64; } - explicit LitVal(ValType refType, void* ptr) : type_(refType) { + explicit LitVal(ValType refType, JSObject* ptr) : type_(refType) { MOZ_ASSERT(refType.isRefOrAnyRef()); - u.ptr_ = intptr_t(ptr); + MOZ_ASSERT(ptr == nullptr, "use Val for non-nullptr ref types to get tracing"); + u.ptr_ = ptr; } explicit LitVal(const I8x16& i8x16, ValType type = ValType::I8x16) : type_(type) { @@ -826,7 +828,7 @@ class LitVal uint64_t i64() const { MOZ_ASSERT(type_ == ValType::I64); return u.i64_; } const float& f32() const { MOZ_ASSERT(type_ == ValType::F32); return u.f32_; } const double& f64() const { MOZ_ASSERT(type_ == ValType::F64); return u.f64_; } - intptr_t ptr() const { MOZ_ASSERT(type_.isRefOrAnyRef()); return u.ptr_; } + JSObject* ptr() const { MOZ_ASSERT(type_.isRefOrAnyRef()); return u.ptr_; } const I8x16& i8x16() const { MOZ_ASSERT(type_ == ValType::I8x16 || type_ == ValType::B8x16); @@ -844,12 +846,41 @@ class LitVal MOZ_ASSERT(type_ == ValType::F32x4); return u.f32x4_; } - - void writePayload(uint8_t* dst) const; + // To be used only by Val. + const void* rawSimd() const { return &u.i32x4_; } }; typedef Vector LitValVector; +// A Val is a LitVal that can contain pointers to JSObjects, thanks to their +// trace implementation. Since a Val is able to store a pointer to a JSObject, +// it needs to be traced during compilation in case the pointee is moved. +// The classic shorthands for Rooted things are defined after this class, for +// easier usage. + +class MOZ_NON_PARAM Val : public LitVal +{ + public: + Val() : LitVal() {} + explicit Val(const LitVal& val); + explicit Val(uint32_t i32) : LitVal(i32) {} + explicit Val(uint64_t i64) : LitVal(i64) {} + explicit Val(float f32) : LitVal(f32) {} + explicit Val(double f64) : LitVal(f64) {} + explicit Val(JSObject* obj) : LitVal(ValType::AnyRef, nullptr) { u.ptr_ = obj; } + void writePayload(uint8_t* dst) const; + void trace(JSTracer* trc); +}; + +typedef Rooted RootedVal; +typedef Handle HandleVal; +typedef MutableHandle MutableHandleVal; + +typedef GCVector GCVectorVal; +typedef Rooted RootedValVector; +typedef Handle HandleValVector; +typedef MutableHandle MutableHandleValVector; + // The FuncType class represents a WebAssembly function signature which takes a // list of value types and returns an expression type. The engine uses two // in-memory representations of the argument Vector's memory (when elements do