Example #1
0
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;
}
Example #2
0
File: Jit.cpp Project: servo/mozjs
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;
}
Example #3
0
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;
}