From a45951f2cd95b6124a944216154933406ebbc9ba Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Fri, 30 Mar 2018 16:31:40 +0200 Subject: [PATCH] Bug 1445272: Add (entry/exit) stubs support for anyref; r=luke --HG-- extra : rebase_source : 1d4e3f43414a718d739efbe4907c1a1c85f261f8 --- js/src/vm/JSFunction.h | 1 + js/src/wasm/WasmBuiltins.cpp | 4 ++ js/src/wasm/WasmCode.cpp | 26 ++++++++++--- js/src/wasm/WasmFrameIter.cpp | 1 + js/src/wasm/WasmInstance.cpp | 71 +++++++++++++++++++++++++++++------ js/src/wasm/WasmInstance.h | 1 + js/src/wasm/WasmJS.cpp | 16 +++++++- js/src/wasm/WasmStubs.cpp | 38 +++++++++++++++++-- js/src/wasm/WasmTypes.h | 14 ++++++- 9 files changed, 147 insertions(+), 25 deletions(-) diff --git a/js/src/vm/JSFunction.h b/js/src/vm/JSFunction.h index 87b2b7a65623..104280c2ec70 100644 --- a/js/src/vm/JSFunction.h +++ b/js/src/vm/JSFunction.h @@ -95,6 +95,7 @@ class JSFunction : public js::NativeObject NATIVE_CLASS_CTOR = NATIVE_FUN | CONSTRUCTOR | CLASSCONSTRUCTOR_KIND, ASMJS_CTOR = ASMJS_KIND | NATIVE_CTOR, ASMJS_LAMBDA_CTOR = ASMJS_KIND | NATIVE_CTOR | LAMBDA, + ASMJS_NATIVE = ASMJS_KIND | NATIVE_FUN, WASM_FUN = NATIVE_FUN | WASM_OPTIMIZED, INTERPRETED_METHOD = INTERPRETED | METHOD_KIND, INTERPRETED_METHOD_GENERATOR_OR_ASYNC = INTERPRETED | METHOD_KIND, diff --git a/js/src/wasm/WasmBuiltins.cpp b/js/src/wasm/WasmBuiltins.cpp index 846830d311d6..3ae6c83e5d5b 100644 --- a/js/src/wasm/WasmBuiltins.cpp +++ b/js/src/wasm/WasmBuiltins.cpp @@ -536,6 +536,9 @@ AddressOf(SymbolicAddress imm, ABIFunctionType* abiType) case SymbolicAddress::CallImport_F64: *abiType = Args_General4; return FuncCast(Instance::callImport_f64, *abiType); + case SymbolicAddress::CallImport_Ref: + *abiType = Args_General4; + return FuncCast(Instance::callImport_ref, *abiType); case SymbolicAddress::CoerceInPlace_ToInt32: *abiType = Args_General1; return FuncCast(CoerceInPlace_ToInt32, *abiType); @@ -690,6 +693,7 @@ wasm::NeedsBuiltinThunk(SymbolicAddress sym) case SymbolicAddress::CallImport_I32: case SymbolicAddress::CallImport_I64: case SymbolicAddress::CallImport_F64: + case SymbolicAddress::CallImport_Ref: case SymbolicAddress::CoerceInPlace_ToInt32: // GenerateImportJitExit case SymbolicAddress::CoerceInPlace_ToNumber: #if defined(JS_CODEGEN_MIPS32) diff --git a/js/src/wasm/WasmCode.cpp b/js/src/wasm/WasmCode.cpp index 88564c7e1f66..5be502347469 100644 --- a/js/src/wasm/WasmCode.cpp +++ b/js/src/wasm/WasmCode.cpp @@ -609,22 +609,25 @@ LazyStubSegment::addStubs(size_t codeLength, const Uint32Vector& funcExportIndic return false; size_t i = 0; - for (DebugOnly funcExportIndex : funcExportIndices) { + for (uint32_t funcExportIndex : funcExportIndices) { const CodeRange& interpRange = codeRanges[i]; MOZ_ASSERT(interpRange.isInterpEntry()); MOZ_ASSERT(interpRange.funcIndex() == funcExports[funcExportIndex].funcIndex()); codeRanges_.infallibleAppend(interpRange); codeRanges_.back().offsetBy(offsetInSegment); + i++; - const CodeRange& jitRange = codeRanges[i + 1]; + if (funcExports[funcExportIndex].sig().temporarilyUnsupportedAnyRef()) + continue; + + const CodeRange& jitRange = codeRanges[i]; MOZ_ASSERT(jitRange.isJitEntry()); MOZ_ASSERT(jitRange.funcIndex() == interpRange.funcIndex()); codeRanges_.infallibleAppend(jitRange); codeRanges_.back().offsetBy(offsetInSegment); - - i += 2; + i++; } return true; @@ -673,8 +676,10 @@ LazyStubTier::createMany(const Uint32Vector& funcExportIndices, const CodeTier& uint8_t* moduleSegmentBase = codeTier.segment().base(); CodeRangeVector codeRanges; + DebugOnly numExpectedRanges = 0; for (uint32_t funcExportIndex : funcExportIndices) { const FuncExport& fe = funcExports[funcExportIndex]; + numExpectedRanges += fe.sig().temporarilyUnsupportedAnyRef() ? 1 : 2; void* calleePtr = moduleSegmentBase + moduleRanges[fe.interpCodeRangeIndex()].funcNormalEntry(); Maybe callee; @@ -682,7 +687,7 @@ LazyStubTier::createMany(const Uint32Vector& funcExportIndices, const CodeTier& if (!GenerateEntryStubs(masm, funcExportIndex, fe, callee, /* asmjs*/ false, &codeRanges)) return false; } - MOZ_ASSERT(codeRanges.length() == 2 * funcExportIndices.length(), "two entries per function"); + MOZ_ASSERT(codeRanges.length() == numExpectedRanges, "incorrect number of entries per function"); masm.finish(); @@ -743,7 +748,9 @@ LazyStubTier::createMany(const Uint32Vector& funcExportIndices, const CodeTier& fe.funcIndex(), &exportIndex)); MOZ_ALWAYS_TRUE(exports_.insert(exports_.begin() + exportIndex, Move(lazyExport))); - interpRangeIndex += 2; + // Functions with anyref in their sig have only one entry (interp). + // All other functions get an extra jit entry. + interpRangeIndex += fe.sig().temporarilyUnsupportedAnyRef() ? 1 : 2; } return true; @@ -763,6 +770,13 @@ LazyStubTier::createOne(uint32_t funcExportIndex, const CodeTier& codeTier) const UniqueLazyStubSegment& segment = stubSegments_[stubSegmentIndex]; const CodeRangeVector& codeRanges = segment->codeRanges(); + // Functions that have anyref in their sig don't get a jit entry. + if (codeTier.metadata().funcExports[funcExportIndex].sig().temporarilyUnsupportedAnyRef()) { + MOZ_ASSERT(codeRanges.length() >= 1); + MOZ_ASSERT(codeRanges.back().isInterpEntry()); + return true; + } + MOZ_ASSERT(codeRanges.length() >= 2); MOZ_ASSERT(codeRanges[codeRanges.length() - 2].isInterpEntry()); diff --git a/js/src/wasm/WasmFrameIter.cpp b/js/src/wasm/WasmFrameIter.cpp index 3807e10f8ea9..d1146bbb37ab 100644 --- a/js/src/wasm/WasmFrameIter.cpp +++ b/js/src/wasm/WasmFrameIter.cpp @@ -1139,6 +1139,7 @@ ThunkedNativeToDescription(SymbolicAddress func) case SymbolicAddress::CallImport_I32: case SymbolicAddress::CallImport_I64: case SymbolicAddress::CallImport_F64: + case SymbolicAddress::CallImport_Ref: case SymbolicAddress::CoerceInPlace_ToInt32: case SymbolicAddress::CoerceInPlace_ToNumber: MOZ_ASSERT(!NeedsBuiltinThunk(func), "not in sync with NeedsBuiltinThunk"); diff --git a/js/src/wasm/WasmInstance.cpp b/js/src/wasm/WasmInstance.cpp index dedcb4f5001a..adea424b2407 100644 --- a/js/src/wasm/WasmInstance.cpp +++ b/js/src/wasm/WasmInstance.cpp @@ -133,6 +133,10 @@ Instance::callImport(JSContext* cx, uint32_t funcImportIndex, unsigned argc, con case ValType::F64: args[i].set(JS::CanonicalizedDoubleValue(*(double*)&argv[i])); break; + case ValType::AnyRef: { + args[i].set(ObjectOrNullValue(*(JSObject**)&argv[i])); + break; + } case ValType::I64: case ValType::I8x16: case ValType::I16x8: @@ -188,23 +192,28 @@ Instance::callImport(JSContext* cx, uint32_t funcImportIndex, unsigned argc, con if (!TypeScript::ThisTypes(script)->hasType(TypeSet::UndefinedType())) return true; + // Functions with anyref in signature don't have a jit exit at the moment. + if (fi.sig().temporarilyUnsupportedAnyRef()) + return true; + const ValTypeVector& importArgs = fi.sig().args(); size_t numKnownArgs = Min(importArgs.length(), importFun->nargs()); for (uint32_t i = 0; i < numKnownArgs; i++) { TypeSet::Type type = TypeSet::UnknownType(); switch (importArgs[i]) { - case ValType::I32: type = TypeSet::Int32Type(); break; - case ValType::F32: type = TypeSet::DoubleType(); break; - case ValType::F64: type = TypeSet::DoubleType(); break; - case ValType::I64: MOZ_CRASH("NYI"); - case ValType::I8x16: MOZ_CRASH("NYI"); - case ValType::I16x8: MOZ_CRASH("NYI"); - case ValType::I32x4: MOZ_CRASH("NYI"); - case ValType::F32x4: MOZ_CRASH("NYI"); - case ValType::B8x16: MOZ_CRASH("NYI"); - case ValType::B16x8: MOZ_CRASH("NYI"); - case ValType::B32x4: MOZ_CRASH("NYI"); + case ValType::I32: type = TypeSet::Int32Type(); break; + case ValType::F32: type = TypeSet::DoubleType(); break; + case ValType::F64: type = TypeSet::DoubleType(); break; + case ValType::AnyRef: MOZ_CRASH("case guarded above"); + case ValType::I64: MOZ_CRASH("NYI"); + case ValType::I8x16: MOZ_CRASH("NYI"); + case ValType::I16x8: MOZ_CRASH("NYI"); + case ValType::I32x4: MOZ_CRASH("NYI"); + case ValType::F32x4: MOZ_CRASH("NYI"); + case ValType::B8x16: MOZ_CRASH("NYI"); + case ValType::B16x8: MOZ_CRASH("NYI"); + case ValType::B32x4: MOZ_CRASH("NYI"); } if (!TypeScript::ArgTypes(script, i)->hasType(type)) return true; @@ -265,6 +274,31 @@ Instance::callImport_f64(Instance* instance, int32_t funcImportIndex, int32_t ar return ToNumber(cx, rval, (double*)argv); } +static bool +ToRef(JSContext* cx, HandleValue val, void* addr) +{ + if (val.isNull()) { + *(JSObject**)addr = nullptr; + return true; + } + + JSObject* obj = ToObject(cx, val); + if (!obj) + return false; + *(JSObject**)addr = obj; + return true; +} + +/* static */ int32_t +Instance::callImport_ref(Instance* instance, int32_t funcImportIndex, int32_t argc, uint64_t* argv) +{ + JSContext* cx = TlsContext.get(); + RootedValue rval(cx); + if (!instance->callImport(cx, funcImportIndex, argc, argv, &rval)) + return false; + return ToRef(cx, rval, argv); +} + /* static */ uint32_t Instance::growMemory_i32(Instance* instance, uint32_t delta) { @@ -672,6 +706,11 @@ Instance::callExport(JSContext* cx, uint32_t funcIndex, CallArgs args) if (!ToNumber(cx, v, (double*)&exportArgs[i])) return false; break; + case ValType::AnyRef: { + if (!ToRef(cx, v, &exportArgs[i])) + return false; + break; + } case ValType::I8x16: { SimdConstant simd; if (!ToSimdConstant(cx, v, &simd)) @@ -755,6 +794,8 @@ Instance::callExport(JSContext* cx, uint32_t funcIndex, CallArgs args) } void* retAddr = &exportArgs[0]; + + bool expectsObject = false; JSObject* retObj = nullptr; switch (func.sig().ret()) { case ExprType::Void: @@ -771,6 +812,10 @@ Instance::callExport(JSContext* cx, uint32_t funcIndex, CallArgs args) case ExprType::F64: args.rval().set(NumberValue(*(double*)retAddr)); break; + case ExprType::AnyRef: + retObj = *(JSObject**)retAddr; + expectsObject = true; + break; case ExprType::I8x16: retObj = CreateSimd(cx, (int8_t*)retAddr); if (!retObj) @@ -810,7 +855,9 @@ Instance::callExport(JSContext* cx, uint32_t funcIndex, CallArgs args) MOZ_CRASH("Limit"); } - if (retObj) + if (expectsObject) + args.rval().set(ObjectOrNullValue(retObj)); + else if (retObj) args.rval().set(ObjectValue(*retObj)); return true; diff --git a/js/src/wasm/WasmInstance.h b/js/src/wasm/WasmInstance.h index 2ea6fe795442..6f7c7620476b 100644 --- a/js/src/wasm/WasmInstance.h +++ b/js/src/wasm/WasmInstance.h @@ -164,6 +164,7 @@ class Instance static int32_t callImport_i32(Instance*, int32_t, int32_t, uint64_t*); static int32_t callImport_i64(Instance*, int32_t, int32_t, uint64_t*); static int32_t callImport_f64(Instance*, int32_t, int32_t, uint64_t*); + static int32_t callImport_ref(Instance*, int32_t, int32_t, uint64_t*); static uint32_t growMemory_i32(Instance* instance, uint32_t delta); static uint32_t currentMemory_i32(Instance* instance); static int32_t wait_i32(Instance* instance, uint32_t byteOffset, int32_t value, int64_t timeout); diff --git a/js/src/wasm/WasmJS.cpp b/js/src/wasm/WasmJS.cpp index 2a3cecf2d884..3321e432d386 100644 --- a/js/src/wasm/WasmJS.cpp +++ b/js/src/wasm/WasmJS.cpp @@ -1291,11 +1291,23 @@ WasmInstanceObject::getExportedFunction(JSContext* cx, HandleWasmInstanceObject RootedAtom name(cx, NumberToAtom(cx, funcIndex)); if (!name) return false; + + // Functions with anyref don't have jit entries yet, so they should + // mostly behave like asm.js functions. Pretend it's the case, until + // jit entries are implemented. + JSFunction::Flags flags = sig.temporarilyUnsupportedAnyRef() + ? JSFunction::ASMJS_NATIVE + : JSFunction::WASM_FUN; + fun.set(NewNativeFunction(cx, WasmCall, numArgs, name, gc::AllocKind::FUNCTION_EXTENDED, - SingletonObject, JSFunction::WASM_FUN)); + SingletonObject, flags)); if (!fun) return false; - fun->setWasmJitEntry(instance.code().getAddressOfJitEntry(funcIndex)); + + if (sig.temporarilyUnsupportedAnyRef()) + fun->setAsmJSIndex(funcIndex); + else + fun->setWasmJitEntry(instance.code().getAddressOfJitEntry(funcIndex)); } fun->setExtendedSlot(FunctionExtended::WASM_INSTANCE_SLOT, ObjectValue(*instanceObj)); diff --git a/js/src/wasm/WasmStubs.cpp b/js/src/wasm/WasmStubs.cpp index a91a5dbedd11..1e8567b00a94 100644 --- a/js/src/wasm/WasmStubs.cpp +++ b/js/src/wasm/WasmStubs.cpp @@ -92,6 +92,10 @@ SetupABIArguments(MacroAssembler& masm, const FuncExport& fe, Register argv, Reg masm.load32(src, iter->gpr()); else if (type == MIRType::Int64) masm.load64(src, iter->gpr64()); + else if (type == MIRType::Pointer) + masm.loadPtr(src, iter->gpr()); + else + MOZ_CRASH("unknown GPR type"); break; #ifdef JS_CODEGEN_REGISTER_PAIR case ABIArg::GPR_PAIR: @@ -148,6 +152,10 @@ SetupABIArguments(MacroAssembler& masm, const FuncExport& fe, Register argv, Reg #endif break; } + case MIRType::Pointer: + masm.loadPtr(src, scratch); + masm.storePtr(scratch, Address(masm.getStackPointer(), iter->offsetFromArgBase())); + break; case MIRType::Double: masm.loadDouble(src, ScratchDoubleReg); masm.storeDouble(ScratchDoubleReg, @@ -204,6 +212,9 @@ StoreABIReturn(MacroAssembler& masm, const FuncExport& fe, Register argv) masm.canonicalizeDouble(ReturnDoubleReg); masm.storeDouble(ReturnDoubleReg, Address(argv, 0)); break; + case ExprType::AnyRef: + masm.storePtr(ReturnReg, Address(argv, 0)); + break; case ExprType::I8x16: case ExprType::I16x8: case ExprType::I32x4: @@ -758,6 +769,9 @@ GenerateJitEntry(MacroAssembler& masm, size_t funcExportIndex, const FuncExport& masm.canonicalizeDouble(ReturnDoubleReg); masm.boxDouble(ReturnDoubleReg, JSReturnOperand, ScratchDoubleReg); break; + case ExprType::AnyRef: + MOZ_CRASH("return anyref in jitentry NYI"); + break; case ExprType::I64: case ExprType::I8x16: case ExprType::I16x8: @@ -845,6 +859,9 @@ StackCopy(MacroAssembler& masm, MIRType type, Register scratch, Address src, Add masm.load64(src, scratch64); masm.store64(scratch64, dst); #endif + } else if (type == MIRType::Pointer) { + masm.loadPtr(src, scratch); + masm.storePtr(scratch, dst); } else if (type == MIRType::Float32) { masm.loadFloat32(src, ScratchFloat32Reg); masm.storeFloat32(ScratchFloat32Reg, dst); @@ -878,8 +895,10 @@ FillArgumentArray(MacroAssembler& masm, const ValTypeVector& args, unsigned argO masm.breakpoint(); else masm.store64(i->gpr64(), dst); - } else { - MOZ_CRASH("unexpected input type?"); + } else if (type == MIRType::Pointer) { + if (toValue) + MOZ_CRASH("generating a jit exit for anyref NYI"); + masm.storePtr(i->gpr(), dst); } break; #ifdef JS_CODEGEN_REGISTER_PAIR @@ -926,6 +945,8 @@ FillArgumentArray(MacroAssembler& masm, const ValTypeVector& args, unsigned argO } else if (type == MIRType::Int64) { // We can't box int64 into Values (yet). masm.breakpoint(); + } else if (type == MIRType::Pointer) { + MOZ_CRASH("generating a jit exit for anyref NYI"); } else { MOZ_ASSERT(IsFloatingPointType(type)); if (type == MIRType::Float32) { @@ -1122,6 +1143,11 @@ GenerateImportInterpExit(MacroAssembler& masm, const FuncImport& fi, uint32_t fu masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel); masm.loadDouble(argv, ReturnDoubleReg); break; + case ExprType::AnyRef: + masm.call(SymbolicAddress::CallImport_Ref); + masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel); + masm.loadPtr(argv, ReturnReg); + break; case ExprType::I8x16: case ExprType::I16x8: case ExprType::I32x4: @@ -1297,6 +1323,9 @@ GenerateImportJitExit(MacroAssembler& masm, const FuncImport& fi, Label* throwLa case ExprType::F64: masm.convertValueToDouble(JSReturnOperand, ReturnDoubleReg, &oolConvert); break; + case ExprType::AnyRef: + MOZ_CRASH("anyref returned by import (jit exit) NYI"); + break; case ExprType::I8x16: case ExprType::I16x8: case ExprType::I32x4: @@ -1712,7 +1741,7 @@ wasm::GenerateEntryStubs(MacroAssembler& masm, size_t funcExportIndex, const Fun if (!codeRanges->emplaceBack(CodeRange::InterpEntry, fe.funcIndex(), offsets)) return false; - if (isAsmJS) + if (isAsmJS || fe.sig().temporarilyUnsupportedAnyRef()) return true; if (!GenerateJitEntry(masm, funcExportIndex, fe, callee, &offsets)) @@ -1748,6 +1777,9 @@ wasm::GenerateStubs(const ModuleEnvironment& env, const FuncImportVector& import if (!code->codeRanges.emplaceBack(CodeRange::ImportInterpExit, funcIndex, interpOffsets)) return false; + if (fi.sig().temporarilyUnsupportedAnyRef()) + continue; + JitExitOffsets jitOffsets; if (!GenerateImportJitExit(masm, fi, &throwLabel, &jitOffsets)) return false; diff --git a/js/src/wasm/WasmTypes.h b/js/src/wasm/WasmTypes.h index 74afe8afc9ab..0ef026f340a0 100644 --- a/js/src/wasm/WasmTypes.h +++ b/js/src/wasm/WasmTypes.h @@ -591,8 +591,17 @@ class Sig bool hasI64ArgOrRet() const { if (ret() == ExprType::I64) return true; - for (ValType a : args()) { - if (a == ValType::I64) + for (ValType arg : args()) { + if (arg == ValType::I64) + return true; + } + return false; + } + bool temporarilyUnsupportedAnyRef() const { + if (ret() == ExprType::AnyRef) + return true; + for (ValType arg : args()) { + if (arg == ValType::AnyRef) return true; } return false; @@ -1459,6 +1468,7 @@ enum class SymbolicAddress CallImport_I32, CallImport_I64, CallImport_F64, + CallImport_Ref, CoerceInPlace_ToInt32, CoerceInPlace_ToNumber, CoerceInPlace_JitEntry,