diff --git a/js/src/devtools/rootAnalysis/analyzeRoots.js b/js/src/devtools/rootAnalysis/analyzeRoots.js index d46ba83e7cec..979c947435dd 100644 --- a/js/src/devtools/rootAnalysis/analyzeRoots.js +++ b/js/src/devtools/rootAnalysis/analyzeRoots.js @@ -21,14 +21,14 @@ var numBatches = (scriptArgs[4]|0) || 1; var tmpfile = scriptArgs[5] || "tmp.txt"; var gcFunctions = {}; -var text = snarf("gcFunctions.lst").split('\n'); +var text = snarf("gcFunctions.lst").split("\n"); assert(text.pop().length == 0); for (var line of text) { gcFunctions[line] = true; } var suppressedFunctions = {}; -var text = snarf("suppressedFunctions.lst").split('\n'); +var text = snarf(suppressedFunctionsFile).split("\n"); assert(text.pop().length == 0); for (var line of text) { suppressedFunctions[line] = true; @@ -39,14 +39,14 @@ var match; var gcThings = {}; var gcPointers = {}; -var gcTypesText = snarf(gcTypesFile).split('\n'); -for (var line of gcTypesText) { +text = snarf(gcTypesFile).split("\n"); +for (var line of text) { if (match = /GCThing: (.*)/.exec(line)) gcThings[match[1]] = true; if (match = /GCPointer: (.*)/.exec(line)) gcPointers[match[1]] = true; } -gcTypesText = null; +text = null; function isUnrootedType(type) { @@ -246,6 +246,10 @@ function computePredecessors(body) function variableUseFollowsGC(suppressed, variable, worklist) { + // Scan through all edges following an unrooted variable use, using an + // explicit worklist. A worklist contains a following edge together with a + // description of where one of its predecessors GC'd (if any). + while (worklist.length) { var entry = worklist.pop(); var body = entry.body, ppoint = entry.ppoint; @@ -263,7 +267,7 @@ function variableUseFollowsGC(suppressed, variable, worklist) if (ppoint == body.Index[0]) { if (body.BlockId.Kind == "Loop") { - // propagate to parents which enter the loop body. + // propagate to parents that enter the loop body. if ("BlockPPoint" in body) { for (var parent of body.BlockPPoint) { var found = false; @@ -301,8 +305,8 @@ function variableUseFollowsGC(suppressed, variable, worklist) } var gcInfo = entry.gcInfo; - if (!gcInfo && !(edge.Index[0] in body.suppressed) && !suppressed) { - var gcName = edgeCanGC(edge); + if (!gcInfo && !(source in body.suppressed) && !suppressed) { + var gcName = edgeCanGC(edge, body); if (gcName) gcInfo = {name:gcName, body:body, ppoint:source}; } @@ -338,10 +342,14 @@ function variableUseFollowsGC(suppressed, variable, worklist) function variableLiveAcrossGC(suppressed, variable) { + // A variable is live across a GC if (1) it is used by an edge, and (2) it + // is used after a GC in a successor edge. + for (var body of functionBodies) { body.seen = null; body.minimumUse = 0; } + for (var body of functionBodies) { if (!("PEdge" in body)) continue; diff --git a/js/src/devtools/rootAnalysis/loadCallgraph.js b/js/src/devtools/rootAnalysis/loadCallgraph.js index b75bdf92d9ee..847cb57e627a 100644 --- a/js/src/devtools/rootAnalysis/loadCallgraph.js +++ b/js/src/devtools/rootAnalysis/loadCallgraph.js @@ -40,62 +40,62 @@ function loadCallgraph(file) { var textLines = snarf(file).split('\n'); for (var line of textLines) { - var match; + var match; if (match = /^\#(\d+) (.*)/.exec(line)) { assert(functionNames.length == match[1]); functionNames.push(match[2]); continue; } - var suppressed = false; - if (/SUPPRESS_GC/.test(line)) { + var suppressed = false; + if (/SUPPRESS_GC/.test(line)) { match = /^(..)SUPPRESS_GC (.*)/.exec(line); line = match[1] + match[2]; suppressed = true; - } - if (match = /^I (\d+) VARIABLE ([^\,]*)/.exec(line)) { + } + if (match = /^I (\d+) VARIABLE ([^\,]*)/.exec(line)) { var caller = functionNames[match[1]]; var name = match[2]; if (!indirectCallCannotGC(caller, name) && !suppressed) - addGCFunction(caller, "IndirectCall: " + name); - } else if (match = /^F (\d+) CLASS (.*?) FIELD (.*)/.exec(line)) { + addGCFunction(caller, "IndirectCall: " + name); + } else if (match = /^F (\d+) CLASS (.*?) FIELD (.*)/.exec(line)) { var caller = functionNames[match[1]]; var csu = match[2]; var fullfield = csu + "." + match[3]; if (!fieldCallCannotGC(csu, fullfield) && !suppressed) - addGCFunction(caller, "FieldCall: " + fullfield); - } else if (match = /^D (\d+) (\d+)/.exec(line)) { + addGCFunction(caller, "FieldCall: " + fullfield); + } else if (match = /^D (\d+) (\d+)/.exec(line)) { var caller = functionNames[match[1]]; var callee = functionNames[match[2]]; addCallEdge(caller, callee, suppressed); - } + } } var worklist = []; for (var name in callerGraph) - suppressedFunctions[name] = true; + suppressedFunctions[name] = true; for (var name in calleeGraph) { - if (!(name in callerGraph)) { + if (!(name in callerGraph)) { suppressedFunctions[name] = true; worklist.push(name); - } + } } while (worklist.length) { - name = worklist.pop(); - if (shouldSuppressGC(name)) + name = worklist.pop(); + if (shouldSuppressGC(name)) continue; - if (!(name in suppressedFunctions)) + if (!(name in suppressedFunctions)) continue; - delete suppressedFunctions[name]; - if (!(name in calleeGraph)) + delete suppressedFunctions[name]; + if (!(name in calleeGraph)) continue; - for (var entry of calleeGraph[name]) { + for (var entry of calleeGraph[name]) { if (!entry.suppressed) - worklist.push(entry.callee); - } + worklist.push(entry.callee); + } } for (var name in gcFunctions) { - if (name in suppressedFunctions) + if (name in suppressedFunctions) delete gcFunctions[name]; } @@ -108,16 +108,16 @@ function loadCallgraph(file) var worklist = []; for (var name in gcFunctions) - worklist.push(name); + worklist.push(name); while (worklist.length) { - name = worklist.pop(); - assert(name in gcFunctions); - if (!(name in callerGraph)) + name = worklist.pop(); + assert(name in gcFunctions); + if (!(name in callerGraph)) continue; - for (var entry of callerGraph[name]) { + for (var entry of callerGraph[name]) { if (!entry.suppressed && addGCFunction(entry.caller, name)) - worklist.push(entry.caller); - } + worklist.push(entry.caller); + } } } diff --git a/js/src/devtools/rootAnalysis/suppressedPoints.js b/js/src/devtools/rootAnalysis/suppressedPoints.js index 34a13f308fe4..ddf749aeb1c2 100644 --- a/js/src/devtools/rootAnalysis/suppressedPoints.js +++ b/js/src/devtools/rootAnalysis/suppressedPoints.js @@ -53,6 +53,7 @@ function computeSuppressedPoints(body) if (!("PEdge" in body)) return; + for (var edge of body.PEdge) { var source = edge.Index[0]; if (!(source in successors)) diff --git a/js/src/devtools/rootAnalysis/utility.js b/js/src/devtools/rootAnalysis/utility.js index 23457e356e35..b74eb263d1e3 100644 --- a/js/src/devtools/rootAnalysis/utility.js +++ b/js/src/devtools/rootAnalysis/utility.js @@ -2,9 +2,14 @@ "use strict"; -function assert(x) +function assert(x, msg) { - if (!x) + if (x) + return; + debugger; + if (msg) + throw "assertion failed: " + msg + "\n" + (Error().stack); + else throw "assertion failed: " + (Error().stack); } @@ -45,7 +50,7 @@ function sameVariable(var0, var1) assert("Name" in var0 || var0.Kind == "This" || var0.Kind == "Return"); assert("Name" in var1 || var1.Kind == "This" || var1.Kind == "Return"); if ("Name" in var0) - return "Name" in var1 && var0.Name[0] == var1.Name[0]; + return "Name" in var1 && var0.Name[0] == var1.Name[0]; return var0.Kind == var1.Kind; }