bool Module::callImport(JSContext* cx, uint32_t importIndex, unsigned argc, const Value* argv, MutableHandleValue rval) { MOZ_ASSERT(dynamicallyLinked_); const Import& import = imports()[importIndex]; RootedValue fval(cx, ObjectValue(*importToExit(import).fun)); if (!Invoke(cx, UndefinedValue(), fval, argc, argv, rval)) return false; ImportExit& exit = importToExit(import); // The exit may already have become optimized. void* jitExitCode = code() + import.jitExitCodeOffset(); if (exit.code == jitExitCode) return true; // Test if the function is JIT compiled. if (!exit.fun->hasScript()) return true; JSScript* script = exit.fun->nonLazyScript(); if (!script->hasBaselineScript()) { MOZ_ASSERT(!script->hasIonScript()); return true; } // Don't enable jit entry when we have a pending ion builder. // Take the interpreter path which will link it and enable // the fast path on the next call. if (script->baselineScript()->hasPendingIonBuilder()) return true; // Currently we can't rectify arguments. Therefore disable if argc is too low. if (exit.fun->nargs() > import.sig().args().length()) return true; // Ensure the argument types are included in the argument TypeSets stored in // the TypeScript. This is necessary for Ion, because the import exit will // use the skip-arg-checks entry point. // // Note that the TypeScript is never discarded while the script has a // BaselineScript, so if those checks hold now they must hold at least until // the BaselineScript is discarded and when that happens the import exit is // patched back. if (!TypeScript::ThisTypes(script)->hasType(TypeSet::UndefinedType())) return true; for (uint32_t i = 0; i < exit.fun->nargs(); i++) { TypeSet::Type type = TypeSet::UnknownType(); switch (import.sig().args()[i]) { case ValType::I32: type = TypeSet::Int32Type(); break; case ValType::I64: MOZ_CRASH("NYI"); case ValType::F32: type = TypeSet::DoubleType(); break; case ValType::F64: type = TypeSet::DoubleType(); break; case ValType::I32x4: MOZ_CRASH("NYI"); case ValType::F32x4: MOZ_CRASH("NYI"); case ValType::B32x4: MOZ_CRASH("NYI"); case ValType::Limit: MOZ_CRASH("Limit"); } if (!TypeScript::ArgTypes(script, i)->hasType(type)) return true; } // Let's optimize it! if (!script->baselineScript()->addDependentWasmModule(cx, *this, importIndex)) return false; exit.code = jitExitCode; exit.baselineScript = script->baselineScript(); return true; }
static EnterJitStatus JS_HAZ_JSNATIVE_CALLER EnterJit(JSContext* cx, RunState& state, uint8_t* code) { MOZ_ASSERT(state.script()->hasBaselineScript()); MOZ_ASSERT(code); MOZ_ASSERT(IsBaselineEnabled(cx)); if (!CheckRecursionLimit(cx)) { return EnterJitStatus::Error; } #ifdef DEBUG // Assert we don't GC before entering JIT code. A GC could discard JIT code // or move the function stored in the CalleeToken (it won't be traced at // this point). We use Maybe<> here so we can call reset() to call the // AutoAssertNoGC destructor before we enter JIT code. mozilla::Maybe<JS::AutoAssertNoGC> nogc; nogc.emplace(cx); #endif JSScript* script = state.script(); size_t numActualArgs; bool constructing; size_t maxArgc; Value* maxArgv; JSObject* envChain; CalleeToken calleeToken; if (state.isInvoke()) { const CallArgs& args = state.asInvoke()->args(); numActualArgs = args.length(); if (TooManyActualArguments(numActualArgs)) { // Too many arguments for Ion. Baseline supports more actual // arguments, so in that case force Baseline code. if (numActualArgs > BASELINE_MAX_ARGS_LENGTH) { return EnterJitStatus::NotEntered; } code = script->baselineScript()->method()->raw(); } constructing = state.asInvoke()->constructing(); maxArgc = args.length() + 1; maxArgv = args.array() - 1; // -1 to include |this| envChain = nullptr; calleeToken = CalleeToToken(&args.callee().as<JSFunction>(), constructing); unsigned numFormals = script->functionNonDelazifying()->nargs(); if (numFormals > numActualArgs) { code = cx->runtime()->jitRuntime()->getArgumentsRectifier().value; } } else { numActualArgs = 0; constructing = false; if (script->isDirectEvalInFunction()) { if (state.asExecute()->newTarget().isNull()) { ScriptFrameIter iter(cx); state.asExecute()->setNewTarget(iter.newTarget()); } maxArgc = 1; maxArgv = state.asExecute()->addressOfNewTarget(); } else { maxArgc = 0; maxArgv = nullptr; } envChain = state.asExecute()->environmentChain(); calleeToken = CalleeToToken(state.script()); } // Caller must construct |this| before invoking the function. MOZ_ASSERT_IF(constructing, maxArgv[0].isObject() || maxArgv[0].isMagic(JS_UNINITIALIZED_LEXICAL)); RootedValue result(cx, Int32Value(numActualArgs)); { AssertRealmUnchanged aru(cx); ActivationEntryMonitor entryMonitor(cx, calleeToken); JitActivation activation(cx); EnterJitCode enter = cx->runtime()->jitRuntime()->enterJit(); #ifdef DEBUG nogc.reset(); #endif CALL_GENERATED_CODE(enter, code, maxArgc, maxArgv, /* osrFrame = */ nullptr, calleeToken, envChain, /* osrNumStackValues = */ 0, result.address()); } MOZ_ASSERT(!cx->hasIonReturnOverride()); // Release temporary buffer used for OSR into Ion. cx->freeOsrTempData(); if (result.isMagic()) { MOZ_ASSERT(result.isMagic(JS_ION_ERROR)); return EnterJitStatus::Error; } // Jit callers wrap primitive constructor return, except for derived // class constructors, which are forced to do it themselves. if (constructing && result.isPrimitive()) { MOZ_ASSERT(maxArgv[0].isObject()); result = maxArgv[0]; } state.setReturnValue(result); return EnterJitStatus::Ok; }
bool Instance::callImport(JSContext* cx, uint32_t funcImportIndex, unsigned argc, const uint64_t* argv, MutableHandleValue rval) { const FuncImport& fi = metadata().funcImports[funcImportIndex]; InvokeArgs args(cx); if (!args.init(argc)) return false; bool hasI64Arg = false; MOZ_ASSERT(fi.sig().args().length() == argc); for (size_t i = 0; i < argc; i++) { switch (fi.sig().args()[i]) { case ValType::I32: args[i].set(Int32Value(*(int32_t*)&argv[i])); break; case ValType::F32: args[i].set(JS::CanonicalizedDoubleValue(*(float*)&argv[i])); break; case ValType::F64: args[i].set(JS::CanonicalizedDoubleValue(*(double*)&argv[i])); break; case ValType::I64: { if (!JitOptions.wasmTestMode) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_I64); return false; } RootedObject obj(cx, CreateI64Object(cx, *(int64_t*)&argv[i])); if (!obj) return false; args[i].set(ObjectValue(*obj)); hasI64Arg = true; break; } case ValType::I8x16: case ValType::I16x8: case ValType::I32x4: case ValType::F32x4: case ValType::B8x16: case ValType::B16x8: case ValType::B32x4: case ValType::Limit: MOZ_CRASH("unhandled type in callImport"); } } FuncImportTls& import = funcImportTls(fi); RootedFunction importFun(cx, &import.obj->as<JSFunction>()); RootedValue fval(cx, ObjectValue(*import.obj)); RootedValue thisv(cx, UndefinedValue()); if (!Call(cx, fval, thisv, args, rval)) return false; // Throw an error if returning i64 and not in test mode. if (!JitOptions.wasmTestMode && fi.sig().ret() == ExprType::I64) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_I64); return false; } // Don't try to optimize if the function has at least one i64 arg or if // it returns an int64. GenerateJitExit relies on this, as does the // type inference code below in this function. if (hasI64Arg || fi.sig().ret() == ExprType::I64) return true; // The import may already have become optimized. void* jitExitCode = codeBase() + fi.jitExitCodeOffset(); if (import.code == jitExitCode) return true; // Test if the function is JIT compiled. if (!importFun->hasScript()) return true; JSScript* script = importFun->nonLazyScript(); if (!script->hasBaselineScript()) { MOZ_ASSERT(!script->hasIonScript()); return true; } // Don't enable jit entry when we have a pending ion builder. // Take the interpreter path which will link it and enable // the fast path on the next call. if (script->baselineScript()->hasPendingIonBuilder()) return true; // Currently we can't rectify arguments. Therefore disable if argc is too low. if (importFun->nargs() > fi.sig().args().length()) return true; // Ensure the argument types are included in the argument TypeSets stored in // the TypeScript. This is necessary for Ion, because the import will use // the skip-arg-checks entry point. // // Note that the TypeScript is never discarded while the script has a // BaselineScript, so if those checks hold now they must hold at least until // the BaselineScript is discarded and when that happens the import is // patched back. if (!TypeScript::ThisTypes(script)->hasType(TypeSet::UndefinedType())) return true; for (uint32_t i = 0; i < importFun->nargs(); i++) { TypeSet::Type type = TypeSet::UnknownType(); switch (fi.sig().args()[i]) { case ValType::I32: type = TypeSet::Int32Type(); break; case ValType::I64: MOZ_CRASH("can't happen because of above guard"); case ValType::F32: type = TypeSet::DoubleType(); break; case ValType::F64: type = TypeSet::DoubleType(); break; 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::Limit: MOZ_CRASH("Limit"); } if (!TypeScript::ArgTypes(script, i)->hasType(type)) return true; } // Let's optimize it! if (!script->baselineScript()->addDependentWasmImport(cx, *this, funcImportIndex)) return false; import.code = jitExitCode; import.baselineScript = script->baselineScript(); return true; }