AsmJSModule::~AsmJSModule() { scriptSource_->decref(); if (code_) { for (unsigned i = 0; i < numExits(); i++) { AsmJSModule::ExitDatum &exitDatum = exitIndexToGlobalDatum(i); if (!exitDatum.fun) continue; if (!exitDatum.fun->hasScript()) continue; JSScript *script = exitDatum.fun->nonLazyScript(); if (!script->hasIonScript()) continue; jit::DependentAsmJSModuleExit exit(this, i); script->ionScript()->removeDependentAsmJSModule(exit); } DeallocateExecutableMemory(code_, pod.totalBytes_); } for (size_t i = 0; i < numFunctionCounts(); i++) js_delete(functionCounts(i)); }
uint32 ion::ForceInvalidation() { JSContext *cx = GetIonContext()->cx; JSScript *script = GetBailedJSScript(cx); JS_ASSERT(script->hasIonScript()); JS_ASSERT(!script->ion->invalidated()); IonSpew(IonSpew_Invalidate, "Forced invalidation bailout"); return Invalidate(cx, script); }
static void MaybeInvalidateScriptUsedWithNew(JSRuntime *rt, types::TypeObject *type) { types::TypeNewScript *newScript = type->newScript(); if (!newScript) return; JSScript *script = newScript->fun->nonLazyScript(); if (script && script->hasIonScript()) { for (ContextIter cx(rt); !cx.done(); cx.next()) jit::Invalidate(cx, script); } }
uint32 ion::CachedShapeGuardFailure() { JSContext *cx = GetIonContext()->cx; JSScript *script = GetBailedJSScript(cx); JS_ASSERT(script->hasIonScript()); JS_ASSERT(!script->ion->invalidated()); // Purge JM caches in the script and all inlined script, to avoid baking in // the same shape guard next time. for (size_t i = 0; i < script->ion->scriptEntries(); i++) mjit::PurgeCaches(script->ion->getScript(i)); IonSpew(IonSpew_Invalidate, "Invalidating due to shape guard failure"); return Invalidate(cx, script); }
bool IonFrameIterator::checkInvalidation(IonScript **ionScriptOut) const { uint8 *returnAddr = returnAddressToFp(); JSScript *script = this->script(); // N.B. the current IonScript is not the same as the frame's // IonScript if the frame has since been invalidated. IonScript *currentIonScript = script->ion; bool invalidated = !script->hasIonScript() || !currentIonScript->containsReturnAddress(returnAddr); if (!invalidated) return false; int32 invalidationDataOffset = ((int32 *) returnAddr)[-1]; uint8 *ionScriptDataOffset = returnAddr + invalidationDataOffset; IonScript *ionScript = (IonScript *) Assembler::getPointer(ionScriptDataOffset); JS_ASSERT(ionScript->containsReturnAddress(returnAddr)); *ionScriptOut = ionScript; return true; }
JS_SetTopFrameAnnotation(JSContext *cx, void *annotation) { StackFrame *fp = cx->fp(); JS_ASSERT_IF(fp->beginsIonActivation(), !fp->annotation()); // Note that if this frame is running in Ion, the actual calling frame // could be inlined or a callee and thus we won't have a correct |fp|. // To account for this, ion::InvalidationBailout will transfer an // annotation from the old cx->fp() to the new top frame. This works // because we will never EnterIon on a frame with an annotation. fp->setAnnotation(annotation); JSScript *script = fp->script(); ReleaseAllJITCode(cx->runtime->defaultFreeOp()); // Ensure that we'll never try to compile this again. JS_ASSERT(!script->hasIonScript()); script->ion = ION_DISABLED_SCRIPT; }
AsmJSModule::~AsmJSModule() { if (code_) { for (unsigned i = 0; i < numExits(); i++) { AsmJSModule::ExitDatum &exitDatum = exitIndexToGlobalDatum(i); if (!exitDatum.fun) continue; if (!exitDatum.fun->hasScript()) continue; JSScript *script = exitDatum.fun->nonLazyScript(); if (!script->hasIonScript()) continue; ion::DependentAsmJSModuleExit exit(this, i); script->ionScript()->removeDependentAsmJSModule(exit); } } for (size_t i = 0; i < numFunctionCounts(); i++) js_delete(functionCounts(i)); }
static void StatsCellCallback(JSRuntime *rt, void *data, void *thing, JSGCTraceKind traceKind, size_t thingSize) { IteratorClosure *closure = static_cast<IteratorClosure *>(data); RuntimeStats *rtStats = closure->rtStats; CompartmentStats *cStats = rtStats->currCompartmentStats; switch (traceKind) { case JSTRACE_OBJECT: { JSObject *obj = static_cast<JSObject *>(thing); if (obj->isFunction()) { cStats->gcHeapObjectsFunction += thingSize; } else { cStats->gcHeapObjectsNonFunction += thingSize; } size_t slotsSize, elementsSize, miscSize; obj->sizeOfExcludingThis(rtStats->mallocSizeOf, &slotsSize, &elementsSize, &miscSize); cStats->objectSlots += slotsSize; cStats->objectElements += elementsSize; cStats->objectMisc += miscSize; if (ObjectPrivateVisitor *opv = closure->opv) { js::Class *clazz = js::GetObjectClass(obj); if (clazz->flags & JSCLASS_HAS_PRIVATE && clazz->flags & JSCLASS_PRIVATE_IS_NSISUPPORTS) { cStats->objectPrivate += opv->sizeOfIncludingThis(GetObjectPrivate(obj)); } } break; } case JSTRACE_STRING: { JSString *str = static_cast<JSString *>(thing); cStats->gcHeapStrings += thingSize; cStats->stringChars += str->sizeOfExcludingThis(rtStats->mallocSizeOf); break; } case JSTRACE_SHAPE: { Shape *shape = static_cast<Shape*>(thing); size_t propTableSize, kidsSize; shape->sizeOfExcludingThis(rtStats->mallocSizeOf, &propTableSize, &kidsSize); if (shape->inDictionary()) { cStats->gcHeapShapesDict += thingSize; cStats->shapesExtraDictTables += propTableSize; JS_ASSERT(kidsSize == 0); } else { cStats->gcHeapShapesTree += thingSize; cStats->shapesExtraTreeTables += propTableSize; cStats->shapesExtraTreeShapeKids += kidsSize; } break; } case JSTRACE_BASE_SHAPE: { cStats->gcHeapShapesBase += thingSize; break; } case JSTRACE_SCRIPT: { JSScript *script = static_cast<JSScript *>(thing); cStats->gcHeapScripts += thingSize; cStats->scriptData += script->sizeOfData(rtStats->mallocSizeOf); #ifdef JS_METHODJIT cStats->mjitData += script->sizeOfJitScripts(rtStats->mallocSizeOf); # ifdef JS_ION if (script->hasIonScript()) cStats->mjitData += script->ion->size(); # endif #endif ScriptSource *ss = script->scriptSource(); SourceSet::AddPtr entry = closure->seenSources.lookupForAdd(ss); if (!entry) { closure->seenSources.add(entry, ss); // Not much to be done on failure. rtStats->runtime.scriptSources += ss->sizeOfIncludingThis(rtStats->mallocSizeOf); } break; } case JSTRACE_IONCODE: { #ifdef JS_METHODJIT # ifdef JS_ION ion::IonCode *code = static_cast<ion::IonCode *>(thing); cStats->gcHeapScripts += thingSize; cStats->mjitData += code->bufferSize(); # endif #endif break; } case JSTRACE_TYPE_OBJECT: { types::TypeObject *obj = static_cast<types::TypeObject *>(thing); cStats->gcHeapTypeObjects += thingSize; obj->sizeOfExcludingThis(&cStats->typeInferenceSizes, rtStats->mallocSizeOf); break; } #if JS_HAS_XML_SUPPORT case JSTRACE_XML: { cStats->gcHeapXML += thingSize; break; } #endif } // Yes, this is a subtraction: see StatsArenaCallback() for details. cStats->gcHeapUnusedGcThings -= thingSize; }
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; }
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; }