☠☠ backed out by 06ffaf98e1e9 ☠ ☠ | |
author | Steve Fink <sfink@mozilla.com> |
Tue, 24 Jul 2018 11:53:42 -0700 | |
changeset 441544 | c22b643cdf5168be413948a2437584426bfd8c06 |
parent 441543 | c7b32ffa822e353b5479b6224194e3cdfd135e65 |
child 441545 | 66d022f720b698c8fced2c8628866445ddea6262 |
push id | 34867 |
push user | shindli@mozilla.com |
push date | Wed, 17 Oct 2018 00:55:53 +0000 |
treeherder | mozilla-central@778427bb6353 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | jonco |
bugs | 1479961 |
milestone | 64.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/js/public/GCAnnotations.h +++ b/js/public/GCAnnotations.h @@ -50,23 +50,29 @@ // Mark an RAII class as suppressing GC within its scope. # define JS_HAZ_GC_SUPPRESSED __attribute__((annotate("Suppress GC"))) // Mark a function as one that can run script if called. This obviously // subsumes JS_HAZ_GC_CALL, since anything that can run script can GC.` # define JS_HAZ_CAN_RUN_SCRIPT __attribute__((annotate("Can run script"))) +// Mark a function as able to call JSNatives. Otherwise, JSNatives don't show +// up in the callgraph. This doesn't matter for the can-GC analysis, but it is +// very nice for other uses of the callgraph. +# define JS_HAZ_JSNATIVE_CALLER __attribute__((annotate("Calls JSNatives"))) + #else # define JS_HAZ_GC_THING # define JS_HAZ_GC_POINTER # define JS_HAZ_ROOTED # define JS_HAZ_GC_INVALIDATED # define JS_HAZ_ROOTED_BASE # define JS_HAZ_NON_GC_POINTER # define JS_HAZ_GC_CALL # define JS_HAZ_GC_SUPPRESSED # define JS_HAZ_CAN_RUN_SCRIPT +# define JS_HAZ_JSNATIVE_CALLER #endif #endif /* js_GCAnnotations_h */
--- a/js/src/devtools/rootAnalysis/annotations.js +++ b/js/src/devtools/rootAnalysis/annotations.js @@ -422,8 +422,20 @@ function isOverridableField(initialCSU, } function listNonGCPointers() { return [ // Safe only because jsids are currently only made from pinned strings. 'NPIdentifier', ]; } + +function isJSNative(mangled) +{ + // _Z...E = function + // 9JSContext = JSContext* + // j = uint32 + // PN2JS5Value = JS::Value* + // P = pointer + // N2JS = JS:: + // 5Value = Value + return mangled.endsWith("P9JSContextjPN2JS5ValueE") && mangled.startsWith("_Z"); +}
--- a/js/src/devtools/rootAnalysis/computeCallgraph.js +++ b/js/src/devtools/rootAnalysis/computeCallgraph.js @@ -13,16 +13,19 @@ if (scriptArgs[0] == '--function') { var typeInfo_filename = scriptArgs[0] || "typeInfo.txt"; var callgraphOut_filename = scriptArgs[1] || "callgraph.txt"; var origOut = os.file.redirect(callgraphOut_filename); var memoized = new Map(); var memoizedCount = 0; +var JSNativeCaller = Object.create(null); +var JSNatives = []; + var unmangled2id = new Set(); function getId(name) { let id = memoized.get(name); if (id !== undefined) return id; @@ -97,18 +100,21 @@ function getAnnotations(functionName, bo // Scan through a function body, pulling out all annotations and calls and // recording them in callgraph.txt. function processBody(functionName, body) { if (!('PEdge' in body)) return; - for (var tag of getAnnotations(functionName, body).values()) + for (var tag of getAnnotations(functionName, body).values()) { print("T " + functionId(functionName) + " " + tag); + if (tag == "Calls JSNatives") + JSNativeCaller[functionName] = true; + } // Set of all callees that have been output so far, in order to suppress // repeated callgraph edges from being recorded. This uses a Map from // callees to limit sets, because we don't want a limited edge to prevent // an unlimited edge from being recorded later. (So an edge will be skipped // if it exists and is at least as limited as the previously seen edge.) // // Limit sets are implemented as integers interpreted as bitfields. @@ -217,16 +223,18 @@ function process(functionName, functionB // This is slightly conservative in the case where they are *not* // identical, but that should be rare enough that we don't care. var markerPos = functionName.indexOf(internalMarker); if (markerPos > 0) { var inChargeXTor = functionName.replace(internalMarker, ""); printOnce("D " + functionId(inChargeXTor) + " " + functionId(functionName)); } + const [ mangled, unmangled ] = splitFunction(functionName); + // Further note: from https://itanium-cxx-abi.github.io/cxx-abi/abi.html the // different kinds of constructors/destructors are: // C1 # complete object constructor // C2 # base object constructor // C3 # complete object allocating constructor // D0 # deleting destructor // D1 # complete object destructor // D2 # base object destructor @@ -251,17 +259,16 @@ function process(functionName, functionB // ::= D2 # base object (not-in-charge) destructor // <special-name> ::= C1 # complete object constructor // ::= C2 # base object constructor // ::= C3 # complete object allocating constructor // // Currently, allocating constructors are never used. // if (functionName.indexOf("C4") != -1) { - var [ mangled, unmangled ] = splitFunction(functionName); // E terminates the method name (and precedes the method parameters). // If eg "C4E" shows up in the mangled name for another reason, this // will create bogus edges in the callgraph. But it will affect little // and is somewhat difficult to avoid, so we will live with it. // // Another possibility! A templatized constructor will contain C4I...E // for template arguments. // @@ -300,19 +307,32 @@ function process(functionName, functionB const not_in_charge_dtor = functionName.replace("(int32)", "()"); const D0 = not_in_charge_dtor.replace("D4Ev", "D0Ev") + " [[deleting_dtor]]"; const D1 = not_in_charge_dtor.replace("D4Ev", "D1Ev") + " [[complete_dtor]]"; const D2 = not_in_charge_dtor.replace("D4Ev", "D2Ev") + " [[base_dtor]]"; printOnce("D " + functionId(D0) + " " + functionId(D1)); printOnce("D " + functionId(D1) + " " + functionId(D2)); printOnce("D " + functionId(D2) + " " + functionId(functionName)); } + + if (isJSNative(mangled)) + JSNatives.push(functionName); +} + +function postprocess_callgraph() { + for (const caller of Object.keys(JSNativeCaller)) { + const caller_id = functionId(caller); + for (const callee of JSNatives) + printOnce(`D ${caller_id} ${functionId(callee)}`); + } } for (var nameIndex = minStream; nameIndex <= maxStream; nameIndex++) { var name = xdb.read_key(nameIndex); var data = xdb.read_entry(name); process(name.readString(), JSON.parse(data.readString())); xdb.free_string(name); xdb.free_string(data); } +postprocess_callgraph(); + os.file.close(os.file.redirect(origOut));
--- a/js/src/jit/Jit.cpp +++ b/js/src/jit/Jit.cpp @@ -12,17 +12,17 @@ #include "jit/JitRealm.h" #include "vm/Interpreter.h" #include "vm/Stack-inl.h" using namespace js; using namespace js::jit; -static EnterJitStatus +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;
--- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -363,17 +363,17 @@ MaybeCreateThisForConstructor(JSContext* RootedObject callee(cx, &args.callee()); RootedObject newTarget(cx, &args.newTarget().toObject()); NewObjectKind newKind = createSingleton ? SingletonObject : GenericObject; return CreateThis(cx, callee, calleeScript, newTarget, newKind, args.mutableThisv()); } -static MOZ_NEVER_INLINE bool +static MOZ_NEVER_INLINE JS_HAZ_JSNATIVE_CALLER bool Interpret(JSContext* cx, RunState& state); InterpreterFrame* InvokeState::pushInterpreterFrame(JSContext* cx) { return cx->interpreterStack().pushInvokeFrame(cx, args_, construct_); } @@ -1987,17 +1987,17 @@ js::ReportInNotObjectError(JSContext* cx lbytes.get(), rbytes.get()); return; } JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_IN_NOT_OBJECT, InformalValueTypeName(rref)); } -static MOZ_NEVER_INLINE bool +static MOZ_NEVER_INLINE JS_HAZ_JSNATIVE_CALLER bool Interpret(JSContext* cx, RunState& state) { /* * Define macros for an interpreter loop. Opcode dispatch may be either by a * switch statement or by indirect goto (aka a threaded interpreter), depending * on compiler support. * * Threaded interpretation appears to be well-supported by GCC 3 and higher.