зеркало из https://github.com/mozilla/gecko-dev.git
Backed out 8 changesets (bug 1147403) for debug asserts on a CLOSED TREE.
Backed out changeset a1018d31e591 (bug 1147403) Backed out changeset fdb1dcf35e04 (bug 1147403) Backed out changeset 66ab1f789052 (bug 1147403) Backed out changeset 8ee01e148887 (bug 1147403) Backed out changeset 615c601284e3 (bug 1147403) Backed out changeset d7a4b4c31c94 (bug 1147403) Backed out changeset b1abceaf0f6e (bug 1147403) Backed out changeset 443b1a2a084f (bug 1147403)
This commit is contained in:
Родитель
beb52571e8
Коммит
4decbb024c
|
@ -7692,8 +7692,6 @@ CheckFunction(ModuleCompiler& m, LifoAlloc& lifo, MIRGenerator** mir, ModuleComp
|
|||
|
||||
*mir = f.extractMIR();
|
||||
(*mir)->initMinAsmJSHeapLength(m.minHeapLength());
|
||||
jit::SpewBeginFunction(*mir, nullptr);
|
||||
|
||||
*funcOut = func;
|
||||
return true;
|
||||
}
|
||||
|
@ -7770,7 +7768,8 @@ CheckFunctionsSequential(ModuleCompiler& m)
|
|||
int64_t before = PRMJ_Now();
|
||||
|
||||
JitContext jcx(m.cx(), &mir->alloc());
|
||||
jit::AutoSpewEndFunction spewEndFunction(mir);
|
||||
|
||||
IonSpewNewFunction(&mir->graph(), NullPtr());
|
||||
|
||||
if (!OptimizeMIR(mir))
|
||||
return m.failOffset(func->srcBegin(), "internal compiler failure (probably out of memory)");
|
||||
|
@ -7783,6 +7782,8 @@ CheckFunctionsSequential(ModuleCompiler& m)
|
|||
|
||||
if (!GenerateCode(m, *func, *mir, *lir))
|
||||
return false;
|
||||
|
||||
IonSpewEndFunction();
|
||||
}
|
||||
|
||||
if (!CheckAllFunctionsDefined(m))
|
||||
|
@ -8021,7 +8022,7 @@ CheckFunctions(ModuleCompiler& m)
|
|||
if (!ParallelCompilationEnabled(m.cx()) || !g.claim())
|
||||
return CheckFunctionsSequential(m);
|
||||
|
||||
JitSpew(JitSpew_IonSyncLogs, "Can't log asm.js script. (Compiled on background thread.)");
|
||||
JitSpew(JitSpew_IonLogs, "Can't log asm.js script. (Compiled on background thread.)");
|
||||
|
||||
// Saturate all helper threads.
|
||||
size_t numParallelJobs = HelperThreadState().maxAsmJSCompilationThreads();
|
||||
|
|
|
@ -30,7 +30,6 @@
|
|||
#include "vm/Interpreter.h"
|
||||
#include "vm/ProxyObject.h"
|
||||
#include "vm/SavedStacks.h"
|
||||
#include "vm/Stack.h"
|
||||
#include "vm/TraceLogging.h"
|
||||
|
||||
#include "jscntxtinlines.h"
|
||||
|
@ -1546,26 +1545,18 @@ js::testingFunc_inIon(JSContext* cx, unsigned argc, jsval* vp)
|
|||
return true;
|
||||
}
|
||||
|
||||
ScriptFrameIter iter(cx);
|
||||
if (iter.isIon()) {
|
||||
// Reset the counter of the IonScript's script.
|
||||
JitFrameIterator jitIter(cx);
|
||||
++jitIter;
|
||||
jitIter.script()->resetWarmUpResetCounter();
|
||||
} else {
|
||||
// Check if we missed multiple attempts at compiling the innermost script.
|
||||
JSScript* script = cx->currentScript();
|
||||
if (script && script->getWarmUpResetCount() >= 20) {
|
||||
JSString* error = JS_NewStringCopyZ(cx, "Compilation is being repeatedly prevented. Giving up.");
|
||||
if (!error)
|
||||
return false;
|
||||
JSScript* script = cx->currentScript();
|
||||
if (script && script->getWarmUpResetCount() >= 20) {
|
||||
JSString* error = JS_NewStringCopyZ(cx, "Compilation is being repeatedly prevented. Giving up.");
|
||||
if (!error)
|
||||
return false;
|
||||
|
||||
args.rval().setString(error);
|
||||
return true;
|
||||
}
|
||||
args.rval().setString(error);
|
||||
return true;
|
||||
}
|
||||
|
||||
args.rval().setBoolean(iter.isIon());
|
||||
// false when not in ionMonkey
|
||||
args.rval().setBoolean(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -225,45 +225,6 @@ compartment.
|
|||
thereby escaping the capability-based limits. For this reason,
|
||||
`onNewGlobalObject` is only available to privileged code.
|
||||
|
||||
<code>onIonCompilation(<i>graph</i>)</code>
|
||||
: A new IonMonkey compilation result is attached to a script instance of
|
||||
the Debuggee, the <i>graph</i> contains the internal intermediate
|
||||
representations of the compiler.
|
||||
|
||||
The value <i>graph</i> is an object composed of the following properties:
|
||||
|
||||
`json`
|
||||
: String containing a JSON of the intermediate representation used by
|
||||
the compiler. This JSON string content is composed of 2 intermediate
|
||||
representation of the graph, a `mir` (Middle-level IR), and a
|
||||
`lir` (Low-level IR).
|
||||
|
||||
Both have a property `blocks`, which is an array of basic
|
||||
blocks in [SSA form][ssa-form] which are used to construct the
|
||||
control flow graph. All elements of these arrays are objects which
|
||||
have a `number`, and an `instructions` properties.
|
||||
|
||||
The MIR blocks have additional properties such as the
|
||||
`predecessors` and `successors` of each block, which can
|
||||
be used to reconstruct the control flow graph, with the
|
||||
`number` properties of the blocks.
|
||||
|
||||
The `instructions` properties are array of objects which have
|
||||
an `id` and an `opcode`. The `id` corresponds to the
|
||||
[SSA form][ssa-form] identifier (number) of each instruction, and the
|
||||
`opcode` is a string which represents the instruction.
|
||||
|
||||
This JSON string contains even more detailed internal information
|
||||
which remains undocummented, as it is potentially subject to
|
||||
frequent modifications.
|
||||
|
||||
`scripts`
|
||||
: Array of [`Debugger.Script`][script] instances. For a block at
|
||||
`mir.blocks[i]` or `lir.blocks[i]` in the JSON, `scripts[i]` is the
|
||||
[`Debugger.Script`][script] containing that block's code.
|
||||
|
||||
This method's return value is ignored.
|
||||
|
||||
|
||||
|
||||
## Function Properties of the Debugger Prototype Object
|
||||
|
|
|
@ -64,4 +64,3 @@ resource 'img-alloc-plot' alloc-plot-console.png $RBASE/8461
|
|||
absolute-label 'protocol' https://wiki.mozilla.org/Remote_Debugging_Protocol "Remote Debugging Protocol"
|
||||
absolute-label 'saved-frame' https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/SavedFrame "SavedFrame"
|
||||
absolute-label 'bernoulli-trial' https://en.wikipedia.org/wiki/Bernoulli_trial "Bernoulli Trial"
|
||||
absolute-label 'ssa-form' https://en.wikipedia.org/wiki/Static_single_assignment_form "SSA form"
|
||||
|
|
|
@ -1,121 +0,0 @@
|
|||
|
||||
function test() {
|
||||
// Force Ion compilation with OSR.
|
||||
for (var res = false; !res; res = inIon()) {};
|
||||
if (typeof res == "string")
|
||||
throw "Skipping test: Ion compilation is disabled/prevented.";
|
||||
};
|
||||
|
||||
|
||||
// Functions used to assert the representation of the graph which is exported in
|
||||
// the JSON string argument.
|
||||
function assertInstruction(ins) {
|
||||
assertEq(typeof ins.id, "number");
|
||||
assertEq(ins.id | 0, ins.id);
|
||||
assertEq(typeof ins.opcode, "string");
|
||||
}
|
||||
|
||||
function assertBlock(block) {
|
||||
assertEq(typeof block, "object");
|
||||
assertEq(typeof block.number, "number");
|
||||
assertEq(block.number | 0, block.number);
|
||||
assertEq(typeof block.instructions, "object");
|
||||
for (var ins of block.instructions)
|
||||
assertInstruction(ins);
|
||||
}
|
||||
|
||||
function assertGraph(graph, scripts) {
|
||||
assertEq(typeof graph, "object");
|
||||
assertEq(typeof graph.blocks, "object");
|
||||
assertEq(graph.blocks.length, scripts.length);
|
||||
for (var block of graph.blocks)
|
||||
assertBlock(block);
|
||||
}
|
||||
|
||||
function assertJSON(str, scripts) {
|
||||
assertEq(typeof str, "string");
|
||||
|
||||
var json = JSON.parse(str);
|
||||
assertGraph(json.mir, scripts);
|
||||
assertGraph(json.lir, scripts);
|
||||
}
|
||||
|
||||
function assertOnIonCompilationArgument(obj) {
|
||||
assertEq(typeof obj, "object");
|
||||
assertEq(typeof obj.scripts, "object");
|
||||
assertJSON(obj.json, obj.scripts);
|
||||
}
|
||||
|
||||
// Attach the current global to a debugger.
|
||||
var hits = 0;
|
||||
var g = newGlobal();
|
||||
g.parent = this;
|
||||
g.eval(`
|
||||
var dbg = new Debugger();
|
||||
var parentw = dbg.addDebuggee(parent);
|
||||
var testw = parentw.makeDebuggeeValue(parent.test);
|
||||
var scriptw = testw.script;
|
||||
`);
|
||||
|
||||
// Wrap the testing function.
|
||||
function check(assert) {
|
||||
// print('reset compilation counter.');
|
||||
with ({}) { // Prevent Ion compilation.
|
||||
gc(); // Flush previous compilation.
|
||||
hits = 0; // Synchronized hit counts.
|
||||
|
||||
try { // Skip this test if we cannot reliably compile with Ion.
|
||||
test(); // Wait until the next Ion compilation.
|
||||
} catch (msg) {
|
||||
if (typeof msg == "string") {
|
||||
// print(msg);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
assert(); // Run the assertions given as arguments.
|
||||
}
|
||||
}
|
||||
|
||||
// With the compilation graph inhibited, we should have no output.
|
||||
g.eval(`
|
||||
dbg.onIonCompilation = function (graph) {
|
||||
// print('Compiled ' + graph.scripts[0].displayName + ':' + graph.scripts[0].startLine);
|
||||
if (graph.scripts[0] !== scriptw)
|
||||
return;
|
||||
parent.assertOnIonCompilationArgument(graph);
|
||||
parent.hits++;
|
||||
};
|
||||
`);
|
||||
|
||||
check(function () {
|
||||
// '>= 1' is needed because --ion-eager is too eager.
|
||||
assertEq(hits >= 1, true);
|
||||
});
|
||||
|
||||
|
||||
// Try re-entering the same compartment as the compiled script.
|
||||
g.dbg.onIonCompilation = function (graph) {
|
||||
// print('Compiled ' + graph.scripts[0].displayName + ':' + graph.scripts[0].startLine);
|
||||
if (graph.scripts[0] !== g.scriptw)
|
||||
return;
|
||||
assertOnIonCompilationArgument(graph);
|
||||
hits++;
|
||||
};
|
||||
check(function () { assertEq(hits >= 1, true); });
|
||||
|
||||
// Disable the debugger, and redo the last 2 tests.
|
||||
g.eval(`
|
||||
dbg.enabled = false;
|
||||
dbg.onIonCompilation = function (graph) {
|
||||
parent.hits++;
|
||||
};
|
||||
`);
|
||||
check(function () { assertEq(hits, 0); });
|
||||
|
||||
|
||||
g.dbg.enabled = false;
|
||||
g.dbg.onIonCompilation = function (graph) {
|
||||
hits++;
|
||||
};
|
||||
check(function () { assertEq(hits, 0); });
|
|
@ -14,8 +14,6 @@
|
|||
#include "jit/MIR.h"
|
||||
#include "jit/MIRGraph.h"
|
||||
|
||||
#include "vm/Printer.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace js::jit;
|
||||
|
||||
|
@ -130,12 +128,11 @@ IonSpewDependency(MInstruction* load, MInstruction* store, const char* verb, con
|
|||
if (!JitSpewEnabled(JitSpew_Alias))
|
||||
return;
|
||||
|
||||
Fprinter& out = JitSpewPrinter();
|
||||
out.printf("Load ");
|
||||
load->printName(out);
|
||||
out.printf(" %s on store ", verb);
|
||||
store->printName(out);
|
||||
out.printf(" (%s)\n", reason);
|
||||
fprintf(JitSpewFile, "Load ");
|
||||
load->printName(JitSpewFile);
|
||||
fprintf(JitSpewFile, " %s on store ", verb);
|
||||
store->printName(JitSpewFile);
|
||||
fprintf(JitSpewFile, " (%s)\n", reason);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -144,10 +141,9 @@ IonSpewAliasInfo(const char* pre, MInstruction* ins, const char* post)
|
|||
if (!JitSpewEnabled(JitSpew_Alias))
|
||||
return;
|
||||
|
||||
Fprinter& out = JitSpewPrinter();
|
||||
out.printf("%s ", pre);
|
||||
ins->printName(out);
|
||||
out.printf(" %s\n", post);
|
||||
fprintf(JitSpewFile, "%s ", pre);
|
||||
ins->printName(JitSpewFile);
|
||||
fprintf(JitSpewFile, " %s\n", post);
|
||||
}
|
||||
|
||||
// This pass annotates every load instruction with the last store instruction
|
||||
|
@ -212,10 +208,9 @@ AliasAnalysis::analyze()
|
|||
}
|
||||
|
||||
if (JitSpewEnabled(JitSpew_Alias)) {
|
||||
Fprinter& out = JitSpewPrinter();
|
||||
out.printf("Processing store ");
|
||||
def->printName(out);
|
||||
out.printf(" (flags %x)\n", set.flags());
|
||||
fprintf(JitSpewFile, "Processing store ");
|
||||
def->printName(JitSpewFile);
|
||||
fprintf(JitSpewFile, " (flags %x)\n", set.flags());
|
||||
}
|
||||
} else {
|
||||
// Find the most recent store on which this instruction depends.
|
||||
|
|
|
@ -16,51 +16,67 @@
|
|||
#include "jit/LIR.h"
|
||||
#include "jit/MIRGraph.h"
|
||||
|
||||
#include "vm/Printer.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace js::jit;
|
||||
|
||||
void
|
||||
C1Spewer::beginFunction(MIRGraph* graph, JSScript* script)
|
||||
bool
|
||||
C1Spewer::init(const char* path)
|
||||
{
|
||||
spewout_ = fopen(path, "w");
|
||||
return spewout_ != nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
C1Spewer::beginFunction(MIRGraph* graph, HandleScript script)
|
||||
{
|
||||
if (!spewout_)
|
||||
return;
|
||||
|
||||
this->graph = graph;
|
||||
|
||||
out_.printf("begin_compilation\n");
|
||||
fprintf(spewout_, "begin_compilation\n");
|
||||
if (script) {
|
||||
out_.printf(" name \"%s:%" PRIuSIZE "\"\n", script->filename(), script->lineno());
|
||||
out_.printf(" method \"%s:%" PRIuSIZE "\"\n", script->filename(), script->lineno());
|
||||
fprintf(spewout_, " name \"%s:%" PRIuSIZE "\"\n", script->filename(), script->lineno());
|
||||
fprintf(spewout_, " method \"%s:%" PRIuSIZE "\"\n", script->filename(), script->lineno());
|
||||
} else {
|
||||
out_.printf(" name \"asm.js compilation\"\n");
|
||||
out_.printf(" method \"asm.js compilation\"\n");
|
||||
fprintf(spewout_, " name \"asm.js compilation\"\n");
|
||||
fprintf(spewout_, " method \"asm.js compilation\"\n");
|
||||
}
|
||||
out_.printf(" date %d\n", (int)time(nullptr));
|
||||
out_.printf("end_compilation\n");
|
||||
fprintf(spewout_, " date %d\n", (int)time(nullptr));
|
||||
fprintf(spewout_, "end_compilation\n");
|
||||
}
|
||||
|
||||
void
|
||||
C1Spewer::spewPass(const char* pass)
|
||||
{
|
||||
out_.printf("begin_cfg\n");
|
||||
out_.printf(" name \"%s\"\n", pass);
|
||||
if (!spewout_)
|
||||
return;
|
||||
|
||||
fprintf(spewout_, "begin_cfg\n");
|
||||
fprintf(spewout_, " name \"%s\"\n", pass);
|
||||
|
||||
for (MBasicBlockIterator block(graph->begin()); block != graph->end(); block++)
|
||||
spewPass(out_, *block);
|
||||
spewPass(spewout_, *block);
|
||||
|
||||
out_.printf("end_cfg\n");
|
||||
fprintf(spewout_, "end_cfg\n");
|
||||
fflush(spewout_);
|
||||
}
|
||||
|
||||
void
|
||||
C1Spewer::spewIntervals(const char* pass, BacktrackingAllocator* regalloc)
|
||||
{
|
||||
out_.printf("begin_intervals\n");
|
||||
out_.printf(" name \"%s\"\n", pass);
|
||||
if (!spewout_)
|
||||
return;
|
||||
|
||||
fprintf(spewout_, "begin_intervals\n");
|
||||
fprintf(spewout_, " name \"%s\"\n", pass);
|
||||
|
||||
size_t nextId = 0x4000;
|
||||
for (MBasicBlockIterator block(graph->begin()); block != graph->end(); block++)
|
||||
spewIntervals(out_, *block, regalloc, nextId);
|
||||
spewIntervals(spewout_, *block, regalloc, nextId);
|
||||
|
||||
out_.printf("end_intervals\n");
|
||||
fprintf(spewout_, "end_intervals\n");
|
||||
fflush(spewout_);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -68,28 +84,35 @@ C1Spewer::endFunction()
|
|||
{
|
||||
}
|
||||
|
||||
static void
|
||||
DumpDefinition(GenericPrinter& out, MDefinition* def)
|
||||
void
|
||||
C1Spewer::finish()
|
||||
{
|
||||
out.printf(" ");
|
||||
out.printf("%u %u ", def->id(), unsigned(def->useCount()));
|
||||
def->printName(out);
|
||||
out.printf(" ");
|
||||
def->printOpcode(out);
|
||||
out.printf(" <|@\n");
|
||||
if (spewout_)
|
||||
fclose(spewout_);
|
||||
}
|
||||
|
||||
static void
|
||||
DumpLIR(GenericPrinter& out, LNode* ins)
|
||||
DumpDefinition(FILE* fp, MDefinition* def)
|
||||
{
|
||||
out.printf(" ");
|
||||
out.printf("%d ", ins->id());
|
||||
ins->dump(out);
|
||||
out.printf(" <|@\n");
|
||||
fprintf(fp, " ");
|
||||
fprintf(fp, "%u %u ", def->id(), unsigned(def->useCount()));
|
||||
def->printName(fp);
|
||||
fprintf(fp, " ");
|
||||
def->printOpcode(fp);
|
||||
fprintf(fp, " <|@\n");
|
||||
}
|
||||
|
||||
static void
|
||||
DumpLIR(FILE* fp, LNode* ins)
|
||||
{
|
||||
fprintf(fp, " ");
|
||||
fprintf(fp, "%d ", ins->id());
|
||||
ins->dump(fp);
|
||||
fprintf(fp, " <|@\n");
|
||||
}
|
||||
|
||||
void
|
||||
C1Spewer::spewIntervals(GenericPrinter& out, BacktrackingAllocator* regalloc, LNode* ins, size_t& nextId)
|
||||
C1Spewer::spewIntervals(FILE* fp, BacktrackingAllocator* regalloc, LNode* ins, size_t& nextId)
|
||||
{
|
||||
for (size_t k = 0; k < ins->numDefs(); k++) {
|
||||
uint32_t id = ins->getDef(k)->virtualRegister();
|
||||
|
@ -98,102 +121,102 @@ C1Spewer::spewIntervals(GenericPrinter& out, BacktrackingAllocator* regalloc, LN
|
|||
for (size_t i = 0; i < vreg->numIntervals(); i++) {
|
||||
LiveInterval* live = vreg->getInterval(i);
|
||||
if (live->numRanges()) {
|
||||
out.printf("%d object \"", (i == 0) ? id : int32_t(nextId++));
|
||||
out.printf("%s", live->getAllocation()->toString());
|
||||
out.printf("\" %d -1", id);
|
||||
fprintf(fp, "%d object \"", (i == 0) ? id : int32_t(nextId++));
|
||||
fprintf(fp, "%s", live->getAllocation()->toString());
|
||||
fprintf(fp, "\" %d -1", id);
|
||||
for (size_t j = 0; j < live->numRanges(); j++) {
|
||||
out.printf(" [%u, %u[", live->getRange(j)->from.bits(),
|
||||
live->getRange(j)->to.bits());
|
||||
fprintf(fp, " [%u, %u[", live->getRange(j)->from.bits(),
|
||||
live->getRange(j)->to.bits());
|
||||
}
|
||||
for (UsePositionIterator usePos(live->usesBegin()); usePos != live->usesEnd(); usePos++)
|
||||
out.printf(" %u M", usePos->pos.bits());
|
||||
out.printf(" \"\"\n");
|
||||
fprintf(fp, " %u M", usePos->pos.bits());
|
||||
fprintf(fp, " \"\"\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
C1Spewer::spewIntervals(GenericPrinter& out, MBasicBlock* block, BacktrackingAllocator* regalloc, size_t& nextId)
|
||||
C1Spewer::spewIntervals(FILE* fp, MBasicBlock* block, BacktrackingAllocator* regalloc, size_t& nextId)
|
||||
{
|
||||
LBlock* lir = block->lir();
|
||||
if (!lir)
|
||||
return;
|
||||
|
||||
for (size_t i = 0; i < lir->numPhis(); i++)
|
||||
spewIntervals(out, regalloc, lir->getPhi(i), nextId);
|
||||
spewIntervals(fp, regalloc, lir->getPhi(i), nextId);
|
||||
|
||||
for (LInstructionIterator ins = lir->begin(); ins != lir->end(); ins++)
|
||||
spewIntervals(out, regalloc, *ins, nextId);
|
||||
spewIntervals(fp, regalloc, *ins, nextId);
|
||||
}
|
||||
|
||||
void
|
||||
C1Spewer::spewPass(GenericPrinter& out, MBasicBlock* block)
|
||||
C1Spewer::spewPass(FILE* fp, MBasicBlock* block)
|
||||
{
|
||||
out.printf(" begin_block\n");
|
||||
out.printf(" name \"B%d\"\n", block->id());
|
||||
out.printf(" from_bci -1\n");
|
||||
out.printf(" to_bci -1\n");
|
||||
fprintf(fp, " begin_block\n");
|
||||
fprintf(fp, " name \"B%d\"\n", block->id());
|
||||
fprintf(fp, " from_bci -1\n");
|
||||
fprintf(fp, " to_bci -1\n");
|
||||
|
||||
out.printf(" predecessors");
|
||||
fprintf(fp, " predecessors");
|
||||
for (uint32_t i = 0; i < block->numPredecessors(); i++) {
|
||||
MBasicBlock* pred = block->getPredecessor(i);
|
||||
out.printf(" \"B%d\"", pred->id());
|
||||
fprintf(fp, " \"B%d\"", pred->id());
|
||||
}
|
||||
out.printf("\n");
|
||||
fprintf(fp, "\n");
|
||||
|
||||
out.printf(" successors");
|
||||
fprintf(fp, " successors");
|
||||
for (uint32_t i = 0; i < block->numSuccessors(); i++) {
|
||||
MBasicBlock* successor = block->getSuccessor(i);
|
||||
out.printf(" \"B%d\"", successor->id());
|
||||
fprintf(fp, " \"B%d\"", successor->id());
|
||||
}
|
||||
out.printf("\n");
|
||||
fprintf(fp, "\n");
|
||||
|
||||
out.printf(" xhandlers\n");
|
||||
out.printf(" flags\n");
|
||||
fprintf(fp, " xhandlers\n");
|
||||
fprintf(fp, " flags\n");
|
||||
|
||||
if (block->lir() && block->lir()->begin() != block->lir()->end()) {
|
||||
out.printf(" first_lir_id %d\n", block->lir()->firstId());
|
||||
out.printf(" last_lir_id %d\n", block->lir()->lastId());
|
||||
fprintf(fp, " first_lir_id %d\n", block->lir()->firstId());
|
||||
fprintf(fp, " last_lir_id %d\n", block->lir()->lastId());
|
||||
}
|
||||
|
||||
out.printf(" begin_states\n");
|
||||
fprintf(fp, " begin_states\n");
|
||||
|
||||
if (block->entryResumePoint()) {
|
||||
out.printf(" begin_locals\n");
|
||||
out.printf(" size %d\n", (int)block->numEntrySlots());
|
||||
out.printf(" method \"None\"\n");
|
||||
fprintf(fp, " begin_locals\n");
|
||||
fprintf(fp, " size %d\n", (int)block->numEntrySlots());
|
||||
fprintf(fp, " method \"None\"\n");
|
||||
for (uint32_t i = 0; i < block->numEntrySlots(); i++) {
|
||||
MDefinition* ins = block->getEntrySlot(i);
|
||||
out.printf(" ");
|
||||
out.printf("%d ", i);
|
||||
fprintf(fp, " ");
|
||||
fprintf(fp, "%d ", i);
|
||||
if (ins->isUnused())
|
||||
out.printf("unused");
|
||||
fprintf(fp, "unused");
|
||||
else
|
||||
ins->printName(out);
|
||||
out.printf("\n");
|
||||
ins->printName(fp);
|
||||
fprintf(fp, "\n");
|
||||
}
|
||||
out.printf(" end_locals\n");
|
||||
fprintf(fp, " end_locals\n");
|
||||
}
|
||||
out.printf(" end_states\n");
|
||||
fprintf(fp, " end_states\n");
|
||||
|
||||
out.printf(" begin_HIR\n");
|
||||
fprintf(fp, " begin_HIR\n");
|
||||
for (MPhiIterator phi(block->phisBegin()); phi != block->phisEnd(); phi++)
|
||||
DumpDefinition(out, *phi);
|
||||
DumpDefinition(fp, *phi);
|
||||
for (MInstructionIterator i(block->begin()); i != block->end(); i++)
|
||||
DumpDefinition(out, *i);
|
||||
out.printf(" end_HIR\n");
|
||||
DumpDefinition(fp, *i);
|
||||
fprintf(fp, " end_HIR\n");
|
||||
|
||||
if (block->lir()) {
|
||||
out.printf(" begin_LIR\n");
|
||||
fprintf(fp, " begin_LIR\n");
|
||||
for (size_t i = 0; i < block->lir()->numPhis(); i++)
|
||||
DumpLIR(out, block->lir()->getPhi(i));
|
||||
DumpLIR(fp, block->lir()->getPhi(i));
|
||||
for (LInstructionIterator i(block->lir()->begin()); i != block->lir()->end(); i++)
|
||||
DumpLIR(out, *i);
|
||||
out.printf(" end_LIR\n");
|
||||
DumpLIR(fp, *i);
|
||||
fprintf(fp, " end_LIR\n");
|
||||
}
|
||||
|
||||
out.printf(" end_block\n");
|
||||
fprintf(fp, " end_block\n");
|
||||
}
|
||||
|
||||
#endif /* DEBUG */
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
#include "NamespaceImports.h"
|
||||
|
||||
#include "js/RootingAPI.h"
|
||||
#include "vm/Printer.h"
|
||||
|
||||
namespace js {
|
||||
namespace jit {
|
||||
|
@ -25,22 +24,24 @@ class LNode;
|
|||
class C1Spewer
|
||||
{
|
||||
MIRGraph* graph;
|
||||
GenericPrinter& out_;
|
||||
FILE* spewout_;
|
||||
|
||||
public:
|
||||
explicit C1Spewer(GenericPrinter& out)
|
||||
: graph(nullptr), out_(out)
|
||||
C1Spewer()
|
||||
: graph(nullptr), spewout_(nullptr)
|
||||
{ }
|
||||
|
||||
void beginFunction(MIRGraph* graph, JSScript* script);
|
||||
bool init(const char* path);
|
||||
void beginFunction(MIRGraph* graph, HandleScript script);
|
||||
void spewPass(const char* pass);
|
||||
void spewIntervals(const char* pass, BacktrackingAllocator* regalloc);
|
||||
void endFunction();
|
||||
void finish();
|
||||
|
||||
private:
|
||||
void spewPass(GenericPrinter& out, MBasicBlock* block);
|
||||
void spewIntervals(GenericPrinter& out, BacktrackingAllocator* regalloc, LNode* ins, size_t& nextId);
|
||||
void spewIntervals(GenericPrinter& out, MBasicBlock* block, BacktrackingAllocator* regalloc, size_t& nextId);
|
||||
void spewPass(FILE* fp, MBasicBlock* block);
|
||||
void spewIntervals(FILE* fp, BacktrackingAllocator* regalloc, LNode* ins, size_t& nextId);
|
||||
void spewIntervals(FILE* fp, MBasicBlock* block, BacktrackingAllocator* regalloc, size_t& nextId);
|
||||
};
|
||||
|
||||
} // namespace jit
|
||||
|
|
|
@ -41,13 +41,11 @@
|
|||
#include "jit/Sink.h"
|
||||
#include "jit/StupidAllocator.h"
|
||||
#include "jit/ValueNumbering.h"
|
||||
#include "vm/Debugger.h"
|
||||
#include "vm/HelperThreads.h"
|
||||
#include "vm/TraceLogging.h"
|
||||
|
||||
#include "jscompartmentinlines.h"
|
||||
#include "jsobjinlines.h"
|
||||
#include "vm/Debugger-inl.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace js::jit;
|
||||
|
@ -394,49 +392,6 @@ JitCompartment::ensureIonStubsExist(JSContext* cx)
|
|||
return true;
|
||||
}
|
||||
|
||||
// This function initializes the values which are given to the Debugger
|
||||
// onIonCompilation hook, if the compilation was successful, and if Ion
|
||||
// compilations of this compartment are watched by any debugger.
|
||||
//
|
||||
// This function must be called in the same AutoEnterAnalysis section as the
|
||||
// CodeGenerator::link. Failing to do so might leave room to interleave other
|
||||
// allocations which can invalidate any JSObject / JSFunction referenced by the
|
||||
// MIRGraph.
|
||||
//
|
||||
// This function ignores any allocation failure and returns whether the
|
||||
// Debugger::onIonCompilation should be called.
|
||||
static inline bool
|
||||
PrepareForDebuggerOnIonCompilationHook(JSContext* cx, bool success, jit::MIRGraph& graph,
|
||||
AutoScriptVector* scripts, LSprinter* spew)
|
||||
{
|
||||
if (!success)
|
||||
return false;
|
||||
|
||||
if (!Debugger::observesIonCompilation(cx))
|
||||
return false;
|
||||
|
||||
// fireOnIonCompilation failures are ignored, do the same here.
|
||||
if (!scripts->reserve(graph.numBlocks())) {
|
||||
cx->clearPendingException();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Collect the list of scripts which are inlined in the MIRGraph.
|
||||
for (jit::MBasicBlockIterator block(graph.begin()); block != graph.end(); block++)
|
||||
scripts->infallibleAppend(block->info().script());
|
||||
|
||||
// Spew the JSON graph made for the Debugger at the end of the LifoAlloc
|
||||
// used by the compiler. This would not prevent unexpected GC from the
|
||||
// compartment of the Debuggee, but do them as part of the compartment of
|
||||
// the Debugger when the content is copied over to a JSString.
|
||||
jit::JSONSpewer spewer(*spew);
|
||||
spewer.spewDebuggerGraph(&graph);
|
||||
if (spew->hadOutOfMemory())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
jit::FinishOffThreadBuilder(JSContext* cx, IonBuilder* builder)
|
||||
{
|
||||
|
@ -517,47 +472,33 @@ jit::LazyLinkTopActivation(JSContext* cx)
|
|||
IonBuilder* builder = calleeScript->ionScript()->pendingBuilder();
|
||||
calleeScript->setPendingIonBuilder(cx, nullptr);
|
||||
|
||||
AutoEnterAnalysis enterTypes(cx);
|
||||
RootedScript script(cx, builder->script());
|
||||
|
||||
// See PrepareForDebuggerOnIonCompilationHook
|
||||
bool callOnIonCompilation = false;
|
||||
AutoScriptVector debugScripts(cx);
|
||||
LSprinter debugPrinter(builder->alloc().lifoAlloc());
|
||||
|
||||
// Remove from pending.
|
||||
builder->remove();
|
||||
|
||||
CodeGenerator* codegen = builder->backgroundCodegen();
|
||||
bool success = false;
|
||||
if (codegen) {
|
||||
JitContext jctx(cx, &builder->alloc());
|
||||
AutoEnterAnalysis enterTypes(cx);
|
||||
if (CodeGenerator* codegen = builder->backgroundCodegen()) {
|
||||
js::TraceLoggerThread* logger = TraceLoggerForMainThread(cx->runtime());
|
||||
TraceLoggerEvent event(logger, TraceLogger_AnnotateScripts, script);
|
||||
AutoTraceLog logScript(logger, event);
|
||||
AutoTraceLog logLink(logger, TraceLogger_IonLinking);
|
||||
|
||||
JitContext jctx(cx, &builder->alloc());
|
||||
|
||||
// Root the assembler until the builder is finished below. As it
|
||||
// was constructed off thread, the assembler has not been rooted
|
||||
// previously, though any GC activity would discard the builder.
|
||||
codegen->masm.constructRoot(cx);
|
||||
|
||||
success = codegen->link(cx, builder->constraints());
|
||||
if (!success) {
|
||||
if (!codegen->link(cx, builder->constraints())) {
|
||||
// Silently ignore OOM during code generation. The assembly code
|
||||
// doesn't has code to handle it after linking happened. So it's
|
||||
// not OK to throw a catchable exception from there.
|
||||
cx->clearPendingException();
|
||||
}
|
||||
|
||||
callOnIonCompilation = PrepareForDebuggerOnIonCompilationHook(
|
||||
cx, success, builder->graph(), &debugScripts, &debugPrinter);
|
||||
}
|
||||
|
||||
// Without AutoEnterAnalysis scope.
|
||||
if (callOnIonCompilation)
|
||||
Debugger::onIonCompilation(cx, debugScripts, debugPrinter);
|
||||
|
||||
FinishOffThreadBuilder(cx, builder);
|
||||
|
||||
MOZ_ASSERT(script->hasBaselineScript());
|
||||
|
@ -1232,7 +1173,6 @@ bool
|
|||
OptimizeMIR(MIRGenerator* mir)
|
||||
{
|
||||
MIRGraph& graph = mir->graph();
|
||||
GraphSpewer& gs = mir->graphSpewer();
|
||||
TraceLoggerThread* logger;
|
||||
if (GetJitContext()->runtime->onMainThread())
|
||||
logger = TraceLoggerForMainThread(GetJitContext()->runtime);
|
||||
|
@ -1244,7 +1184,7 @@ OptimizeMIR(MIRGenerator* mir)
|
|||
return false;
|
||||
}
|
||||
|
||||
gs.spewPass("BuildSSA");
|
||||
IonSpewPass("BuildSSA");
|
||||
AssertBasicGraphCoherency(graph);
|
||||
|
||||
if (mir->shouldCancel("Start"))
|
||||
|
@ -1253,7 +1193,7 @@ OptimizeMIR(MIRGenerator* mir)
|
|||
if (!mir->compilingAsmJS()) {
|
||||
AutoTraceLog log(logger, TraceLogger_FoldTests);
|
||||
FoldTests(graph);
|
||||
gs.spewPass("Fold Tests");
|
||||
IonSpewPass("Fold Tests");
|
||||
AssertBasicGraphCoherency(graph);
|
||||
|
||||
if (mir->shouldCancel("Fold Tests"))
|
||||
|
@ -1264,7 +1204,7 @@ OptimizeMIR(MIRGenerator* mir)
|
|||
AutoTraceLog log(logger, TraceLogger_SplitCriticalEdges);
|
||||
if (!SplitCriticalEdges(graph))
|
||||
return false;
|
||||
gs.spewPass("Split Critical Edges");
|
||||
IonSpewPass("Split Critical Edges");
|
||||
AssertGraphCoherency(graph);
|
||||
|
||||
if (mir->shouldCancel("Split Critical Edges"))
|
||||
|
@ -1275,7 +1215,7 @@ OptimizeMIR(MIRGenerator* mir)
|
|||
AutoTraceLog log(logger, TraceLogger_RenumberBlocks);
|
||||
if (!RenumberBlocks(graph))
|
||||
return false;
|
||||
gs.spewPass("Renumber Blocks");
|
||||
IonSpewPass("Renumber Blocks");
|
||||
AssertGraphCoherency(graph);
|
||||
|
||||
if (mir->shouldCancel("Renumber Blocks"))
|
||||
|
@ -1303,7 +1243,7 @@ OptimizeMIR(MIRGenerator* mir)
|
|||
: AggressiveObservability;
|
||||
if (!EliminatePhis(mir, graph, observability))
|
||||
return false;
|
||||
gs.spewPass("Eliminate phis");
|
||||
IonSpewPass("Eliminate phis");
|
||||
AssertGraphCoherency(graph);
|
||||
|
||||
if (mir->shouldCancel("Eliminate phis"))
|
||||
|
@ -1322,7 +1262,7 @@ OptimizeMIR(MIRGenerator* mir)
|
|||
AutoTraceLog log(logger, TraceLogger_ScalarReplacement);
|
||||
if (!ScalarReplacement(mir, graph))
|
||||
return false;
|
||||
gs.spewPass("Scalar Replacement");
|
||||
IonSpewPass("Scalar Replacement");
|
||||
AssertGraphCoherency(graph);
|
||||
|
||||
if (mir->shouldCancel("Scalar Replacement"))
|
||||
|
@ -1333,7 +1273,7 @@ OptimizeMIR(MIRGenerator* mir)
|
|||
AutoTraceLog log(logger, TraceLogger_ApplyTypes);
|
||||
if (!ApplyTypeInformation(mir, graph))
|
||||
return false;
|
||||
gs.spewPass("Apply types");
|
||||
IonSpewPass("Apply types");
|
||||
AssertExtendedGraphCoherency(graph);
|
||||
|
||||
if (mir->shouldCancel("Apply types"))
|
||||
|
@ -1344,7 +1284,7 @@ OptimizeMIR(MIRGenerator* mir)
|
|||
AutoTraceLog log(logger, TraceLogger_EagerSimdUnbox);
|
||||
if (!EagerSimdUnbox(mir, graph))
|
||||
return false;
|
||||
gs.spewPass("Eager Simd Unbox");
|
||||
IonSpewPass("Eager Simd Unbox");
|
||||
AssertGraphCoherency(graph);
|
||||
|
||||
if (mir->shouldCancel("Eager Simd Unbox"))
|
||||
|
@ -1356,7 +1296,7 @@ OptimizeMIR(MIRGenerator* mir)
|
|||
AlignmentMaskAnalysis ama(graph);
|
||||
if (!ama.analyze())
|
||||
return false;
|
||||
gs.spewPass("Alignment Mask Analysis");
|
||||
IonSpewPass("Alignment Mask Analysis");
|
||||
AssertExtendedGraphCoherency(graph);
|
||||
|
||||
if (mir->shouldCancel("Alignment Mask Analysis"))
|
||||
|
@ -1376,7 +1316,7 @@ OptimizeMIR(MIRGenerator* mir)
|
|||
AliasAnalysis analysis(mir, graph);
|
||||
if (!analysis.analyze())
|
||||
return false;
|
||||
gs.spewPass("Alias analysis");
|
||||
IonSpewPass("Alias analysis");
|
||||
AssertExtendedGraphCoherency(graph);
|
||||
|
||||
if (mir->shouldCancel("Alias analysis"))
|
||||
|
@ -1398,7 +1338,7 @@ OptimizeMIR(MIRGenerator* mir)
|
|||
AutoTraceLog log(logger, TraceLogger_GVN);
|
||||
if (!gvn.run(ValueNumberer::UpdateAliasAnalysis))
|
||||
return false;
|
||||
gs.spewPass("GVN");
|
||||
IonSpewPass("GVN");
|
||||
AssertExtendedGraphCoherency(graph);
|
||||
|
||||
if (mir->shouldCancel("GVN"))
|
||||
|
@ -1414,7 +1354,7 @@ OptimizeMIR(MIRGenerator* mir)
|
|||
if (!script || !script->hadFrequentBailouts()) {
|
||||
if (!LICM(mir, graph))
|
||||
return false;
|
||||
gs.spewPass("LICM");
|
||||
IonSpewPass("LICM");
|
||||
AssertExtendedGraphCoherency(graph);
|
||||
|
||||
if (mir->shouldCancel("LICM"))
|
||||
|
@ -1427,7 +1367,7 @@ OptimizeMIR(MIRGenerator* mir)
|
|||
RangeAnalysis r(mir, graph);
|
||||
if (!r.addBetaNodes())
|
||||
return false;
|
||||
gs.spewPass("Beta");
|
||||
IonSpewPass("Beta");
|
||||
AssertExtendedGraphCoherency(graph);
|
||||
|
||||
if (mir->shouldCancel("RA Beta"))
|
||||
|
@ -1435,7 +1375,7 @@ OptimizeMIR(MIRGenerator* mir)
|
|||
|
||||
if (!r.analyze() || !r.addRangeAssertions())
|
||||
return false;
|
||||
gs.spewPass("Range Analysis");
|
||||
IonSpewPass("Range Analysis");
|
||||
AssertExtendedGraphCoherency(graph);
|
||||
|
||||
if (mir->shouldCancel("Range Analysis"))
|
||||
|
@ -1443,7 +1383,7 @@ OptimizeMIR(MIRGenerator* mir)
|
|||
|
||||
if (!r.removeBetaNodes())
|
||||
return false;
|
||||
gs.spewPass("De-Beta");
|
||||
IonSpewPass("De-Beta");
|
||||
AssertExtendedGraphCoherency(graph);
|
||||
|
||||
if (mir->shouldCancel("RA De-Beta"))
|
||||
|
@ -1453,7 +1393,7 @@ OptimizeMIR(MIRGenerator* mir)
|
|||
bool shouldRunUCE = false;
|
||||
if (!r.prepareForUCE(&shouldRunUCE))
|
||||
return false;
|
||||
gs.spewPass("RA check UCE");
|
||||
IonSpewPass("RA check UCE");
|
||||
AssertExtendedGraphCoherency(graph);
|
||||
|
||||
if (mir->shouldCancel("RA check UCE"))
|
||||
|
@ -1462,7 +1402,7 @@ OptimizeMIR(MIRGenerator* mir)
|
|||
if (shouldRunUCE) {
|
||||
if (!gvn.run(ValueNumberer::DontUpdateAliasAnalysis))
|
||||
return false;
|
||||
gs.spewPass("UCE After RA");
|
||||
IonSpewPass("UCE After RA");
|
||||
AssertExtendedGraphCoherency(graph);
|
||||
|
||||
if (mir->shouldCancel("UCE After RA"))
|
||||
|
@ -1473,7 +1413,7 @@ OptimizeMIR(MIRGenerator* mir)
|
|||
if (mir->optimizationInfo().autoTruncateEnabled()) {
|
||||
if (!r.truncate())
|
||||
return false;
|
||||
gs.spewPass("Truncate Doubles");
|
||||
IonSpewPass("Truncate Doubles");
|
||||
AssertExtendedGraphCoherency(graph);
|
||||
|
||||
if (mir->shouldCancel("Truncate Doubles"))
|
||||
|
@ -1486,7 +1426,7 @@ OptimizeMIR(MIRGenerator* mir)
|
|||
if (!UnrollLoops(graph, r.loopIterationBounds))
|
||||
return false;
|
||||
|
||||
gs.spewPass("Unroll Loops");
|
||||
IonSpewPass("Unroll Loops");
|
||||
AssertExtendedGraphCoherency(graph);
|
||||
}
|
||||
}
|
||||
|
@ -1496,7 +1436,7 @@ OptimizeMIR(MIRGenerator* mir)
|
|||
EffectiveAddressAnalysis eaa(mir, graph);
|
||||
if (!eaa.analyze())
|
||||
return false;
|
||||
gs.spewPass("Effective Address Analysis");
|
||||
IonSpewPass("Effective Address Analysis");
|
||||
AssertExtendedGraphCoherency(graph);
|
||||
|
||||
if (mir->shouldCancel("Effective Address Analysis"))
|
||||
|
@ -1507,7 +1447,7 @@ OptimizeMIR(MIRGenerator* mir)
|
|||
AutoTraceLog log(logger, TraceLogger_EliminateDeadCode);
|
||||
if (!EliminateDeadCode(mir, graph))
|
||||
return false;
|
||||
gs.spewPass("DCE");
|
||||
IonSpewPass("DCE");
|
||||
AssertExtendedGraphCoherency(graph);
|
||||
|
||||
if (mir->shouldCancel("DCE"))
|
||||
|
@ -1518,7 +1458,7 @@ OptimizeMIR(MIRGenerator* mir)
|
|||
AutoTraceLog log(logger, TraceLogger_EliminateDeadCode);
|
||||
if (!Sink(mir, graph))
|
||||
return false;
|
||||
gs.spewPass("Sink");
|
||||
IonSpewPass("Sink");
|
||||
AssertExtendedGraphCoherency(graph);
|
||||
|
||||
if (mir->shouldCancel("Sink"))
|
||||
|
@ -1531,7 +1471,7 @@ OptimizeMIR(MIRGenerator* mir)
|
|||
AutoTraceLog log(logger, TraceLogger_MakeLoopsContiguous);
|
||||
if (!MakeLoopsContiguous(graph))
|
||||
return false;
|
||||
gs.spewPass("Make loops contiguous");
|
||||
IonSpewPass("Make loops contiguous");
|
||||
AssertExtendedGraphCoherency(graph);
|
||||
|
||||
if (mir->shouldCancel("Make loops contiguous"))
|
||||
|
@ -1546,7 +1486,7 @@ OptimizeMIR(MIRGenerator* mir)
|
|||
EdgeCaseAnalysis edgeCaseAnalysis(mir, graph);
|
||||
if (!edgeCaseAnalysis.analyzeLate())
|
||||
return false;
|
||||
gs.spewPass("Edge Case Analysis (Late)");
|
||||
IonSpewPass("Edge Case Analysis (Late)");
|
||||
AssertGraphCoherency(graph);
|
||||
|
||||
if (mir->shouldCancel("Edge Case Analysis (Late)"))
|
||||
|
@ -1561,7 +1501,7 @@ OptimizeMIR(MIRGenerator* mir)
|
|||
// before its bounds check.
|
||||
if (!EliminateRedundantChecks(graph))
|
||||
return false;
|
||||
gs.spewPass("Bounds Check Elimination");
|
||||
IonSpewPass("Bounds Check Elimination");
|
||||
AssertGraphCoherency(graph);
|
||||
}
|
||||
|
||||
|
@ -1572,7 +1512,6 @@ LIRGraph*
|
|||
GenerateLIR(MIRGenerator* mir)
|
||||
{
|
||||
MIRGraph& graph = mir->graph();
|
||||
GraphSpewer& gs = mir->graphSpewer();
|
||||
|
||||
TraceLoggerThread* logger;
|
||||
if (GetJitContext()->runtime->onMainThread())
|
||||
|
@ -1589,7 +1528,7 @@ GenerateLIR(MIRGenerator* mir)
|
|||
AutoTraceLog log(logger, TraceLogger_GenerateLIR);
|
||||
if (!lirgen.generate())
|
||||
return nullptr;
|
||||
gs.spewPass("Generate LIR");
|
||||
IonSpewPass("Generate LIR");
|
||||
|
||||
if (mir->shouldCancel("Generate LIR"))
|
||||
return nullptr;
|
||||
|
@ -1616,7 +1555,7 @@ GenerateLIR(MIRGenerator* mir)
|
|||
return nullptr;
|
||||
#endif
|
||||
|
||||
gs.spewPass("Allocate Registers [Backtracking]");
|
||||
IonSpewPass("Allocate Registers [Backtracking]");
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1631,7 +1570,7 @@ GenerateLIR(MIRGenerator* mir)
|
|||
return nullptr;
|
||||
if (!integrity.check(true))
|
||||
return nullptr;
|
||||
gs.spewPass("Allocate Registers [Stupid]");
|
||||
IonSpewPass("Allocate Registers [Stupid]");
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1673,7 +1612,6 @@ CompileBackEnd(MIRGenerator* mir)
|
|||
{
|
||||
// Everything in CompileBackEnd can potentially run on a helper thread.
|
||||
AutoEnterIonCompilation enter;
|
||||
AutoSpewEndFunction spewEndFunction(mir);
|
||||
|
||||
if (!OptimizeMIR(mir))
|
||||
return nullptr;
|
||||
|
@ -1692,6 +1630,7 @@ AttachFinishedCompilations(JSContext* cx)
|
|||
if (!ion)
|
||||
return;
|
||||
|
||||
AutoEnterAnalysis enterTypes(cx);
|
||||
AutoLockHelperThreadState lock;
|
||||
|
||||
GlobalHelperThreadState::IonBuilderVector& finished = HelperThreadState().ionFinishedList();
|
||||
|
@ -1743,17 +1682,9 @@ AttachFinishedCompilations(JSContext* cx)
|
|||
}
|
||||
}
|
||||
|
||||
// See PrepareForDebuggerOnIonCompilationHook
|
||||
bool callOnIonCompilation = false;
|
||||
AutoScriptVector debugScripts(cx);
|
||||
LSprinter debugPrinter(builder->alloc().lifoAlloc());
|
||||
|
||||
RootedScript script(cx, builder->script());
|
||||
CodeGenerator* codegen = builder->backgroundCodegen();
|
||||
bool success = false;
|
||||
if (codegen) {
|
||||
if (CodeGenerator* codegen = builder->backgroundCodegen()) {
|
||||
RootedScript script(cx, builder->script());
|
||||
JitContext jctx(cx, &builder->alloc());
|
||||
AutoEnterAnalysis enterTypes(cx);
|
||||
TraceLoggerEvent event(logger, TraceLogger_AnnotateScripts, script);
|
||||
AutoTraceLog logScript(logger, event);
|
||||
AutoTraceLog logLink(logger, TraceLogger_IonLinking);
|
||||
|
@ -1763,6 +1694,7 @@ AttachFinishedCompilations(JSContext* cx)
|
|||
// previously, though any GC activity would discard the builder.
|
||||
codegen->masm.constructRoot(cx);
|
||||
|
||||
bool success;
|
||||
{
|
||||
AutoUnlockHelperThreadState unlock;
|
||||
success = codegen->link(cx, builder->constraints());
|
||||
|
@ -1775,15 +1707,6 @@ AttachFinishedCompilations(JSContext* cx)
|
|||
// exception from there.
|
||||
cx->clearPendingException();
|
||||
}
|
||||
|
||||
callOnIonCompilation = PrepareForDebuggerOnIonCompilationHook(
|
||||
cx, success, builder->graph(), &debugScripts, &debugPrinter);
|
||||
}
|
||||
|
||||
if (callOnIonCompilation) {
|
||||
// Without AutoEnterAnalysis scope.
|
||||
AutoUnlockHelperThreadState unlock;
|
||||
Debugger::onIonCompilation(cx, debugScripts, debugPrinter);
|
||||
}
|
||||
|
||||
FinishOffThreadBuilder(cx, builder);
|
||||
|
@ -1936,6 +1859,8 @@ IonCompile(JSContext* cx, JSScript* script,
|
|||
|
||||
JitContext jctx(cx, temp);
|
||||
|
||||
AutoEnterAnalysis enter(cx);
|
||||
|
||||
if (!cx->compartment()->ensureJitCompartmentExists(cx))
|
||||
return AbortReason_Alloc;
|
||||
|
||||
|
@ -1990,18 +1915,15 @@ IonCompile(JSContext* cx, JSScript* script,
|
|||
if (recompile)
|
||||
builderScript->ionScript()->setRecompiling();
|
||||
|
||||
SpewBeginFunction(builder, builderScript);
|
||||
#ifdef DEBUG
|
||||
IonSpewFunction ionSpewFunction(graph, builderScript);
|
||||
#endif
|
||||
|
||||
bool succeeded;
|
||||
{
|
||||
AutoEnterAnalysis enter(cx);
|
||||
succeeded = builder->build();
|
||||
builder->clearForBackEnd();
|
||||
}
|
||||
bool succeeded = builder->build();
|
||||
builder->clearForBackEnd();
|
||||
|
||||
if (!succeeded) {
|
||||
AbortReason reason = builder->abortReason();
|
||||
builder->graphSpewer().endFunction();
|
||||
if (reason == AbortReason_PreliminaryObjects) {
|
||||
// Some group was accessed which has associated preliminary objects
|
||||
// to analyze. Do this now and we will try to build again shortly.
|
||||
|
@ -2035,8 +1957,7 @@ IonCompile(JSContext* cx, JSScript* script,
|
|||
if (!recompile)
|
||||
builderScript->setIonScript(cx, ION_COMPILING_SCRIPT);
|
||||
|
||||
JitSpew(JitSpew_IonSyncLogs, "Can't log script %s:%" PRIuSIZE
|
||||
". (Compiled on background thread.)",
|
||||
JitSpew(JitSpew_IonLogs, "Can't log script %s:%" PRIuSIZE ". (Compiled on background thread.)",
|
||||
builderScript->filename(), builderScript->lineno());
|
||||
|
||||
JSRuntime* rt = cx->runtime();
|
||||
|
@ -2050,7 +1971,6 @@ IonCompile(JSContext* cx, JSScript* script,
|
|||
|
||||
if (!StartOffThreadIonCompile(cx, builder)) {
|
||||
JitSpew(JitSpew_IonAbort, "Unable to start off-thread ion compilation.");
|
||||
builder->graphSpewer().endFunction();
|
||||
return AbortReason_Alloc;
|
||||
}
|
||||
|
||||
|
@ -2061,29 +1981,15 @@ IonCompile(JSContext* cx, JSScript* script,
|
|||
return AbortReason_NoAbort;
|
||||
}
|
||||
|
||||
// See PrepareForDebuggerOnIonCompilationHook
|
||||
bool callOnIonCompilation = false;
|
||||
AutoScriptVector debugScripts(cx);
|
||||
LSprinter debugPrinter(builder->alloc().lifoAlloc());
|
||||
|
||||
ScopedJSDeletePtr<CodeGenerator> codegen;
|
||||
{
|
||||
AutoEnterAnalysis enter(cx);
|
||||
codegen = CompileBackEnd(builder);
|
||||
if (!codegen) {
|
||||
JitSpew(JitSpew_IonAbort, "Failed during back-end compilation.");
|
||||
return AbortReason_Disable;
|
||||
}
|
||||
|
||||
succeeded = codegen->link(cx, builder->constraints());
|
||||
callOnIonCompilation = PrepareForDebuggerOnIonCompilationHook(
|
||||
cx, succeeded, builder->graph(), &debugScripts, &debugPrinter);
|
||||
ScopedJSDeletePtr<CodeGenerator> codegen(CompileBackEnd(builder));
|
||||
if (!codegen) {
|
||||
JitSpew(JitSpew_IonAbort, "Failed during back-end compilation.");
|
||||
return AbortReason_Disable;
|
||||
}
|
||||
|
||||
if (callOnIonCompilation)
|
||||
Debugger::onIonCompilation(cx, debugScripts, debugPrinter);
|
||||
bool success = codegen->link(cx, builder->constraints());
|
||||
|
||||
if (succeeded)
|
||||
if (success)
|
||||
return AbortReason_NoAbort;
|
||||
if (cx->isExceptionPending())
|
||||
return AbortReason_Error;
|
||||
|
|
|
@ -2827,7 +2827,7 @@ LinearSum::add(int32_t constant)
|
|||
}
|
||||
|
||||
void
|
||||
LinearSum::dump(GenericPrinter& out) const
|
||||
LinearSum::print(Sprinter& sp) const
|
||||
{
|
||||
for (size_t i = 0; i < terms_.length(); i++) {
|
||||
int32_t scale = terms_[i].scale;
|
||||
|
@ -2835,29 +2835,36 @@ LinearSum::dump(GenericPrinter& out) const
|
|||
MOZ_ASSERT(scale);
|
||||
if (scale > 0) {
|
||||
if (i)
|
||||
out.printf("+");
|
||||
sp.printf("+");
|
||||
if (scale == 1)
|
||||
out.printf("#%d", id);
|
||||
sp.printf("#%d", id);
|
||||
else
|
||||
out.printf("%d*#%d", scale, id);
|
||||
sp.printf("%d*#%d", scale, id);
|
||||
} else if (scale == -1) {
|
||||
out.printf("-#%d", id);
|
||||
sp.printf("-#%d", id);
|
||||
} else {
|
||||
out.printf("%d*#%d", scale, id);
|
||||
sp.printf("%d*#%d", scale, id);
|
||||
}
|
||||
}
|
||||
if (constant_ > 0)
|
||||
out.printf("+%d", constant_);
|
||||
sp.printf("+%d", constant_);
|
||||
else if (constant_ < 0)
|
||||
out.printf("%d", constant_);
|
||||
sp.printf("%d", constant_);
|
||||
}
|
||||
|
||||
void
|
||||
LinearSum::dump(FILE* fp) const
|
||||
{
|
||||
Sprinter sp(GetJitContext()->cx);
|
||||
sp.init();
|
||||
print(sp);
|
||||
fprintf(fp, "%s\n", sp.string());
|
||||
}
|
||||
|
||||
void
|
||||
LinearSum::dump() const
|
||||
{
|
||||
Fprinter out(stderr);
|
||||
dump(out);
|
||||
out.finish();
|
||||
dump(stderr);
|
||||
}
|
||||
|
||||
MDefinition*
|
||||
|
|
|
@ -148,7 +148,8 @@ class LinearSum
|
|||
LinearTerm term(size_t i) const { return terms_[i]; }
|
||||
void replaceTerm(size_t i, MDefinition* def) { terms_[i].term = def; }
|
||||
|
||||
void dump(GenericPrinter& out) const;
|
||||
void print(Sprinter& sp) const;
|
||||
void dump(FILE*) const;
|
||||
void dump() const;
|
||||
|
||||
private:
|
||||
|
|
|
@ -871,6 +871,7 @@ class IonBuilder
|
|||
InliningStatus inlineBailout(CallInfo& callInfo);
|
||||
InliningStatus inlineAssertFloat32(CallInfo& callInfo);
|
||||
InliningStatus inlineAssertRecoveredOnBailout(CallInfo& callInfo);
|
||||
InliningStatus inlineTrue(CallInfo& callInfo);
|
||||
|
||||
// Bind function.
|
||||
InliningStatus inlineBoundFunction(CallInfo& callInfo, JSFunction* target);
|
||||
|
|
|
@ -17,33 +17,47 @@
|
|||
using namespace js;
|
||||
using namespace js::jit;
|
||||
|
||||
JSONSpewer::~JSONSpewer()
|
||||
{
|
||||
if (fp_)
|
||||
fclose(fp_);
|
||||
}
|
||||
|
||||
void
|
||||
JSONSpewer::indent()
|
||||
{
|
||||
if (!fp_)
|
||||
return;
|
||||
MOZ_ASSERT(indentLevel_ >= 0);
|
||||
out_.printf("\n");
|
||||
fprintf(fp_, "\n");
|
||||
for (int i = 0; i < indentLevel_; i++)
|
||||
out_.printf(" ");
|
||||
fprintf(fp_, " ");
|
||||
}
|
||||
|
||||
void
|
||||
JSONSpewer::property(const char* name)
|
||||
{
|
||||
if (!fp_)
|
||||
return;
|
||||
|
||||
if (!first_)
|
||||
out_.printf(",");
|
||||
fprintf(fp_, ",");
|
||||
indent();
|
||||
out_.printf("\"%s\":", name);
|
||||
fprintf(fp_, "\"%s\":", name);
|
||||
first_ = false;
|
||||
}
|
||||
|
||||
void
|
||||
JSONSpewer::beginObject()
|
||||
{
|
||||
if (!fp_)
|
||||
return;
|
||||
|
||||
if (!first_) {
|
||||
out_.printf(",");
|
||||
fprintf(fp_, ",");
|
||||
indent();
|
||||
}
|
||||
out_.printf("{");
|
||||
fprintf(fp_, "{");
|
||||
indentLevel_++;
|
||||
first_ = true;
|
||||
}
|
||||
|
@ -51,8 +65,11 @@ JSONSpewer::beginObject()
|
|||
void
|
||||
JSONSpewer::beginObjectProperty(const char* name)
|
||||
{
|
||||
if (!fp_)
|
||||
return;
|
||||
|
||||
property(name);
|
||||
out_.printf("{");
|
||||
fprintf(fp_, "{");
|
||||
indentLevel_++;
|
||||
first_ = true;
|
||||
}
|
||||
|
@ -60,33 +77,27 @@ JSONSpewer::beginObjectProperty(const char* name)
|
|||
void
|
||||
JSONSpewer::beginListProperty(const char* name)
|
||||
{
|
||||
if (!fp_)
|
||||
return;
|
||||
|
||||
property(name);
|
||||
out_.printf("[");
|
||||
fprintf(fp_, "[");
|
||||
first_ = true;
|
||||
}
|
||||
|
||||
void
|
||||
JSONSpewer::beginStringProperty(const char* name)
|
||||
{
|
||||
property(name);
|
||||
out_.printf("\"");
|
||||
}
|
||||
|
||||
void
|
||||
JSONSpewer::endStringProperty()
|
||||
{
|
||||
out_.printf("\"");
|
||||
}
|
||||
|
||||
void
|
||||
JSONSpewer::stringProperty(const char* name, const char* format, ...)
|
||||
{
|
||||
if (!fp_)
|
||||
return;
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
|
||||
beginStringProperty(name);
|
||||
out_.vprintf(format, ap);
|
||||
endStringProperty();
|
||||
property(name);
|
||||
fprintf(fp_, "\"");
|
||||
vfprintf(fp_, format, ap);
|
||||
fprintf(fp_, "\"");
|
||||
|
||||
va_end(ap);
|
||||
}
|
||||
|
@ -94,14 +105,17 @@ JSONSpewer::stringProperty(const char* name, const char* format, ...)
|
|||
void
|
||||
JSONSpewer::stringValue(const char* format, ...)
|
||||
{
|
||||
if (!fp_)
|
||||
return;
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
|
||||
if (!first_)
|
||||
out_.printf(",");
|
||||
out_.printf("\"");
|
||||
out_.vprintf(format, ap);
|
||||
out_.printf("\"");
|
||||
fprintf(fp_, ",");
|
||||
fprintf(fp_, "\"");
|
||||
vfprintf(fp_, format, ap);
|
||||
fprintf(fp_, "\"");
|
||||
|
||||
va_end(ap);
|
||||
first_ = false;
|
||||
|
@ -110,44 +124,73 @@ JSONSpewer::stringValue(const char* format, ...)
|
|||
void
|
||||
JSONSpewer::integerProperty(const char* name, int value)
|
||||
{
|
||||
if (!fp_)
|
||||
return;
|
||||
|
||||
property(name);
|
||||
out_.printf("%d", value);
|
||||
fprintf(fp_, "%d", value);
|
||||
}
|
||||
|
||||
void
|
||||
JSONSpewer::integerValue(int value)
|
||||
{
|
||||
if (!fp_)
|
||||
return;
|
||||
|
||||
if (!first_)
|
||||
out_.printf(",");
|
||||
out_.printf("%d", value);
|
||||
fprintf(fp_, ",");
|
||||
fprintf(fp_, "%d", value);
|
||||
first_ = false;
|
||||
}
|
||||
|
||||
void
|
||||
JSONSpewer::endObject()
|
||||
{
|
||||
if (!fp_)
|
||||
return;
|
||||
|
||||
indentLevel_--;
|
||||
indent();
|
||||
out_.printf("}");
|
||||
fprintf(fp_, "}");
|
||||
first_ = false;
|
||||
}
|
||||
|
||||
void
|
||||
JSONSpewer::endList()
|
||||
{
|
||||
out_.printf("]");
|
||||
if (!fp_)
|
||||
return;
|
||||
|
||||
fprintf(fp_, "]");
|
||||
first_ = false;
|
||||
}
|
||||
|
||||
bool
|
||||
JSONSpewer::init(const char* path)
|
||||
{
|
||||
fp_ = fopen(path, "w");
|
||||
if (!fp_)
|
||||
return false;
|
||||
|
||||
beginObject();
|
||||
beginListProperty("functions");
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
JSONSpewer::beginFunction(JSScript* script)
|
||||
{
|
||||
if (inFunction_)
|
||||
endFunction();
|
||||
|
||||
beginObject();
|
||||
if (script)
|
||||
stringProperty("name", "%s:%d", script->filename(), script->lineno());
|
||||
else
|
||||
stringProperty("name", "asm.js compilation");
|
||||
beginListProperty("passes");
|
||||
|
||||
inFunction_ = true;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -171,13 +214,13 @@ JSONSpewer::spewMResumePoint(MResumePoint* rp)
|
|||
property("mode");
|
||||
switch (rp->mode()) {
|
||||
case MResumePoint::ResumeAt:
|
||||
out_.printf("\"At\"");
|
||||
fprintf(fp_, "\"At\"");
|
||||
break;
|
||||
case MResumePoint::ResumeAfter:
|
||||
out_.printf("\"After\"");
|
||||
fprintf(fp_, "\"After\"");
|
||||
break;
|
||||
case MResumePoint::Outer:
|
||||
out_.printf("\"Outer\"");
|
||||
fprintf(fp_, "\"Outer\"");
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -201,9 +244,9 @@ JSONSpewer::spewMDef(MDefinition* def)
|
|||
integerProperty("id", def->id());
|
||||
|
||||
property("opcode");
|
||||
out_.printf("\"");
|
||||
def->printOpcode(out_);
|
||||
out_.printf("\"");
|
||||
fprintf(fp_, "\"");
|
||||
def->printOpcode(fp_);
|
||||
fprintf(fp_, "\"");
|
||||
|
||||
beginListProperty("attributes");
|
||||
#define OUTPUT_ATTRIBUTE(X) do{ if(def->is##X()) stringValue(#X); } while(0);
|
||||
|
@ -233,10 +276,10 @@ JSONSpewer::spewMDef(MDefinition* def)
|
|||
isTruncated = static_cast<MBinaryArithInstruction*>(def)->isTruncated();
|
||||
|
||||
if (def->type() != MIRType_None && def->range()) {
|
||||
beginStringProperty("type");
|
||||
def->range()->dump(out_);
|
||||
out_.printf(" : %s%s", StringFromMIRType(def->type()), (isTruncated ? " (t)" : ""));
|
||||
endStringProperty();
|
||||
Sprinter sp(GetJitContext()->cx);
|
||||
sp.init();
|
||||
def->range()->print(sp);
|
||||
stringProperty("type", "%s : %s%s", sp.string(), StringFromMIRType(def->type()), (isTruncated ? " (t)" : ""));
|
||||
} else {
|
||||
stringProperty("type", "%s%s", StringFromMIRType(def->type()), (isTruncated ? " (t)" : ""));
|
||||
}
|
||||
|
@ -252,6 +295,9 @@ JSONSpewer::spewMDef(MDefinition* def)
|
|||
void
|
||||
JSONSpewer::spewMIR(MIRGraph* mir)
|
||||
{
|
||||
if (!fp_)
|
||||
return;
|
||||
|
||||
beginObjectProperty("mir");
|
||||
beginListProperty("blocks");
|
||||
|
||||
|
@ -298,14 +344,17 @@ JSONSpewer::spewMIR(MIRGraph* mir)
|
|||
void
|
||||
JSONSpewer::spewLIns(LNode* ins)
|
||||
{
|
||||
if (!fp_)
|
||||
return;
|
||||
|
||||
beginObject();
|
||||
|
||||
integerProperty("id", ins->id());
|
||||
|
||||
property("opcode");
|
||||
out_.printf("\"");
|
||||
ins->dump(out_);
|
||||
out_.printf("\"");
|
||||
fprintf(fp_, "\"");
|
||||
ins->dump(fp_);
|
||||
fprintf(fp_, "\"");
|
||||
|
||||
beginListProperty("defs");
|
||||
for (size_t i = 0; i < ins->numDefs(); i++)
|
||||
|
@ -318,6 +367,9 @@ JSONSpewer::spewLIns(LNode* ins)
|
|||
void
|
||||
JSONSpewer::spewLIR(MIRGraph* mir)
|
||||
{
|
||||
if (!fp_)
|
||||
return;
|
||||
|
||||
beginObjectProperty("lir");
|
||||
beginListProperty("blocks");
|
||||
|
||||
|
@ -346,6 +398,9 @@ JSONSpewer::spewLIR(MIRGraph* mir)
|
|||
void
|
||||
JSONSpewer::spewIntervals(BacktrackingAllocator* regalloc)
|
||||
{
|
||||
if (!fp_)
|
||||
return;
|
||||
|
||||
beginObjectProperty("intervals");
|
||||
beginListProperty("blocks");
|
||||
|
||||
|
@ -370,7 +425,7 @@ JSONSpewer::spewIntervals(BacktrackingAllocator* regalloc)
|
|||
if (live->numRanges()) {
|
||||
beginObject();
|
||||
property("allocation");
|
||||
out_.printf("\"%s\"", live->getAllocation()->toString());
|
||||
fprintf(fp_, "\"%s\"", live->getAllocation()->toString());
|
||||
beginListProperty("ranges");
|
||||
|
||||
for (size_t j = 0; j < live->numRanges(); j++) {
|
||||
|
@ -402,20 +457,33 @@ void
|
|||
JSONSpewer::endPass()
|
||||
{
|
||||
endObject();
|
||||
fflush(fp_);
|
||||
}
|
||||
|
||||
void
|
||||
JSONSpewer::endFunction()
|
||||
{
|
||||
MOZ_ASSERT(inFunction_);
|
||||
endList();
|
||||
endObject();
|
||||
fflush(fp_);
|
||||
inFunction_ = false;
|
||||
}
|
||||
|
||||
void
|
||||
JSONSpewer::spewDebuggerGraph(MIRGraph* graph)
|
||||
JSONSpewer::finish()
|
||||
{
|
||||
beginObject();
|
||||
spewMIR(graph);
|
||||
spewLIR(graph);
|
||||
if (!fp_)
|
||||
return;
|
||||
|
||||
if (inFunction_)
|
||||
endFunction();
|
||||
|
||||
endList();
|
||||
endObject();
|
||||
fprintf(fp_, "\n");
|
||||
|
||||
fclose(fp_);
|
||||
fp_ = nullptr;
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
#include <stdio.h>
|
||||
|
||||
#include "js/TypeDecls.h"
|
||||
#include "vm/Printer.h"
|
||||
|
||||
namespace js {
|
||||
namespace jit {
|
||||
|
@ -24,9 +23,13 @@ class LNode;
|
|||
class JSONSpewer
|
||||
{
|
||||
private:
|
||||
// Set by beginFunction(); unset by endFunction().
|
||||
// Used to correctly format output in case of abort during compilation.
|
||||
bool inFunction_;
|
||||
|
||||
int indentLevel_;
|
||||
bool first_;
|
||||
GenericPrinter& out_;
|
||||
FILE* fp_;
|
||||
|
||||
void indent();
|
||||
|
||||
|
@ -36,20 +39,21 @@ class JSONSpewer
|
|||
void beginListProperty(const char* name);
|
||||
void stringValue(const char* format, ...);
|
||||
void stringProperty(const char* name, const char* format, ...);
|
||||
void beginStringProperty(const char* name);
|
||||
void endStringProperty();
|
||||
void integerValue(int value);
|
||||
void integerProperty(const char* name, int value);
|
||||
void endObject();
|
||||
void endList();
|
||||
|
||||
public:
|
||||
explicit JSONSpewer(GenericPrinter& out)
|
||||
: indentLevel_(0),
|
||||
JSONSpewer()
|
||||
: inFunction_(false),
|
||||
indentLevel_(0),
|
||||
first_(true),
|
||||
out_(out)
|
||||
fp_(nullptr)
|
||||
{ }
|
||||
~JSONSpewer();
|
||||
|
||||
bool init(const char* path);
|
||||
void beginFunction(JSScript* script);
|
||||
void beginPass(const char * pass);
|
||||
void spewMDef(MDefinition* def);
|
||||
|
@ -60,8 +64,7 @@ class JSONSpewer
|
|||
void spewIntervals(BacktrackingAllocator* regalloc);
|
||||
void endPass();
|
||||
void endFunction();
|
||||
|
||||
void spewDebuggerGraph(MIRGraph* mir);
|
||||
void finish();
|
||||
};
|
||||
|
||||
} // namespace jit
|
||||
|
|
|
@ -8,8 +8,6 @@
|
|||
|
||||
#include "jit/JitSpewer.h"
|
||||
|
||||
#include "mozilla/Atomics.h"
|
||||
|
||||
#include "jit/Ion.h"
|
||||
#include "jit/MIR.h"
|
||||
|
||||
|
@ -28,67 +26,12 @@
|
|||
using namespace js;
|
||||
using namespace js::jit;
|
||||
|
||||
class IonSpewer
|
||||
{
|
||||
private:
|
||||
PRLock* outputLock_;
|
||||
Fprinter c1Output_;
|
||||
Fprinter jsonOutput_;
|
||||
bool firstFunction_;
|
||||
bool asyncLogging_;
|
||||
bool inited_;
|
||||
|
||||
void release();
|
||||
|
||||
public:
|
||||
IonSpewer()
|
||||
: firstFunction_(false),
|
||||
asyncLogging_(false),
|
||||
inited_(false)
|
||||
{ }
|
||||
|
||||
// File output is terminated safely upon destruction.
|
||||
~IonSpewer();
|
||||
|
||||
bool init();
|
||||
bool isEnabled() {
|
||||
return inited_;
|
||||
}
|
||||
void setAsyncLogging(bool incremental) {
|
||||
asyncLogging_ = incremental;
|
||||
}
|
||||
bool getAsyncLogging() {
|
||||
return asyncLogging_;
|
||||
}
|
||||
|
||||
void beginFunction();
|
||||
void spewPass(GraphSpewer* gs);
|
||||
void endFunction(GraphSpewer* gs);
|
||||
|
||||
// Lock used to sequentialized asynchronous compilation output.
|
||||
void lockOutput() {
|
||||
PR_Lock(outputLock_);
|
||||
}
|
||||
void unlockOutput() {
|
||||
PR_Unlock(outputLock_);
|
||||
}
|
||||
};
|
||||
|
||||
class AutoLockIonSpewerOutput
|
||||
{
|
||||
private:
|
||||
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
|
||||
public:
|
||||
explicit AutoLockIonSpewerOutput(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM);
|
||||
~AutoLockIonSpewerOutput();
|
||||
};
|
||||
|
||||
// IonSpewer singleton.
|
||||
static IonSpewer ionspewer;
|
||||
|
||||
static bool LoggingChecked = false;
|
||||
static uint32_t LoggingBits = 0;
|
||||
static mozilla::Atomic<uint32_t, mozilla::Relaxed> filteredOutCompilations(0);
|
||||
static uint32_t filteredOutCompilations = 0;
|
||||
|
||||
static const char * const ChannelNames[] =
|
||||
{
|
||||
|
@ -98,7 +41,7 @@ static const char * const ChannelNames[] =
|
|||
};
|
||||
|
||||
static bool
|
||||
FilterContainsLocation(JSScript* function)
|
||||
FilterContainsLocation(HandleScript function)
|
||||
{
|
||||
static const char* filter = getenv("IONFILTER");
|
||||
|
||||
|
@ -130,31 +73,48 @@ FilterContainsLocation(JSScript* function)
|
|||
}
|
||||
|
||||
void
|
||||
jit::EnableIonDebugSyncLogging()
|
||||
jit::EnableIonDebugLogging()
|
||||
{
|
||||
EnableChannel(JitSpew_IonLogs);
|
||||
ionspewer.init();
|
||||
ionspewer.setAsyncLogging(false);
|
||||
EnableChannel(JitSpew_IonSyncLogs);
|
||||
}
|
||||
|
||||
void
|
||||
jit::EnableIonDebugAsyncLogging()
|
||||
jit::IonSpewNewFunction(MIRGraph* graph, HandleScript func)
|
||||
{
|
||||
ionspewer.init();
|
||||
ionspewer.setAsyncLogging(true);
|
||||
if (GetJitContext()->runtime->onMainThread())
|
||||
ionspewer.beginFunction(graph, func);
|
||||
}
|
||||
|
||||
void
|
||||
IonSpewer::release()
|
||||
jit::IonSpewPass(const char* pass)
|
||||
{
|
||||
if (c1Output_.isInitialized())
|
||||
c1Output_.finish();
|
||||
if (jsonOutput_.isInitialized())
|
||||
jsonOutput_.finish();
|
||||
if (outputLock_)
|
||||
PR_DestroyLock(outputLock_);
|
||||
outputLock_ = nullptr;
|
||||
inited_ = false;
|
||||
if (GetJitContext()->runtime->onMainThread())
|
||||
ionspewer.spewPass(pass);
|
||||
}
|
||||
|
||||
void
|
||||
jit::IonSpewPass(const char* pass, BacktrackingAllocator* ra)
|
||||
{
|
||||
if (GetJitContext()->runtime->onMainThread())
|
||||
ionspewer.spewPass(pass, ra);
|
||||
}
|
||||
|
||||
void
|
||||
jit::IonSpewEndFunction()
|
||||
{
|
||||
if (GetJitContext()->runtime->onMainThread())
|
||||
ionspewer.endFunction();
|
||||
}
|
||||
|
||||
|
||||
IonSpewer::~IonSpewer()
|
||||
{
|
||||
if (!inited_)
|
||||
return;
|
||||
|
||||
c1Spewer.finish();
|
||||
jsonSpewer.finish();
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -163,191 +123,87 @@ IonSpewer::init()
|
|||
if (inited_)
|
||||
return true;
|
||||
|
||||
outputLock_ = PR_NewLock();
|
||||
if (!outputLock_ ||
|
||||
!c1Output_.init(JIT_SPEW_DIR "ion.cfg") ||
|
||||
!jsonOutput_.init(JIT_SPEW_DIR "ion.json"))
|
||||
{
|
||||
release();
|
||||
if (!c1Spewer.init(JIT_SPEW_DIR "ion.cfg"))
|
||||
return false;
|
||||
if (!jsonSpewer.init(JIT_SPEW_DIR "ion.json"))
|
||||
return false;
|
||||
}
|
||||
|
||||
jsonOutput_.printf("{\n \"functions\": [\n");
|
||||
firstFunction_ = true;
|
||||
|
||||
inited_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
AutoLockIonSpewerOutput::AutoLockIonSpewerOutput(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_IN_IMPL)
|
||||
bool
|
||||
IonSpewer::isSpewingFunction() const
|
||||
{
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||
ionspewer.lockOutput();
|
||||
}
|
||||
|
||||
AutoLockIonSpewerOutput::~AutoLockIonSpewerOutput()
|
||||
{
|
||||
ionspewer.unlockOutput();
|
||||
return inited_ && graph;
|
||||
}
|
||||
|
||||
void
|
||||
IonSpewer::beginFunction()
|
||||
{
|
||||
// If we are doing a synchronous logging then we spew everything as we go,
|
||||
// as this is useful in case of failure during the compilation. On the other
|
||||
// hand, it is recommended to disabled off main thread compilation.
|
||||
if (!getAsyncLogging() && !firstFunction_) {
|
||||
AutoLockIonSpewerOutput outputLock;
|
||||
jsonOutput_.put(","); // separate functions
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
IonSpewer::spewPass(GraphSpewer* gs)
|
||||
{
|
||||
if (!getAsyncLogging()) {
|
||||
AutoLockIonSpewerOutput outputLock;
|
||||
gs->dump(c1Output_, jsonOutput_);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
IonSpewer::endFunction(GraphSpewer* gs)
|
||||
{
|
||||
AutoLockIonSpewerOutput outputLock;
|
||||
if (getAsyncLogging() && !firstFunction_)
|
||||
jsonOutput_.put(","); // separate functions
|
||||
|
||||
gs->dump(c1Output_, jsonOutput_);
|
||||
firstFunction_ = false;
|
||||
}
|
||||
|
||||
IonSpewer::~IonSpewer()
|
||||
IonSpewer::beginFunction(MIRGraph* graph, HandleScript function)
|
||||
{
|
||||
if (!inited_)
|
||||
return;
|
||||
|
||||
jsonOutput_.printf("\n]}\n");
|
||||
release();
|
||||
}
|
||||
|
||||
void
|
||||
GraphSpewer::init(MIRGraph* graph, JSScript* function)
|
||||
{
|
||||
MOZ_ASSERT(!isSpewing());
|
||||
if (!ionspewer.isEnabled())
|
||||
return;
|
||||
|
||||
if (!FilterContainsLocation(function)) {
|
||||
MOZ_ASSERT(!this->graph);
|
||||
// filter out logs during the compilation.
|
||||
filteredOutCompilations++;
|
||||
MOZ_ASSERT(!isSpewing());
|
||||
return;
|
||||
}
|
||||
|
||||
graph_ = graph;
|
||||
MOZ_ASSERT(isSpewing());
|
||||
this->graph = graph;
|
||||
|
||||
c1Spewer.beginFunction(graph, function);
|
||||
jsonSpewer.beginFunction(function);
|
||||
}
|
||||
|
||||
void
|
||||
GraphSpewer::beginFunction(JSScript* function)
|
||||
IonSpewer::spewPass(const char* pass)
|
||||
{
|
||||
if (!isSpewing())
|
||||
if (!isSpewingFunction())
|
||||
return;
|
||||
|
||||
c1Spewer_.beginFunction(graph_, function);
|
||||
jsonSpewer_.beginFunction(function);
|
||||
|
||||
ionspewer.beginFunction();
|
||||
c1Spewer.spewPass(pass);
|
||||
jsonSpewer.beginPass(pass);
|
||||
jsonSpewer.spewMIR(graph);
|
||||
jsonSpewer.spewLIR(graph);
|
||||
jsonSpewer.endPass();
|
||||
}
|
||||
|
||||
void
|
||||
GraphSpewer::spewPass(const char* pass)
|
||||
IonSpewer::spewPass(const char* pass, BacktrackingAllocator* ra)
|
||||
{
|
||||
if (!isSpewing())
|
||||
if (!isSpewingFunction())
|
||||
return;
|
||||
|
||||
c1Spewer_.spewPass(pass);
|
||||
|
||||
jsonSpewer_.beginPass(pass);
|
||||
jsonSpewer_.spewMIR(graph_);
|
||||
jsonSpewer_.spewLIR(graph_);
|
||||
jsonSpewer_.endPass();
|
||||
|
||||
ionspewer.spewPass(this);
|
||||
c1Spewer.spewPass(pass);
|
||||
c1Spewer.spewIntervals(pass, ra);
|
||||
jsonSpewer.beginPass(pass);
|
||||
jsonSpewer.spewMIR(graph);
|
||||
jsonSpewer.spewLIR(graph);
|
||||
jsonSpewer.spewIntervals(ra);
|
||||
jsonSpewer.endPass();
|
||||
}
|
||||
|
||||
void
|
||||
GraphSpewer::spewPass(const char* pass, BacktrackingAllocator* ra)
|
||||
IonSpewer::endFunction()
|
||||
{
|
||||
if (!isSpewing())
|
||||
return;
|
||||
|
||||
c1Spewer_.spewPass(pass);
|
||||
c1Spewer_.spewIntervals(pass, ra);
|
||||
|
||||
jsonSpewer_.beginPass(pass);
|
||||
jsonSpewer_.spewMIR(graph_);
|
||||
jsonSpewer_.spewLIR(graph_);
|
||||
jsonSpewer_.spewIntervals(ra);
|
||||
jsonSpewer_.endPass();
|
||||
|
||||
ionspewer.spewPass(this);
|
||||
}
|
||||
|
||||
void
|
||||
GraphSpewer::endFunction()
|
||||
{
|
||||
if (!ionspewer.isEnabled())
|
||||
return;
|
||||
|
||||
if (!isSpewing()) {
|
||||
MOZ_ASSERT(filteredOutCompilations != 0);
|
||||
filteredOutCompilations--;
|
||||
if (!isSpewingFunction()) {
|
||||
if (inited_) {
|
||||
MOZ_ASSERT(filteredOutCompilations != 0);
|
||||
filteredOutCompilations--;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
c1Spewer_.endFunction();
|
||||
jsonSpewer_.endFunction();
|
||||
c1Spewer.endFunction();
|
||||
jsonSpewer.endFunction();
|
||||
|
||||
ionspewer.endFunction(this);
|
||||
graph_ = nullptr;
|
||||
this->graph = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
GraphSpewer::dump(Fprinter& c1Out, Fprinter& jsonOut)
|
||||
{
|
||||
if (!c1Printer_.hadOutOfMemory())
|
||||
c1Printer_.exportInto(c1Out);
|
||||
c1Printer_.clear();
|
||||
|
||||
if (!jsonPrinter_.hadOutOfMemory())
|
||||
jsonPrinter_.exportInto(jsonOut);
|
||||
else
|
||||
jsonOut.put("{}");
|
||||
jsonPrinter_.clear();
|
||||
}
|
||||
|
||||
void
|
||||
jit::SpewBeginFunction(MIRGenerator* mir, JSScript* function)
|
||||
{
|
||||
MIRGraph* graph = &mir->graph();
|
||||
mir->graphSpewer().init(graph, function);
|
||||
mir->graphSpewer().beginFunction(function);
|
||||
}
|
||||
|
||||
AutoSpewEndFunction::~AutoSpewEndFunction()
|
||||
{
|
||||
mir_->graphSpewer().endFunction();
|
||||
}
|
||||
|
||||
Fprinter&
|
||||
jit::JitSpewPrinter()
|
||||
{
|
||||
static Fprinter out;
|
||||
return out;
|
||||
}
|
||||
|
||||
FILE* jit::JitSpewFile = nullptr;
|
||||
|
||||
static bool
|
||||
ContainsFlag(const char* str, const char* flag)
|
||||
|
@ -398,7 +254,6 @@ jit::CheckLogging()
|
|||
" range Range Analysis\n"
|
||||
" unroll Loop unrolling\n"
|
||||
" logs C1 and JSON visualization logging\n"
|
||||
" logs-sync Same as logs, but flushes between each pass (sync. compiled functions only).\n"
|
||||
" profiling Profiling-related information\n"
|
||||
" trackopts Optimization tracking information\n"
|
||||
" all Everything\n"
|
||||
|
@ -458,9 +313,7 @@ jit::CheckLogging()
|
|||
if (ContainsFlag(env, "cacheflush"))
|
||||
EnableChannel(JitSpew_CacheFlush);
|
||||
if (ContainsFlag(env, "logs"))
|
||||
EnableIonDebugAsyncLogging();
|
||||
if (ContainsFlag(env, "logs-sync"))
|
||||
EnableIonDebugSyncLogging();
|
||||
EnableIonDebugLogging();
|
||||
if (ContainsFlag(env, "profiling"))
|
||||
EnableChannel(JitSpew_Profiling);
|
||||
if (ContainsFlag(env, "trackopts"))
|
||||
|
@ -495,7 +348,7 @@ jit::CheckLogging()
|
|||
EnableChannel(JitSpew_BaselineDebugModeOSR);
|
||||
}
|
||||
|
||||
JitSpewPrinter().init(stderr);
|
||||
JitSpewFile = stderr;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -549,10 +402,9 @@ jit::JitSpewDef(JitSpewChannel channel, const char* str, MDefinition* def)
|
|||
return;
|
||||
|
||||
JitSpewHeader(channel);
|
||||
Fprinter& out = JitSpewPrinter();
|
||||
out.put(str);
|
||||
def->dump(out);
|
||||
def->dumpLocation(out);
|
||||
fprintf(JitSpewFile, "%s", str);
|
||||
def->dump(JitSpewFile);
|
||||
def->dumpLocation(JitSpewFile);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -602,5 +454,15 @@ jit::DisableChannel(JitSpewChannel channel)
|
|||
LoggingBits &= ~(1 << uint32_t(channel));
|
||||
}
|
||||
|
||||
IonSpewFunction::IonSpewFunction(MIRGraph* graph, JS::HandleScript function)
|
||||
{
|
||||
IonSpewNewFunction(graph, function);
|
||||
}
|
||||
|
||||
IonSpewFunction::~IonSpewFunction()
|
||||
{
|
||||
IonSpewEndFunction();
|
||||
}
|
||||
|
||||
#endif /* DEBUG */
|
||||
|
||||
|
|
|
@ -77,7 +77,7 @@ namespace jit {
|
|||
/* Information about compiled scripts */\
|
||||
_(IonScripts) \
|
||||
/* Info about failing to log script */ \
|
||||
_(IonSyncLogs) \
|
||||
_(IonLogs) \
|
||||
/* Information during MIR building */ \
|
||||
_(IonMIR) \
|
||||
/* Information during bailouts */ \
|
||||
|
@ -103,52 +103,44 @@ static const int NULL_ID = -1;
|
|||
|
||||
#ifdef DEBUG
|
||||
|
||||
// Class made to hold the MIR and LIR graphs of an AsmJS / Ion compilation.
|
||||
class GraphSpewer
|
||||
class IonSpewer
|
||||
{
|
||||
private:
|
||||
MIRGraph* graph_;
|
||||
LSprinter c1Printer_;
|
||||
LSprinter jsonPrinter_;
|
||||
C1Spewer c1Spewer_;
|
||||
JSONSpewer jsonSpewer_;
|
||||
MIRGraph* graph;
|
||||
C1Spewer c1Spewer;
|
||||
JSONSpewer jsonSpewer;
|
||||
bool inited_;
|
||||
|
||||
public:
|
||||
explicit GraphSpewer(TempAllocator *alloc)
|
||||
: graph_(nullptr),
|
||||
c1Printer_(alloc->lifoAlloc()),
|
||||
jsonPrinter_(alloc->lifoAlloc()),
|
||||
c1Spewer_(c1Printer_),
|
||||
jsonSpewer_(jsonPrinter_)
|
||||
IonSpewer()
|
||||
: graph(nullptr), inited_(false)
|
||||
{ }
|
||||
|
||||
bool isSpewing() const {
|
||||
return graph_;
|
||||
}
|
||||
void init(MIRGraph* graph, JSScript* function);
|
||||
void beginFunction(JSScript* function);
|
||||
// File output is terminated safely upon destruction.
|
||||
~IonSpewer();
|
||||
|
||||
bool init();
|
||||
void beginFunction(MIRGraph* graph, JS::HandleScript);
|
||||
bool isSpewingFunction() const;
|
||||
void spewPass(const char* pass);
|
||||
void spewPass(const char* pass, BacktrackingAllocator* ra);
|
||||
void endFunction();
|
||||
|
||||
void dump(Fprinter& c1, Fprinter& json);
|
||||
};
|
||||
|
||||
void SpewBeginFunction(MIRGenerator* mir, JSScript* function);
|
||||
class AutoSpewEndFunction
|
||||
class IonSpewFunction
|
||||
{
|
||||
private:
|
||||
MIRGenerator* mir_;
|
||||
|
||||
public:
|
||||
explicit AutoSpewEndFunction(MIRGenerator* mir)
|
||||
: mir_(mir)
|
||||
{ }
|
||||
~AutoSpewEndFunction();
|
||||
IonSpewFunction(MIRGraph* graph, JS::HandleScript function);
|
||||
~IonSpewFunction();
|
||||
};
|
||||
|
||||
void IonSpewNewFunction(MIRGraph* graph, JS::HandleScript function);
|
||||
void IonSpewPass(const char* pass);
|
||||
void IonSpewPass(const char* pass, BacktrackingAllocator* ra);
|
||||
void IonSpewEndFunction();
|
||||
|
||||
void CheckLogging();
|
||||
Fprinter& JitSpewPrinter();
|
||||
extern FILE* JitSpewFile;
|
||||
void JitSpew(JitSpewChannel channel, const char* fmt, ...);
|
||||
void JitSpewStart(JitSpewChannel channel, const char* fmt, ...);
|
||||
void JitSpewCont(JitSpewChannel channel, const char* fmt, ...);
|
||||
|
@ -162,42 +154,22 @@ void JitSpewDef(JitSpewChannel channel, const char* str, MDefinition* def);
|
|||
|
||||
void EnableChannel(JitSpewChannel channel);
|
||||
void DisableChannel(JitSpewChannel channel);
|
||||
void EnableIonDebugSyncLogging();
|
||||
void EnableIonDebugAsyncLogging();
|
||||
void EnableIonDebugLogging();
|
||||
|
||||
#else
|
||||
|
||||
class GraphSpewer
|
||||
{
|
||||
public:
|
||||
GraphSpewer(TempAllocator *alloc) { }
|
||||
|
||||
bool isSpewing() { return false; }
|
||||
void init(MIRGraph* graph, JSScript* function) { }
|
||||
void beginFunction(JSScript* function) { }
|
||||
void spewPass(const char* pass) { }
|
||||
void spewPass(const char* pass, BacktrackingAllocator* ra) { }
|
||||
void endFunction() { }
|
||||
|
||||
void dump(Fprinter& c1, Fprinter& json) { }
|
||||
};
|
||||
|
||||
static inline void SpewBeginFunction(MIRGenerator* mir, JSScript* function)
|
||||
static inline void IonSpewNewFunction(MIRGraph* graph, JS::HandleScript function)
|
||||
{ }
|
||||
static inline void IonSpewPass(const char* pass)
|
||||
{ }
|
||||
static inline void IonSpewPass(const char* pass, BacktrackingAllocator* ra)
|
||||
{ }
|
||||
static inline void IonSpewEndFunction()
|
||||
{ }
|
||||
|
||||
class AutoSpewEndFunction
|
||||
{
|
||||
public:
|
||||
explicit AutoSpewEndFunction(MIRGenerator* mir) { }
|
||||
~AutoSpewEndFunction() { }
|
||||
};
|
||||
|
||||
static inline void CheckLogging()
|
||||
{ }
|
||||
static inline Fprinter& JitSpewPrinter()
|
||||
{
|
||||
MOZ_CRASH("No empty backend for JitSpewPrinter");
|
||||
}
|
||||
static FILE* const JitSpewFile = nullptr;
|
||||
static inline void JitSpew(JitSpewChannel, const char* fmt, ...)
|
||||
{ }
|
||||
static inline void JitSpewStart(JitSpewChannel channel, const char* fmt, ...)
|
||||
|
@ -220,9 +192,7 @@ static inline void EnableChannel(JitSpewChannel)
|
|||
{ }
|
||||
static inline void DisableChannel(JitSpewChannel)
|
||||
{ }
|
||||
static inline void EnableIonDebugSyncLogging()
|
||||
{ }
|
||||
static inline void EnableIonDebugAsyncLogging()
|
||||
static inline void EnableIonDebugLogging()
|
||||
{ }
|
||||
|
||||
#endif /* DEBUG */
|
||||
|
|
|
@ -106,7 +106,7 @@ class LMoveGroup : public LInstructionHelper<0, 0, 0>
|
|||
return new(alloc) LMoveGroup(alloc);
|
||||
}
|
||||
|
||||
void printOperands(GenericPrinter& out);
|
||||
void printOperands(FILE* fp);
|
||||
|
||||
// Add a move which takes place simultaneously with all others in the group.
|
||||
bool add(LAllocation* from, LAllocation* to, LDefinition::Type type);
|
||||
|
|
|
@ -57,20 +57,18 @@ LIRGraph::noteNeedsSafepoint(LInstruction* ins)
|
|||
}
|
||||
|
||||
void
|
||||
LIRGraph::dump(GenericPrinter& out)
|
||||
LIRGraph::dump(FILE* fp)
|
||||
{
|
||||
for (size_t i = 0; i < numBlocks(); i++) {
|
||||
getBlock(i)->dump(out);
|
||||
out.printf("\n");
|
||||
getBlock(i)->dump(fp);
|
||||
fprintf(fp, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
LIRGraph::dump()
|
||||
{
|
||||
Fprinter out(stderr);
|
||||
dump(out);
|
||||
out.finish();
|
||||
dump(stderr);
|
||||
}
|
||||
|
||||
LBlock::LBlock(MBasicBlock* from)
|
||||
|
@ -152,25 +150,23 @@ LBlock::getExitMoveGroup(TempAllocator& alloc)
|
|||
}
|
||||
|
||||
void
|
||||
LBlock::dump(GenericPrinter& out)
|
||||
LBlock::dump(FILE* fp)
|
||||
{
|
||||
out.printf("block%u:\n", mir()->id());
|
||||
fprintf(fp, "block%u:\n", mir()->id());
|
||||
for (size_t i = 0; i < numPhis(); ++i) {
|
||||
getPhi(i)->dump(out);
|
||||
out.printf("\n");
|
||||
getPhi(i)->dump(fp);
|
||||
fprintf(fp, "\n");
|
||||
}
|
||||
for (LInstructionIterator iter = begin(); iter != end(); iter++) {
|
||||
iter->dump(out);
|
||||
out.printf("\n");
|
||||
iter->dump(fp);
|
||||
fprintf(fp, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
LBlock::dump()
|
||||
{
|
||||
Fprinter out(stderr);
|
||||
dump(out);
|
||||
out.finish();
|
||||
dump(stderr);
|
||||
}
|
||||
|
||||
static size_t
|
||||
|
@ -313,7 +309,7 @@ LSnapshot::rewriteRecoveredInput(LUse input)
|
|||
}
|
||||
|
||||
void
|
||||
LNode::printName(GenericPrinter& out, Opcode op)
|
||||
LNode::printName(FILE* fp, Opcode op)
|
||||
{
|
||||
static const char * const names[] =
|
||||
{
|
||||
|
@ -324,13 +320,13 @@ LNode::printName(GenericPrinter& out, Opcode op)
|
|||
const char* name = names[op];
|
||||
size_t len = strlen(name);
|
||||
for (size_t i = 0; i < len; i++)
|
||||
out.printf("%c", tolower(name[i]));
|
||||
fprintf(fp, "%c", tolower(name[i]));
|
||||
}
|
||||
|
||||
void
|
||||
LNode::printName(GenericPrinter& out)
|
||||
LNode::printName(FILE* fp)
|
||||
{
|
||||
printName(out, op());
|
||||
printName(fp, op());
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -460,12 +456,12 @@ LDefinition::dump() const
|
|||
}
|
||||
|
||||
void
|
||||
LNode::printOperands(GenericPrinter& out)
|
||||
LNode::printOperands(FILE* fp)
|
||||
{
|
||||
for (size_t i = 0, e = numOperands(); i < e; i++) {
|
||||
out.printf(" (%s)", getOperand(i)->toString());
|
||||
fprintf(fp, " (%s)", getOperand(i)->toString());
|
||||
if (i != numOperands() - 1)
|
||||
out.printf(",");
|
||||
fprintf(fp, ",");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -478,59 +474,56 @@ LInstruction::assignSnapshot(LSnapshot* snapshot)
|
|||
#ifdef DEBUG
|
||||
if (JitSpewEnabled(JitSpew_IonSnapshots)) {
|
||||
JitSpewHeader(JitSpew_IonSnapshots);
|
||||
Fprinter& out = JitSpewPrinter();
|
||||
out.printf("Assigning snapshot %p to instruction %p (",
|
||||
(void*)snapshot, (void*)this);
|
||||
printName(out);
|
||||
out.printf(")\n");
|
||||
fprintf(JitSpewFile, "Assigning snapshot %p to instruction %p (",
|
||||
(void*)snapshot, (void*)this);
|
||||
printName(JitSpewFile);
|
||||
fprintf(JitSpewFile, ")\n");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
LNode::dump(GenericPrinter& out)
|
||||
LNode::dump(FILE* fp)
|
||||
{
|
||||
if (numDefs() != 0) {
|
||||
out.printf("{");
|
||||
fprintf(fp, "{");
|
||||
for (size_t i = 0; i < numDefs(); i++) {
|
||||
out.printf("%s", getDef(i)->toString());
|
||||
fprintf(fp, "%s", getDef(i)->toString());
|
||||
if (i != numDefs() - 1)
|
||||
out.printf(", ");
|
||||
fprintf(fp, ", ");
|
||||
}
|
||||
out.printf("} <- ");
|
||||
fprintf(fp, "} <- ");
|
||||
}
|
||||
|
||||
printName(out);
|
||||
printOperands(out);
|
||||
printName(fp);
|
||||
printOperands(fp);
|
||||
|
||||
if (numTemps()) {
|
||||
out.printf(" t=(");
|
||||
fprintf(fp, " t=(");
|
||||
for (size_t i = 0; i < numTemps(); i++) {
|
||||
out.printf("%s", getTemp(i)->toString());
|
||||
fprintf(fp, "%s", getTemp(i)->toString());
|
||||
if (i != numTemps() - 1)
|
||||
out.printf(", ");
|
||||
fprintf(fp, ", ");
|
||||
}
|
||||
out.printf(")");
|
||||
fprintf(fp, ")");
|
||||
}
|
||||
|
||||
if (numSuccessors()) {
|
||||
out.printf(" s=(");
|
||||
fprintf(fp, " s=(");
|
||||
for (size_t i = 0; i < numSuccessors(); i++) {
|
||||
out.printf("block%u", getSuccessor(i)->id());
|
||||
fprintf(fp, "block%u", getSuccessor(i)->id());
|
||||
if (i != numSuccessors() - 1)
|
||||
out.printf(", ");
|
||||
fprintf(fp, ", ");
|
||||
}
|
||||
out.printf(")");
|
||||
fprintf(fp, ")");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
LNode::dump()
|
||||
{
|
||||
Fprinter out(stderr);
|
||||
dump(out);
|
||||
out.printf("\n");
|
||||
out.finish();
|
||||
dump(stderr);
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -598,18 +591,18 @@ LMoveGroup::addAfter(LAllocation* from, LAllocation* to, LDefinition::Type type)
|
|||
}
|
||||
|
||||
void
|
||||
LMoveGroup::printOperands(GenericPrinter& out)
|
||||
LMoveGroup::printOperands(FILE* fp)
|
||||
{
|
||||
for (size_t i = 0; i < numMoves(); i++) {
|
||||
const LMove& move = getMove(i);
|
||||
// Use two printfs, as LAllocation::toString is not reentrant.
|
||||
out.printf(" [%s", move.from()->toString());
|
||||
out.printf(" -> %s", move.to()->toString());
|
||||
fprintf(fp, " [%s", move.from()->toString());
|
||||
fprintf(fp, " -> %s", move.to()->toString());
|
||||
#ifdef DEBUG
|
||||
out.printf(", %s", TypeChars[move.type()]);
|
||||
fprintf(fp, ", %s", TypeChars[move.type()]);
|
||||
#endif
|
||||
out.printf("]");
|
||||
fprintf(fp, "]");
|
||||
if (i != numMoves() - 1)
|
||||
out.printf(",");
|
||||
fprintf(fp, ",");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -702,11 +702,11 @@ class LNode
|
|||
return false;
|
||||
}
|
||||
|
||||
virtual void dump(GenericPrinter& out);
|
||||
virtual void dump(FILE* fp);
|
||||
void dump();
|
||||
static void printName(GenericPrinter& out, Opcode op);
|
||||
virtual void printName(GenericPrinter& out);
|
||||
virtual void printOperands(GenericPrinter& out);
|
||||
static void printName(FILE* fp, Opcode op);
|
||||
virtual void printName(FILE* fp);
|
||||
virtual void printOperands(FILE* fp);
|
||||
|
||||
public:
|
||||
// Opcode testing and casts.
|
||||
|
@ -986,7 +986,7 @@ class LBlock
|
|||
return begin()->isGoto() && !mir()->isLoopHeader();
|
||||
}
|
||||
|
||||
void dump(GenericPrinter& out);
|
||||
void dump(FILE* fp);
|
||||
void dump();
|
||||
};
|
||||
|
||||
|
@ -1799,7 +1799,7 @@ class LIRGraph
|
|||
return safepoints_[i];
|
||||
}
|
||||
|
||||
void dump(GenericPrinter& out);
|
||||
void dump(FILE* fp);
|
||||
void dump();
|
||||
};
|
||||
|
||||
|
|
|
@ -4172,28 +4172,27 @@ LIRGenerator::visitNurseryObject(MNurseryObject* ins)
|
|||
static void
|
||||
SpewResumePoint(MBasicBlock* block, MInstruction* ins, MResumePoint* resumePoint)
|
||||
{
|
||||
Fprinter& out = JitSpewPrinter();
|
||||
out.printf("Current resume point %p details:\n", (void*)resumePoint);
|
||||
out.printf(" frame count: %u\n", resumePoint->frameCount());
|
||||
fprintf(JitSpewFile, "Current resume point %p details:\n", (void*)resumePoint);
|
||||
fprintf(JitSpewFile, " frame count: %u\n", resumePoint->frameCount());
|
||||
|
||||
if (ins) {
|
||||
out.printf(" taken after: ");
|
||||
ins->printName(out);
|
||||
fprintf(JitSpewFile, " taken after: ");
|
||||
ins->printName(JitSpewFile);
|
||||
} else {
|
||||
out.printf(" taken at block %d entry", block->id());
|
||||
fprintf(JitSpewFile, " taken at block %d entry", block->id());
|
||||
}
|
||||
out.printf("\n");
|
||||
fprintf(JitSpewFile, "\n");
|
||||
|
||||
out.printf(" pc: %p (script: %p, offset: %d)\n",
|
||||
fprintf(JitSpewFile, " pc: %p (script: %p, offset: %d)\n",
|
||||
(void*)resumePoint->pc(),
|
||||
(void*)resumePoint->block()->info().script(),
|
||||
int(resumePoint->block()->info().script()->pcToOffset(resumePoint->pc())));
|
||||
|
||||
for (size_t i = 0, e = resumePoint->numOperands(); i < e; i++) {
|
||||
MDefinition* in = resumePoint->getOperand(i);
|
||||
out.printf(" slot%u: ", (unsigned)i);
|
||||
in->printName(out);
|
||||
out.printf("\n");
|
||||
fprintf(JitSpewFile, " slot%u: ", (unsigned)i);
|
||||
in->printName(JitSpewFile);
|
||||
fprintf(JitSpewFile, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -263,6 +263,8 @@ IonBuilder::inlineNativeCall(CallInfo& callInfo, JSFunction* target)
|
|||
return inlineAssertFloat32(callInfo);
|
||||
if (native == testingFunc_assertRecoveredOnBailout)
|
||||
return inlineAssertRecoveredOnBailout(callInfo);
|
||||
if (native == testingFunc_inIon || native == testingFunc_inJit)
|
||||
return inlineTrue(callInfo);
|
||||
|
||||
// Bound function
|
||||
if (native == js::CallOrConstructBoundFunction)
|
||||
|
@ -2670,6 +2672,15 @@ IonBuilder::inlineBailout(CallInfo& callInfo)
|
|||
return InliningStatus_Inlined;
|
||||
}
|
||||
|
||||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineTrue(CallInfo& callInfo)
|
||||
{
|
||||
callInfo.setImplicitlyUsedUnchecked();
|
||||
|
||||
pushConstant(BooleanValue(true));
|
||||
return InliningStatus_Inlined;
|
||||
}
|
||||
|
||||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineAssertFloat32(CallInfo& callInfo)
|
||||
{
|
||||
|
|
|
@ -61,7 +61,7 @@ CheckUsesAreFloat32Consumers(MInstruction* ins)
|
|||
}
|
||||
|
||||
void
|
||||
MDefinition::PrintOpcodeName(GenericPrinter& out, MDefinition::Opcode op)
|
||||
MDefinition::PrintOpcodeName(FILE* fp, MDefinition::Opcode op)
|
||||
{
|
||||
static const char * const names[] =
|
||||
{
|
||||
|
@ -72,7 +72,7 @@ MDefinition::PrintOpcodeName(GenericPrinter& out, MDefinition::Opcode op)
|
|||
const char* name = names[op];
|
||||
size_t len = strlen(name);
|
||||
for (size_t i = 0; i < len; i++)
|
||||
out.printf("%c", tolower(name[i]));
|
||||
fprintf(fp, "%c", tolower(name[i]));
|
||||
}
|
||||
|
||||
const Value&
|
||||
|
@ -209,10 +209,10 @@ EvaluateExactReciprocal(TempAllocator& alloc, MDiv* ins)
|
|||
}
|
||||
|
||||
void
|
||||
MDefinition::printName(GenericPrinter& out) const
|
||||
MDefinition::printName(FILE* fp) const
|
||||
{
|
||||
PrintOpcodeName(out, op());
|
||||
out.printf("%u", id());
|
||||
PrintOpcodeName(fp, op());
|
||||
fprintf(fp, "%u", id());
|
||||
}
|
||||
|
||||
HashNumber
|
||||
|
@ -439,42 +439,40 @@ MTest::filtersUndefinedOrNull(bool trueBranch, MDefinition** subject, bool* filt
|
|||
}
|
||||
|
||||
void
|
||||
MDefinition::printOpcode(GenericPrinter& out) const
|
||||
MDefinition::printOpcode(FILE* fp) const
|
||||
{
|
||||
PrintOpcodeName(out, op());
|
||||
PrintOpcodeName(fp, op());
|
||||
for (size_t j = 0, e = numOperands(); j < e; j++) {
|
||||
out.printf(" ");
|
||||
fprintf(fp, " ");
|
||||
if (getUseFor(j)->hasProducer())
|
||||
getOperand(j)->printName(out);
|
||||
getOperand(j)->printName(fp);
|
||||
else
|
||||
out.printf("(null)");
|
||||
fprintf(fp, "(null)");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MDefinition::dump(GenericPrinter& out) const
|
||||
MDefinition::dump(FILE* fp) const
|
||||
{
|
||||
printName(out);
|
||||
out.printf(" = ");
|
||||
printOpcode(out);
|
||||
out.printf("\n");
|
||||
printName(fp);
|
||||
fprintf(fp, " = ");
|
||||
printOpcode(fp);
|
||||
fprintf(fp, "\n");
|
||||
|
||||
if (isInstruction()) {
|
||||
if (MResumePoint* resume = toInstruction()->resumePoint())
|
||||
resume->dump(out);
|
||||
resume->dump(fp);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MDefinition::dump() const
|
||||
{
|
||||
Fprinter out(stderr);
|
||||
dump(out);
|
||||
out.finish();
|
||||
dump(stderr);
|
||||
}
|
||||
|
||||
void
|
||||
MDefinition::dumpLocation(GenericPrinter& out) const
|
||||
MDefinition::dumpLocation(FILE* fp) const
|
||||
{
|
||||
MResumePoint* rp = nullptr;
|
||||
const char* linkWord = nullptr;
|
||||
|
@ -489,7 +487,7 @@ MDefinition::dumpLocation(GenericPrinter& out) const
|
|||
while (rp) {
|
||||
JSScript* script = rp->block()->info().script();
|
||||
uint32_t lineno = PCToLineNumber(rp->block()->info().script(), rp->pc());
|
||||
out.printf(" %s %s:%d\n", linkWord, script->filename(), lineno);
|
||||
fprintf(fp, " %s %s:%d\n", linkWord, script->filename(), lineno);
|
||||
rp = rp->caller();
|
||||
linkWord = "in";
|
||||
}
|
||||
|
@ -498,9 +496,7 @@ MDefinition::dumpLocation(GenericPrinter& out) const
|
|||
void
|
||||
MDefinition::dumpLocation() const
|
||||
{
|
||||
Fprinter out(stderr);
|
||||
dumpLocation(out);
|
||||
out.finish();
|
||||
dumpLocation(stderr);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
|
@ -757,72 +753,72 @@ MConstant::congruentTo(const MDefinition* ins) const
|
|||
}
|
||||
|
||||
void
|
||||
MConstant::printOpcode(GenericPrinter& out) const
|
||||
MConstant::printOpcode(FILE* fp) const
|
||||
{
|
||||
PrintOpcodeName(out, op());
|
||||
out.printf(" ");
|
||||
PrintOpcodeName(fp, op());
|
||||
fprintf(fp, " ");
|
||||
switch (type()) {
|
||||
case MIRType_Undefined:
|
||||
out.printf("undefined");
|
||||
fprintf(fp, "undefined");
|
||||
break;
|
||||
case MIRType_Null:
|
||||
out.printf("null");
|
||||
fprintf(fp, "null");
|
||||
break;
|
||||
case MIRType_Boolean:
|
||||
out.printf(value().toBoolean() ? "true" : "false");
|
||||
fprintf(fp, value().toBoolean() ? "true" : "false");
|
||||
break;
|
||||
case MIRType_Int32:
|
||||
out.printf("0x%x", value().toInt32());
|
||||
fprintf(fp, "0x%x", value().toInt32());
|
||||
break;
|
||||
case MIRType_Double:
|
||||
out.printf("%f", value().toDouble());
|
||||
fprintf(fp, "%f", value().toDouble());
|
||||
break;
|
||||
case MIRType_Float32:
|
||||
{
|
||||
float val = value().toDouble();
|
||||
out.printf("%f", val);
|
||||
fprintf(fp, "%f", val);
|
||||
break;
|
||||
}
|
||||
case MIRType_Object:
|
||||
if (value().toObject().is<JSFunction>()) {
|
||||
JSFunction* fun = &value().toObject().as<JSFunction>();
|
||||
if (fun->displayAtom()) {
|
||||
out.put("function ");
|
||||
EscapedStringPrinter(out, fun->displayAtom(), 0);
|
||||
fputs("function ", fp);
|
||||
FileEscapedString(fp, fun->displayAtom(), 0);
|
||||
} else {
|
||||
out.put("unnamed function");
|
||||
fputs("unnamed function", fp);
|
||||
}
|
||||
if (fun->hasScript()) {
|
||||
JSScript* script = fun->nonLazyScript();
|
||||
out.printf(" (%s:%" PRIuSIZE ")",
|
||||
fprintf(fp, " (%s:%" PRIuSIZE ")",
|
||||
script->filename() ? script->filename() : "", script->lineno());
|
||||
}
|
||||
out.printf(" at %p", (void*) fun);
|
||||
fprintf(fp, " at %p", (void*) fun);
|
||||
break;
|
||||
}
|
||||
out.printf("object %p (%s)", (void*)&value().toObject(),
|
||||
fprintf(fp, "object %p (%s)", (void*)&value().toObject(),
|
||||
value().toObject().getClass()->name);
|
||||
break;
|
||||
case MIRType_Symbol:
|
||||
out.printf("symbol at %p", (void*)value().toSymbol());
|
||||
fprintf(fp, "symbol at %p", (void*)value().toSymbol());
|
||||
break;
|
||||
case MIRType_String:
|
||||
out.printf("string %p", (void*)value().toString());
|
||||
fprintf(fp, "string %p", (void*)value().toString());
|
||||
break;
|
||||
case MIRType_MagicOptimizedArguments:
|
||||
out.printf("magic lazyargs");
|
||||
fprintf(fp, "magic lazyargs");
|
||||
break;
|
||||
case MIRType_MagicHole:
|
||||
out.printf("magic hole");
|
||||
fprintf(fp, "magic hole");
|
||||
break;
|
||||
case MIRType_MagicIsConstructing:
|
||||
out.printf("magic is-constructing");
|
||||
fprintf(fp, "magic is-constructing");
|
||||
break;
|
||||
case MIRType_MagicOptimizedOut:
|
||||
out.printf("magic optimized-out");
|
||||
fprintf(fp, "magic optimized-out");
|
||||
break;
|
||||
case MIRType_MagicUninitializedLexical:
|
||||
out.printf("magic uninitialized-lexical");
|
||||
fprintf(fp, "magic uninitialized-lexical");
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("unexpected type");
|
||||
|
@ -1005,43 +1001,43 @@ MSimdGeneralShuffle::foldsTo(TempAllocator& alloc)
|
|||
|
||||
template <typename T>
|
||||
static void
|
||||
PrintOpcodeOperation(T* mir, GenericPrinter& out)
|
||||
PrintOpcodeOperation(T* mir, FILE* fp)
|
||||
{
|
||||
mir->MDefinition::printOpcode(out);
|
||||
out.printf(" (%s)", T::OperationName(mir->operation()));
|
||||
mir->MDefinition::printOpcode(fp);
|
||||
fprintf(fp, " (%s)", T::OperationName(mir->operation()));
|
||||
}
|
||||
|
||||
void
|
||||
MSimdBinaryArith::printOpcode(GenericPrinter& out) const
|
||||
MSimdBinaryArith::printOpcode(FILE* fp) const
|
||||
{
|
||||
PrintOpcodeOperation(this, out);
|
||||
PrintOpcodeOperation(this, fp);
|
||||
}
|
||||
void
|
||||
MSimdBinaryBitwise::printOpcode(GenericPrinter& out) const
|
||||
MSimdBinaryBitwise::printOpcode(FILE* fp) const
|
||||
{
|
||||
PrintOpcodeOperation(this, out);
|
||||
PrintOpcodeOperation(this, fp);
|
||||
}
|
||||
void
|
||||
MSimdUnaryArith::printOpcode(GenericPrinter& out) const
|
||||
MSimdUnaryArith::printOpcode(FILE* fp) const
|
||||
{
|
||||
PrintOpcodeOperation(this, out);
|
||||
PrintOpcodeOperation(this, fp);
|
||||
}
|
||||
void
|
||||
MSimdBinaryComp::printOpcode(GenericPrinter& out) const
|
||||
MSimdBinaryComp::printOpcode(FILE* fp) const
|
||||
{
|
||||
PrintOpcodeOperation(this, out);
|
||||
PrintOpcodeOperation(this, fp);
|
||||
}
|
||||
void
|
||||
MSimdShift::printOpcode(GenericPrinter& out) const
|
||||
MSimdShift::printOpcode(FILE* fp) const
|
||||
{
|
||||
PrintOpcodeOperation(this, out);
|
||||
PrintOpcodeOperation(this, fp);
|
||||
}
|
||||
|
||||
void
|
||||
MSimdInsertElement::printOpcode(GenericPrinter& out) const
|
||||
MSimdInsertElement::printOpcode(FILE* fp) const
|
||||
{
|
||||
MDefinition::printOpcode(out);
|
||||
out.printf(" (%s)", MSimdInsertElement::LaneName(lane()));
|
||||
MDefinition::printOpcode(fp);
|
||||
fprintf(fp, " (%s)", MSimdInsertElement::LaneName(lane()));
|
||||
}
|
||||
|
||||
MCloneLiteral*
|
||||
|
@ -1051,40 +1047,42 @@ MCloneLiteral::New(TempAllocator& alloc, MDefinition* obj)
|
|||
}
|
||||
|
||||
void
|
||||
MControlInstruction::printOpcode(GenericPrinter& out) const
|
||||
MControlInstruction::printOpcode(FILE* fp) const
|
||||
{
|
||||
MDefinition::printOpcode(out);
|
||||
MDefinition::printOpcode(fp);
|
||||
for (size_t j = 0; j < numSuccessors(); j++)
|
||||
out.printf(" block%u", getSuccessor(j)->id());
|
||||
fprintf(fp, " block%u", getSuccessor(j)->id());
|
||||
}
|
||||
|
||||
void
|
||||
MCompare::printOpcode(GenericPrinter& out) const
|
||||
MCompare::printOpcode(FILE* fp) const
|
||||
{
|
||||
MDefinition::printOpcode(out);
|
||||
out.printf(" %s", js_CodeName[jsop()]);
|
||||
MDefinition::printOpcode(fp);
|
||||
fprintf(fp, " %s", js_CodeName[jsop()]);
|
||||
}
|
||||
|
||||
void
|
||||
MConstantElements::printOpcode(GenericPrinter& out) const
|
||||
MConstantElements::printOpcode(FILE* fp) const
|
||||
{
|
||||
PrintOpcodeName(out, op());
|
||||
out.printf(" %p", value());
|
||||
PrintOpcodeName(fp, op());
|
||||
fprintf(fp, " %p", value());
|
||||
}
|
||||
|
||||
void
|
||||
MLoadUnboxedScalar::printOpcode(GenericPrinter& out) const
|
||||
MLoadUnboxedScalar::printOpcode(FILE* fp) const
|
||||
{
|
||||
MDefinition::printOpcode(out);
|
||||
out.printf(" %s", ScalarTypeDescr::typeName(indexType()));
|
||||
MDefinition::printOpcode(fp);
|
||||
fprintf(fp, " %s", ScalarTypeDescr::typeName(indexType()));
|
||||
}
|
||||
|
||||
void
|
||||
MAssertRange::printOpcode(GenericPrinter& out) const
|
||||
MAssertRange::printOpcode(FILE* fp) const
|
||||
{
|
||||
MDefinition::printOpcode(out);
|
||||
out.put(" ");
|
||||
assertedRange()->dump(out);
|
||||
MDefinition::printOpcode(fp);
|
||||
Sprinter sp(GetJitContext()->cx);
|
||||
sp.init();
|
||||
assertedRange()->print(sp);
|
||||
fprintf(fp, " %s", sp.string());
|
||||
}
|
||||
|
||||
const char*
|
||||
|
@ -1121,10 +1119,10 @@ MMathFunction::FunctionName(Function function)
|
|||
}
|
||||
|
||||
void
|
||||
MMathFunction::printOpcode(GenericPrinter& out) const
|
||||
MMathFunction::printOpcode(FILE* fp) const
|
||||
{
|
||||
MDefinition::printOpcode(out);
|
||||
out.printf(" %s", FunctionName(function()));
|
||||
MDefinition::printOpcode(fp);
|
||||
fprintf(fp, " %s", FunctionName(function()));
|
||||
}
|
||||
|
||||
MDefinition*
|
||||
|
@ -1229,13 +1227,13 @@ MParameter::New(TempAllocator& alloc, int32_t index, TemporaryTypeSet* types)
|
|||
}
|
||||
|
||||
void
|
||||
MParameter::printOpcode(GenericPrinter& out) const
|
||||
MParameter::printOpcode(FILE* fp) const
|
||||
{
|
||||
PrintOpcodeName(out, op());
|
||||
PrintOpcodeName(fp, op());
|
||||
if (index() == THIS_SLOT)
|
||||
out.printf(" THIS_SLOT");
|
||||
fprintf(fp, " THIS_SLOT");
|
||||
else
|
||||
out.printf(" %d", index());
|
||||
fprintf(fp, " %d", index());
|
||||
}
|
||||
|
||||
HashNumber
|
||||
|
@ -1487,37 +1485,37 @@ MGoto::New(TempAllocator& alloc, MBasicBlock* target)
|
|||
}
|
||||
|
||||
void
|
||||
MUnbox::printOpcode(GenericPrinter& out) const
|
||||
MUnbox::printOpcode(FILE* fp) const
|
||||
{
|
||||
PrintOpcodeName(out, op());
|
||||
out.printf(" ");
|
||||
getOperand(0)->printName(out);
|
||||
out.printf(" ");
|
||||
PrintOpcodeName(fp, op());
|
||||
fprintf(fp, " ");
|
||||
getOperand(0)->printName(fp);
|
||||
fprintf(fp, " ");
|
||||
|
||||
switch (type()) {
|
||||
case MIRType_Int32: out.printf("to Int32"); break;
|
||||
case MIRType_Double: out.printf("to Double"); break;
|
||||
case MIRType_Boolean: out.printf("to Boolean"); break;
|
||||
case MIRType_String: out.printf("to String"); break;
|
||||
case MIRType_Symbol: out.printf("to Symbol"); break;
|
||||
case MIRType_Object: out.printf("to Object"); break;
|
||||
case MIRType_Int32: fprintf(fp, "to Int32"); break;
|
||||
case MIRType_Double: fprintf(fp, "to Double"); break;
|
||||
case MIRType_Boolean: fprintf(fp, "to Boolean"); break;
|
||||
case MIRType_String: fprintf(fp, "to String"); break;
|
||||
case MIRType_Symbol: fprintf(fp, "to Symbol"); break;
|
||||
case MIRType_Object: fprintf(fp, "to Object"); break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
switch (mode()) {
|
||||
case Fallible: out.printf(" (fallible)"); break;
|
||||
case Infallible: out.printf(" (infallible)"); break;
|
||||
case TypeBarrier: out.printf(" (typebarrier)"); break;
|
||||
case Fallible: fprintf(fp, " (fallible)"); break;
|
||||
case Infallible: fprintf(fp, " (infallible)"); break;
|
||||
case TypeBarrier: fprintf(fp, " (typebarrier)"); break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MTypeBarrier::printOpcode(GenericPrinter& out) const
|
||||
MTypeBarrier::printOpcode(FILE* fp) const
|
||||
{
|
||||
PrintOpcodeName(out, op());
|
||||
out.printf(" ");
|
||||
getOperand(0)->printName(out);
|
||||
PrintOpcodeName(fp, op());
|
||||
fprintf(fp, " ");
|
||||
getOperand(0)->printName(fp);
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -3227,41 +3225,39 @@ MResumePoint::addStore(TempAllocator& alloc, MDefinition* store, const MResumePo
|
|||
}
|
||||
|
||||
void
|
||||
MResumePoint::dump(GenericPrinter& out) const
|
||||
MResumePoint::dump(FILE* fp) const
|
||||
{
|
||||
out.printf("resumepoint mode=");
|
||||
fprintf(fp, "resumepoint mode=");
|
||||
|
||||
switch (mode()) {
|
||||
case MResumePoint::ResumeAt:
|
||||
out.printf("At");
|
||||
fprintf(fp, "At");
|
||||
break;
|
||||
case MResumePoint::ResumeAfter:
|
||||
out.printf("After");
|
||||
fprintf(fp, "After");
|
||||
break;
|
||||
case MResumePoint::Outer:
|
||||
out.printf("Outer");
|
||||
fprintf(fp, "Outer");
|
||||
break;
|
||||
}
|
||||
|
||||
if (MResumePoint* c = caller())
|
||||
out.printf(" (caller in block%u)", c->block()->id());
|
||||
fprintf(fp, " (caller in block%u)", c->block()->id());
|
||||
|
||||
for (size_t i = 0; i < numOperands(); i++) {
|
||||
out.printf(" ");
|
||||
fprintf(fp, " ");
|
||||
if (operands_[i].hasProducer())
|
||||
getOperand(i)->printName(out);
|
||||
getOperand(i)->printName(fp);
|
||||
else
|
||||
out.printf("(null)");
|
||||
fprintf(fp, "(null)");
|
||||
}
|
||||
out.printf("\n");
|
||||
fprintf(fp, "\n");
|
||||
}
|
||||
|
||||
void
|
||||
MResumePoint::dump() const
|
||||
{
|
||||
Fprinter out(stderr);
|
||||
dump(out);
|
||||
out.finish();
|
||||
dump(stderr);
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -3904,12 +3900,18 @@ MNot::trySpecializeFloat32(TempAllocator& alloc)
|
|||
}
|
||||
|
||||
void
|
||||
MBeta::printOpcode(GenericPrinter& out) const
|
||||
MBeta::printOpcode(FILE* fp) const
|
||||
{
|
||||
MDefinition::printOpcode(out);
|
||||
MDefinition::printOpcode(fp);
|
||||
|
||||
out.printf(" ");
|
||||
comparison_->dump(out);
|
||||
if (JitContext* context = MaybeGetJitContext()) {
|
||||
Sprinter sp(context->cx);
|
||||
sp.init();
|
||||
comparison_->print(sp);
|
||||
fprintf(fp, " %s", sp.string());
|
||||
} else {
|
||||
fprintf(fp, " ???");
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -281,7 +281,7 @@ class MNode : public TempObject
|
|||
|
||||
virtual bool writeRecoverData(CompactBufferWriter& writer) const;
|
||||
|
||||
virtual void dump(GenericPrinter& out) const = 0;
|
||||
virtual void dump(FILE* fp) const = 0;
|
||||
virtual void dump() const = 0;
|
||||
|
||||
protected:
|
||||
|
@ -445,12 +445,12 @@ class MDefinition : public MNode
|
|||
virtual const char* opName() const = 0;
|
||||
virtual void accept(MDefinitionVisitor* visitor) = 0;
|
||||
|
||||
void printName(GenericPrinter& out) const;
|
||||
static void PrintOpcodeName(GenericPrinter& out, Opcode op);
|
||||
virtual void printOpcode(GenericPrinter& out) const;
|
||||
void dump(GenericPrinter& out) const override;
|
||||
void printName(FILE* fp) const;
|
||||
static void PrintOpcodeName(FILE* fp, Opcode op);
|
||||
virtual void printOpcode(FILE* fp) const;
|
||||
void dump(FILE* fp) const override;
|
||||
void dump() const override;
|
||||
void dumpLocation(GenericPrinter& out) const;
|
||||
void dumpLocation(FILE* fp) const;
|
||||
void dumpLocation() const;
|
||||
|
||||
// For LICM.
|
||||
|
@ -753,7 +753,9 @@ class MDefinition : public MNode
|
|||
|
||||
void setVirtualRegister(uint32_t vreg) {
|
||||
virtualRegister_ = vreg;
|
||||
#ifdef DEBUG
|
||||
setLoweredUnchecked();
|
||||
#endif
|
||||
}
|
||||
uint32_t virtualRegister() const {
|
||||
MOZ_ASSERT(isLowered());
|
||||
|
@ -1329,7 +1331,7 @@ class MConstant : public MNullaryInstruction
|
|||
return ToBoolean(HandleValue::fromMarkedLocation(&value_));
|
||||
}
|
||||
|
||||
void printOpcode(GenericPrinter& out) const override;
|
||||
void printOpcode(FILE* fp) const override;
|
||||
|
||||
HashNumber valueHash() const override;
|
||||
bool congruentTo(const MDefinition* ins) const override;
|
||||
|
@ -1720,7 +1722,7 @@ class MSimdInsertElement
|
|||
return binaryCongruentTo(ins) && lane_ == ins->toSimdInsertElement()->lane();
|
||||
}
|
||||
|
||||
void printOpcode(GenericPrinter& out) const override;
|
||||
void printOpcode(FILE* fp) const override;
|
||||
|
||||
ALLOW_CLONE(MSimdInsertElement)
|
||||
};
|
||||
|
@ -2046,7 +2048,7 @@ class MSimdUnaryArith
|
|||
return congruentIfOperandsEqual(ins) && ins->toSimdUnaryArith()->operation() == operation();
|
||||
}
|
||||
|
||||
void printOpcode(GenericPrinter& out) const override;
|
||||
void printOpcode(FILE* fp) const override;
|
||||
|
||||
ALLOW_CLONE(MSimdUnaryArith);
|
||||
};
|
||||
|
@ -2133,7 +2135,7 @@ class MSimdBinaryComp
|
|||
operation_ == other->operation();
|
||||
}
|
||||
|
||||
void printOpcode(GenericPrinter& out) const override;
|
||||
void printOpcode(FILE* fp) const override;
|
||||
|
||||
ALLOW_CLONE(MSimdBinaryComp)
|
||||
};
|
||||
|
@ -2202,7 +2204,7 @@ class MSimdBinaryArith
|
|||
return operation_ == ins->toSimdBinaryArith()->operation();
|
||||
}
|
||||
|
||||
void printOpcode(GenericPrinter& out) const override;
|
||||
void printOpcode(FILE* fp) const override;
|
||||
|
||||
ALLOW_CLONE(MSimdBinaryArith)
|
||||
};
|
||||
|
@ -2267,7 +2269,7 @@ class MSimdBinaryBitwise
|
|||
return operation_ == ins->toSimdBinaryBitwise()->operation();
|
||||
}
|
||||
|
||||
void printOpcode(GenericPrinter& out) const override;
|
||||
void printOpcode(FILE* fp) const override;
|
||||
|
||||
ALLOW_CLONE(MSimdBinaryBitwise)
|
||||
};
|
||||
|
@ -2324,7 +2326,7 @@ class MSimdShift
|
|||
MOZ_CRASH("unexpected operation");
|
||||
}
|
||||
|
||||
void printOpcode(GenericPrinter& out) const override;
|
||||
void printOpcode(FILE* fp) const override;
|
||||
|
||||
bool congruentTo(const MDefinition* ins) const override {
|
||||
if (!binaryCongruentTo(ins))
|
||||
|
@ -2427,7 +2429,7 @@ class MParameter : public MNullaryInstruction
|
|||
int32_t index() const {
|
||||
return index_;
|
||||
}
|
||||
void printOpcode(GenericPrinter& out) const override;
|
||||
void printOpcode(FILE* fp) const override;
|
||||
|
||||
HashNumber valueHash() const override;
|
||||
bool congruentTo(const MDefinition* ins) const override;
|
||||
|
@ -2493,7 +2495,7 @@ class MControlInstruction : public MInstruction
|
|||
return true;
|
||||
}
|
||||
|
||||
void printOpcode(GenericPrinter& out) const override;
|
||||
void printOpcode(FILE* fp) const override;
|
||||
};
|
||||
|
||||
class MTableSwitch final
|
||||
|
@ -4263,7 +4265,7 @@ class MCompare
|
|||
return AliasSet::None();
|
||||
}
|
||||
|
||||
void printOpcode(GenericPrinter& out) const override;
|
||||
void printOpcode(FILE* fp) const override;
|
||||
void collectRangeInfoPreTrunc() override;
|
||||
|
||||
void trySpecializeFloat32(TempAllocator& alloc) override;
|
||||
|
@ -4442,7 +4444,7 @@ class MUnbox final : public MUnaryInstruction, public BoxInputsPolicy::Data
|
|||
AliasSet getAliasSet() const override {
|
||||
return AliasSet::None();
|
||||
}
|
||||
void printOpcode(GenericPrinter& out) const override;
|
||||
void printOpcode(FILE* fp) const override;
|
||||
void makeInfallible() {
|
||||
// Should only be called if we're already Infallible or TypeBarrier
|
||||
MOZ_ASSERT(mode() != Fallible);
|
||||
|
@ -4554,7 +4556,7 @@ class MAssertRange
|
|||
return AliasSet::None();
|
||||
}
|
||||
|
||||
void printOpcode(GenericPrinter& out) const override;
|
||||
void printOpcode(FILE* fp) const override;
|
||||
};
|
||||
|
||||
// Caller-side allocation of |this| for |new|:
|
||||
|
@ -6088,7 +6090,7 @@ class MMathFunction
|
|||
|
||||
MDefinition* foldsTo(TempAllocator& alloc) override;
|
||||
|
||||
void printOpcode(GenericPrinter& out) const override;
|
||||
void printOpcode(FILE* fp) const override;
|
||||
|
||||
static const char* FunctionName(Function function);
|
||||
|
||||
|
@ -6908,7 +6910,7 @@ class MBeta
|
|||
|
||||
public:
|
||||
INSTRUCTION_HEADER(Beta)
|
||||
void printOpcode(GenericPrinter& out) const override;
|
||||
void printOpcode(FILE* fp) const override;
|
||||
static MBeta* New(TempAllocator& alloc, MDefinition* val, const Range* comp)
|
||||
{
|
||||
return new(alloc) MBeta(val, comp);
|
||||
|
@ -7659,7 +7661,7 @@ class MConstantElements : public MNullaryInstruction
|
|||
return value_;
|
||||
}
|
||||
|
||||
void printOpcode(GenericPrinter& out) const override;
|
||||
void printOpcode(FILE* fp) const override;
|
||||
|
||||
HashNumber valueHash() const override {
|
||||
return (HashNumber)(size_t) value_;
|
||||
|
@ -9240,7 +9242,7 @@ class MLoadUnboxedScalar
|
|||
return congruentIfOperandsEqual(other);
|
||||
}
|
||||
|
||||
void printOpcode(GenericPrinter& out) const override;
|
||||
void printOpcode(FILE* fp) const override;
|
||||
|
||||
void computeRange(TempAllocator& alloc) override;
|
||||
|
||||
|
@ -12055,7 +12057,7 @@ class MTypeBarrier
|
|||
return new(alloc) MTypeBarrier(def, types, kind);
|
||||
}
|
||||
|
||||
void printOpcode(GenericPrinter& out) const override;
|
||||
void printOpcode(FILE* fp) const override;
|
||||
bool congruentTo(const MDefinition* def) const override;
|
||||
|
||||
AliasSet getAliasSet() const override {
|
||||
|
@ -12441,7 +12443,7 @@ class MResumePoint final :
|
|||
return stores_.end();
|
||||
}
|
||||
|
||||
virtual void dump(GenericPrinter& out) const override;
|
||||
virtual void dump(FILE* fp) const override;
|
||||
virtual void dump() const override;
|
||||
};
|
||||
|
||||
|
|
|
@ -245,14 +245,6 @@ class MIRGenerator
|
|||
}
|
||||
bool needsAsmJSBoundsCheckBranch(const MAsmJSHeapAccess* access) const;
|
||||
size_t foldableOffsetRange(const MAsmJSHeapAccess* access) const;
|
||||
|
||||
private:
|
||||
GraphSpewer gs_;
|
||||
|
||||
public:
|
||||
GraphSpewer& graphSpewer() {
|
||||
return gs_;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace jit
|
||||
|
|
|
@ -48,8 +48,7 @@ MIRGenerator::MIRGenerator(CompileCompartment* compartment, const JitCompileOpti
|
|||
#if defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB)
|
||||
usesSignalHandlersForAsmJSOOB_(usesSignalHandlersForAsmJSOOB),
|
||||
#endif
|
||||
options(options),
|
||||
gs_(alloc)
|
||||
options(options)
|
||||
{ }
|
||||
|
||||
bool
|
||||
|
@ -1517,6 +1516,19 @@ MBasicBlock::specializePhis()
|
|||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
MBasicBlock::dumpStack(FILE* fp)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
fprintf(fp, " %-3s %-16s %-6s %-10s\n", "#", "name", "copyOf", "first/next");
|
||||
fprintf(fp, "-------------------------------------------\n");
|
||||
for (uint32_t i = 0; i < stackPosition_; i++) {
|
||||
fprintf(fp, " %-3d", i);
|
||||
fprintf(fp, " %-16p\n", (void*)slots_[i]);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
MTest*
|
||||
MBasicBlock::immediateDominatorBranch(BranchDirection* pdirection)
|
||||
{
|
||||
|
@ -1546,33 +1558,12 @@ MBasicBlock::immediateDominatorBranch(BranchDirection* pdirection)
|
|||
}
|
||||
|
||||
void
|
||||
MBasicBlock::dumpStack(GenericPrinter& out)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
out.printf(" %-3s %-16s %-6s %-10s\n", "#", "name", "copyOf", "first/next");
|
||||
out.printf("-------------------------------------------\n");
|
||||
for (uint32_t i = 0; i < stackPosition_; i++) {
|
||||
out.printf(" %-3d", i);
|
||||
out.printf(" %-16p\n", (void*)slots_[i]);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
MBasicBlock::dumpStack()
|
||||
{
|
||||
Fprinter out(stderr);
|
||||
dumpStack(out);
|
||||
out.finish();
|
||||
}
|
||||
|
||||
void
|
||||
MIRGraph::dump(GenericPrinter& out)
|
||||
MIRGraph::dump(FILE* fp)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
for (MBasicBlockIterator iter(begin()); iter != end(); iter++) {
|
||||
iter->dump(out);
|
||||
out.printf("\n");
|
||||
iter->dump(fp);
|
||||
fprintf(fp, "\n");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -1580,32 +1571,31 @@ MIRGraph::dump(GenericPrinter& out)
|
|||
void
|
||||
MIRGraph::dump()
|
||||
{
|
||||
Fprinter out(stderr);
|
||||
dump(out);
|
||||
out.finish();
|
||||
dump(stderr);
|
||||
}
|
||||
|
||||
void
|
||||
MBasicBlock::dump(GenericPrinter& out)
|
||||
MBasicBlock::dump(FILE* fp)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
out.printf("block%u:%s%s%s\n", id(),
|
||||
isLoopHeader() ? " (loop header)" : "",
|
||||
unreachable() ? " (unreachable)" : "",
|
||||
isMarked() ? " (marked)" : "");
|
||||
if (MResumePoint* resume = entryResumePoint())
|
||||
resume->dump(out);
|
||||
for (MPhiIterator iter(phisBegin()); iter != phisEnd(); iter++)
|
||||
iter->dump(out);
|
||||
for (MInstructionIterator iter(begin()); iter != end(); iter++)
|
||||
iter->dump(out);
|
||||
fprintf(fp, "block%u:%s%s%s\n", id(),
|
||||
isLoopHeader() ? " (loop header)" : "",
|
||||
unreachable() ? " (unreachable)" : "",
|
||||
isMarked() ? " (marked)" : "");
|
||||
if (MResumePoint* resume = entryResumePoint()) {
|
||||
resume->dump();
|
||||
}
|
||||
for (MPhiIterator iter(phisBegin()); iter != phisEnd(); iter++) {
|
||||
iter->dump(fp);
|
||||
}
|
||||
for (MInstructionIterator iter(begin()); iter != end(); iter++) {
|
||||
iter->dump(fp);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
MBasicBlock::dump()
|
||||
{
|
||||
Fprinter out(stderr);
|
||||
dump(out);
|
||||
out.finish();
|
||||
dump(stderr);
|
||||
}
|
||||
|
|
|
@ -599,10 +599,9 @@ class MBasicBlock : public TempObject, public InlineListNode<MBasicBlock>
|
|||
return info_.script()->strict();
|
||||
}
|
||||
|
||||
void dumpStack(GenericPrinter& out);
|
||||
void dumpStack();
|
||||
void dumpStack(FILE* fp);
|
||||
|
||||
void dump(GenericPrinter& out);
|
||||
void dump(FILE* fp);
|
||||
void dump();
|
||||
|
||||
// Track bailouts by storing the current pc in MIR instruction added at
|
||||
|
@ -810,7 +809,7 @@ class MIRGraph
|
|||
hasTryBlock_ = true;
|
||||
}
|
||||
|
||||
void dump(GenericPrinter& out);
|
||||
void dump(FILE* fp);
|
||||
void dump();
|
||||
};
|
||||
|
||||
|
|
|
@ -115,10 +115,9 @@ SpewRange(MDefinition* def)
|
|||
#ifdef DEBUG
|
||||
if (JitSpewEnabled(JitSpew_Range) && def->type() != MIRType_None && def->range()) {
|
||||
JitSpewHeader(JitSpew_Range);
|
||||
Fprinter& out = JitSpewPrinter();
|
||||
def->printName(out);
|
||||
out.printf(" has range ");
|
||||
def->range()->dump(out);
|
||||
def->printName(JitSpewFile);
|
||||
fprintf(JitSpewFile, " has range ");
|
||||
def->range()->dump(JitSpewFile);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -282,9 +281,8 @@ RangeAnalysis::addBetaNodes()
|
|||
|
||||
if (JitSpewEnabled(JitSpew_Range)) {
|
||||
JitSpewHeader(JitSpew_Range);
|
||||
Fprinter& out = JitSpewPrinter();
|
||||
out.printf("Adding beta node for %d with range ", val->id());
|
||||
comp.dump(out);
|
||||
fprintf(JitSpewFile, "Adding beta node for %d with range ", val->id());
|
||||
comp.dump(JitSpewFile);
|
||||
}
|
||||
|
||||
MBeta* beta = MBeta::New(alloc(), val, new(alloc()) Range(comp));
|
||||
|
@ -322,20 +320,20 @@ RangeAnalysis::removeBetaNodes()
|
|||
}
|
||||
|
||||
void
|
||||
SymbolicBound::dump(GenericPrinter& out) const
|
||||
SymbolicBound::print(Sprinter& sp) const
|
||||
{
|
||||
if (loop)
|
||||
out.printf("[loop] ");
|
||||
sum.dump(out);
|
||||
sp.printf("[loop] ");
|
||||
sum.print(sp);
|
||||
}
|
||||
|
||||
void
|
||||
SymbolicBound::dump() const
|
||||
{
|
||||
Fprinter out(stderr);
|
||||
dump(out);
|
||||
out.printf("\n");
|
||||
out.finish();
|
||||
Sprinter sp(GetJitContext()->cx);
|
||||
sp.init();
|
||||
print(sp);
|
||||
fprintf(stderr, "%s\n", sp.string());
|
||||
}
|
||||
|
||||
// Test whether the given range's exponent tells us anything that its lower
|
||||
|
@ -358,41 +356,41 @@ IsExponentInteresting(const Range* r)
|
|||
}
|
||||
|
||||
void
|
||||
Range::dump(GenericPrinter& out) const
|
||||
Range::print(Sprinter& sp) const
|
||||
{
|
||||
assertInvariants();
|
||||
|
||||
// Floating-point or Integer subset.
|
||||
if (canHaveFractionalPart_)
|
||||
out.printf("F");
|
||||
sp.printf("F");
|
||||
else
|
||||
out.printf("I");
|
||||
sp.printf("I");
|
||||
|
||||
out.printf("[");
|
||||
sp.printf("[");
|
||||
|
||||
if (!hasInt32LowerBound_)
|
||||
out.printf("?");
|
||||
sp.printf("?");
|
||||
else
|
||||
out.printf("%d", lower_);
|
||||
sp.printf("%d", lower_);
|
||||
if (symbolicLower_) {
|
||||
out.printf(" {");
|
||||
symbolicLower_->dump(out);
|
||||
out.printf("}");
|
||||
sp.printf(" {");
|
||||
symbolicLower_->print(sp);
|
||||
sp.printf("}");
|
||||
}
|
||||
|
||||
out.printf(", ");
|
||||
sp.printf(", ");
|
||||
|
||||
if (!hasInt32UpperBound_)
|
||||
out.printf("?");
|
||||
sp.printf("?");
|
||||
else
|
||||
out.printf("%d", upper_);
|
||||
sp.printf("%d", upper_);
|
||||
if (symbolicUpper_) {
|
||||
out.printf(" {");
|
||||
symbolicUpper_->dump(out);
|
||||
out.printf("}");
|
||||
sp.printf(" {");
|
||||
symbolicUpper_->print(sp);
|
||||
sp.printf("}");
|
||||
}
|
||||
|
||||
out.printf("]");
|
||||
sp.printf("]");
|
||||
|
||||
bool includesNaN = max_exponent_ == IncludesInfinityAndNaN;
|
||||
bool includesNegativeInfinity = max_exponent_ >= IncludesInfinity && !hasInt32LowerBound_;
|
||||
|
@ -404,49 +402,55 @@ Range::dump(GenericPrinter& out) const
|
|||
includesPositiveInfinity ||
|
||||
includesNegativeZero)
|
||||
{
|
||||
out.printf(" (");
|
||||
sp.printf(" (");
|
||||
bool first = true;
|
||||
if (includesNaN) {
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
out.printf(" ");
|
||||
out.printf("U NaN");
|
||||
sp.printf(" ");
|
||||
sp.printf("U NaN");
|
||||
}
|
||||
if (includesNegativeInfinity) {
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
out.printf(" ");
|
||||
out.printf("U -Infinity");
|
||||
sp.printf(" ");
|
||||
sp.printf("U -Infinity");
|
||||
}
|
||||
if (includesPositiveInfinity) {
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
out.printf(" ");
|
||||
out.printf("U Infinity");
|
||||
sp.printf(" ");
|
||||
sp.printf("U Infinity");
|
||||
}
|
||||
if (includesNegativeZero) {
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
out.printf(" ");
|
||||
out.printf("U -0");
|
||||
sp.printf(" ");
|
||||
sp.printf("U -0");
|
||||
}
|
||||
out.printf(")");
|
||||
sp.printf(")");
|
||||
}
|
||||
if (max_exponent_ < IncludesInfinity && IsExponentInteresting(this))
|
||||
out.printf(" (< pow(2, %d+1))", max_exponent_);
|
||||
sp.printf(" (< pow(2, %d+1))", max_exponent_);
|
||||
}
|
||||
|
||||
void
|
||||
Range::dump(FILE* fp) const
|
||||
{
|
||||
Sprinter sp(GetJitContext()->cx);
|
||||
sp.init();
|
||||
print(sp);
|
||||
fprintf(fp, "%s\n", sp.string());
|
||||
}
|
||||
|
||||
void
|
||||
Range::dump() const
|
||||
{
|
||||
Fprinter out(stderr);
|
||||
dump(out);
|
||||
out.printf("\n");
|
||||
out.finish();
|
||||
dump(stderr);
|
||||
}
|
||||
|
||||
Range*
|
||||
|
@ -1867,7 +1871,7 @@ RangeAnalysis::analyzeLoop(MBasicBlock* header)
|
|||
if (JitSpewEnabled(JitSpew_Range)) {
|
||||
Sprinter sp(GetJitContext()->cx);
|
||||
sp.init();
|
||||
iterationBound->boundSum.dump(sp);
|
||||
iterationBound->boundSum.print(sp);
|
||||
JitSpew(JitSpew_Range, "computed symbolic bound on backedges: %s",
|
||||
sp.string());
|
||||
}
|
||||
|
|
|
@ -81,7 +81,7 @@ struct SymbolicBound : public TempObject
|
|||
// Computed symbolic bound, see above.
|
||||
LinearSum sum;
|
||||
|
||||
void dump(GenericPrinter& out) const;
|
||||
void print(Sprinter& sp) const;
|
||||
void dump() const;
|
||||
};
|
||||
|
||||
|
@ -450,7 +450,8 @@ class Range : public TempObject {
|
|||
return r;
|
||||
}
|
||||
|
||||
void dump(GenericPrinter& out) const;
|
||||
void print(Sprinter& sp) const;
|
||||
void dump(FILE* fp) const;
|
||||
void dump() const;
|
||||
bool update(const Range* other);
|
||||
|
||||
|
|
|
@ -205,13 +205,12 @@ SafepointWriter::writeValueSlots(LSafepoint* safepoint)
|
|||
static void
|
||||
DumpNunboxPart(const LAllocation& a)
|
||||
{
|
||||
Fprinter& out = JitSpewPrinter();
|
||||
if (a.isStackSlot()) {
|
||||
out.printf("stack %d", a.toStackSlot()->slot());
|
||||
fprintf(JitSpewFile, "stack %d", a.toStackSlot()->slot());
|
||||
} else if (a.isArgument()) {
|
||||
out.printf("arg %d", a.toArgument()->index());
|
||||
fprintf(JitSpewFile, "arg %d", a.toArgument()->index());
|
||||
} else {
|
||||
out.printf("reg %s", a.toGeneralReg()->reg().name());
|
||||
fprintf(JitSpewFile, "reg %s", a.toGeneralReg()->reg().name());
|
||||
}
|
||||
}
|
||||
#endif // DEBUG
|
||||
|
@ -296,12 +295,11 @@ SafepointWriter::writeNunboxParts(LSafepoint* safepoint)
|
|||
if (entry.type.isUse() || entry.payload.isUse())
|
||||
continue;
|
||||
JitSpewHeader(JitSpew_Safepoints);
|
||||
Fprinter& out = JitSpewPrinter();
|
||||
out.printf(" nunbox (type in ");
|
||||
fprintf(JitSpewFile, " nunbox (type in ");
|
||||
DumpNunboxPart(entry.type);
|
||||
out.printf(", payload in ");
|
||||
fprintf(JitSpewFile, ", payload in ");
|
||||
DumpNunboxPart(entry.payload);
|
||||
out.printf(")\n");
|
||||
fprintf(JitSpewFile, ")\n");
|
||||
}
|
||||
}
|
||||
# endif
|
||||
|
|
|
@ -16,8 +16,6 @@
|
|||
#include "jit/MIR.h"
|
||||
#include "jit/Recover.h"
|
||||
|
||||
#include "vm/Printer.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace js::jit;
|
||||
|
||||
|
@ -421,43 +419,43 @@ ValTypeToString(JSValueType type)
|
|||
}
|
||||
|
||||
void
|
||||
RValueAllocation::dumpPayload(GenericPrinter& out, PayloadType type, Payload p)
|
||||
RValueAllocation::dumpPayload(FILE* fp, PayloadType type, Payload p)
|
||||
{
|
||||
switch (type) {
|
||||
case PAYLOAD_NONE:
|
||||
break;
|
||||
case PAYLOAD_INDEX:
|
||||
out.printf("index %u", p.index);
|
||||
fprintf(fp, "index %u", p.index);
|
||||
break;
|
||||
case PAYLOAD_STACK_OFFSET:
|
||||
out.printf("stack %d", p.stackOffset);
|
||||
fprintf(fp, "stack %d", p.stackOffset);
|
||||
break;
|
||||
case PAYLOAD_GPR:
|
||||
out.printf("reg %s", p.gpr.name());
|
||||
fprintf(fp, "reg %s", p.gpr.name());
|
||||
break;
|
||||
case PAYLOAD_FPU:
|
||||
out.printf("reg %s", p.fpu.name());
|
||||
fprintf(fp, "reg %s", p.fpu.name());
|
||||
break;
|
||||
case PAYLOAD_PACKED_TAG:
|
||||
out.printf("%s", ValTypeToString(p.type));
|
||||
fprintf(fp, "%s", ValTypeToString(p.type));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
RValueAllocation::dump(GenericPrinter& out) const
|
||||
RValueAllocation::dump(FILE* fp) const
|
||||
{
|
||||
const Layout& layout = layoutFromMode(mode());
|
||||
out.printf("%s", layout.name);
|
||||
fprintf(fp, "%s", layout.name);
|
||||
|
||||
if (layout.type1 != PAYLOAD_NONE)
|
||||
out.printf(" (");
|
||||
dumpPayload(out, layout.type1, arg1_);
|
||||
fprintf(fp, " (");
|
||||
dumpPayload(fp, layout.type1, arg1_);
|
||||
if (layout.type2 != PAYLOAD_NONE)
|
||||
out.printf(", ");
|
||||
dumpPayload(out, layout.type2, arg2_);
|
||||
fprintf(fp, ", ");
|
||||
dumpPayload(fp, layout.type2, arg2_);
|
||||
if (layout.type1 != PAYLOAD_NONE)
|
||||
out.printf(")");
|
||||
fprintf(fp, ")");
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -550,13 +548,12 @@ SnapshotReader::spewBailingFrom() const
|
|||
{
|
||||
if (JitSpewEnabled(JitSpew_IonBailouts)) {
|
||||
JitSpewHeader(JitSpew_IonBailouts);
|
||||
Fprinter& out = JitSpewPrinter();
|
||||
out.printf(" bailing from bytecode: %s, MIR: ", js_CodeName[pcOpcode_]);
|
||||
MDefinition::PrintOpcodeName(out, MDefinition::Opcode(mirOpcode_));
|
||||
out.printf(" [%u], LIR: ", mirId_);
|
||||
LInstruction::printName(out, LInstruction::Opcode(lirOpcode_));
|
||||
out.printf(" [%u]", lirId_);
|
||||
out.printf("\n");
|
||||
fprintf(JitSpewFile, " bailing from bytecode: %s, MIR: ", js_CodeName[pcOpcode_]);
|
||||
MDefinition::PrintOpcodeName(JitSpewFile, MDefinition::Opcode(mirOpcode_));
|
||||
fprintf(JitSpewFile, " [%u], LIR: ", mirId_);
|
||||
LInstruction::printName(JitSpewFile, LInstruction::Opcode(lirOpcode_));
|
||||
fprintf(JitSpewFile, " [%u]", lirId_);
|
||||
fprintf(JitSpewFile, "\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -669,10 +666,9 @@ SnapshotWriter::add(const RValueAllocation& alloc)
|
|||
|
||||
if (JitSpewEnabled(JitSpew_IonSnapshots)) {
|
||||
JitSpewHeader(JitSpew_IonSnapshots);
|
||||
Fprinter& out = JitSpewPrinter();
|
||||
out.printf(" slot %u (%d): ", allocWritten_, offset);
|
||||
alloc.dump(out);
|
||||
out.printf("\n");
|
||||
fprintf(JitSpewFile, " slot %u (%d): ", allocWritten_, offset);
|
||||
alloc.dump(JitSpewFile);
|
||||
fprintf(JitSpewFile, "\n");
|
||||
}
|
||||
|
||||
allocWritten_++;
|
||||
|
|
|
@ -19,8 +19,6 @@
|
|||
#include "js/HashTable.h"
|
||||
|
||||
namespace js {
|
||||
class GenericPrinter;
|
||||
|
||||
namespace jit {
|
||||
|
||||
class RValueAllocation;
|
||||
|
@ -167,7 +165,7 @@ class RValueAllocation
|
|||
static void writePayload(CompactBufferWriter& writer, PayloadType t,
|
||||
Payload p);
|
||||
static void writePadding(CompactBufferWriter& writer);
|
||||
static void dumpPayload(GenericPrinter& out, PayloadType t, Payload p);
|
||||
static void dumpPayload(FILE* fp, PayloadType t, Payload p);
|
||||
static bool equalPayloads(PayloadType t, Payload lhs, Payload rhs);
|
||||
|
||||
RValueAllocation(Mode mode, Payload a1, Payload a2)
|
||||
|
@ -336,7 +334,7 @@ class RValueAllocation
|
|||
}
|
||||
|
||||
public:
|
||||
void dump(GenericPrinter& out) const;
|
||||
void dump(FILE* fp) const;
|
||||
|
||||
public:
|
||||
bool operator==(const RValueAllocation& rhs) const {
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "mozilla/SizePrintfMacros.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
|
@ -824,6 +825,9 @@ js::DumpScript(JSContext* cx, JSScript* scriptArg)
|
|||
return ok;
|
||||
}
|
||||
|
||||
static char*
|
||||
QuoteString(Sprinter* sp, JSString* str, char16_t quote);
|
||||
|
||||
static bool
|
||||
ToDisassemblySource(JSContext* cx, HandleValue v, JSAutoByteString* bytes)
|
||||
{
|
||||
|
@ -1083,6 +1087,336 @@ js::Disassemble1(JSContext* cx, HandleScript script, jsbytecode* pc,
|
|||
|
||||
#endif /* DEBUG */
|
||||
|
||||
/************************************************************************/
|
||||
|
||||
const size_t Sprinter::DefaultSize = 64;
|
||||
|
||||
bool
|
||||
Sprinter::realloc_(size_t newSize)
|
||||
{
|
||||
MOZ_ASSERT(newSize > (size_t) offset);
|
||||
char* newBuf = (char*) js_realloc(base, newSize);
|
||||
if (!newBuf) {
|
||||
reportOutOfMemory();
|
||||
return false;
|
||||
}
|
||||
base = newBuf;
|
||||
size = newSize;
|
||||
base[size - 1] = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
Sprinter::Sprinter(ExclusiveContext* cx)
|
||||
: context(cx),
|
||||
#ifdef DEBUG
|
||||
initialized(false),
|
||||
#endif
|
||||
base(nullptr), size(0), offset(0), reportedOOM(false)
|
||||
{ }
|
||||
|
||||
Sprinter::~Sprinter()
|
||||
{
|
||||
#ifdef DEBUG
|
||||
if (initialized)
|
||||
checkInvariants();
|
||||
#endif
|
||||
js_free(base);
|
||||
}
|
||||
|
||||
bool
|
||||
Sprinter::init()
|
||||
{
|
||||
MOZ_ASSERT(!initialized);
|
||||
base = (char*) js_malloc(DefaultSize);
|
||||
if (!base) {
|
||||
reportOutOfMemory();
|
||||
return false;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
initialized = true;
|
||||
#endif
|
||||
*base = 0;
|
||||
size = DefaultSize;
|
||||
base[size - 1] = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
Sprinter::checkInvariants() const
|
||||
{
|
||||
MOZ_ASSERT(initialized);
|
||||
MOZ_ASSERT((size_t) offset < size);
|
||||
MOZ_ASSERT(base[size - 1] == 0);
|
||||
}
|
||||
|
||||
const char*
|
||||
Sprinter::string() const
|
||||
{
|
||||
return base;
|
||||
}
|
||||
|
||||
const char*
|
||||
Sprinter::stringEnd() const
|
||||
{
|
||||
return base + offset;
|
||||
}
|
||||
|
||||
char*
|
||||
Sprinter::stringAt(ptrdiff_t off) const
|
||||
{
|
||||
MOZ_ASSERT(off >= 0 && (size_t) off < size);
|
||||
return base + off;
|
||||
}
|
||||
|
||||
char&
|
||||
Sprinter::operator[](size_t off)
|
||||
{
|
||||
MOZ_ASSERT(off < size);
|
||||
return *(base + off);
|
||||
}
|
||||
|
||||
char*
|
||||
Sprinter::reserve(size_t len)
|
||||
{
|
||||
InvariantChecker ic(this);
|
||||
|
||||
while (len + 1 > size - offset) { /* Include trailing \0 */
|
||||
if (!realloc_(size * 2))
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
char* sb = base + offset;
|
||||
offset += len;
|
||||
return sb;
|
||||
}
|
||||
|
||||
ptrdiff_t
|
||||
Sprinter::put(const char* s, size_t len)
|
||||
{
|
||||
InvariantChecker ic(this);
|
||||
|
||||
const char* oldBase = base;
|
||||
const char* oldEnd = base + size;
|
||||
|
||||
ptrdiff_t oldOffset = offset;
|
||||
char* bp = reserve(len);
|
||||
if (!bp)
|
||||
return -1;
|
||||
|
||||
/* s is within the buffer already */
|
||||
if (s >= oldBase && s < oldEnd) {
|
||||
/* buffer was realloc'ed */
|
||||
if (base != oldBase)
|
||||
s = stringAt(s - oldBase); /* this is where it lives now */
|
||||
memmove(bp, s, len);
|
||||
} else {
|
||||
js_memcpy(bp, s, len);
|
||||
}
|
||||
|
||||
bp[len] = 0;
|
||||
return oldOffset;
|
||||
}
|
||||
|
||||
ptrdiff_t
|
||||
Sprinter::put(const char* s)
|
||||
{
|
||||
return put(s, strlen(s));
|
||||
}
|
||||
|
||||
ptrdiff_t
|
||||
Sprinter::putString(JSString* s)
|
||||
{
|
||||
InvariantChecker ic(this);
|
||||
|
||||
size_t length = s->length();
|
||||
size_t size = length;
|
||||
|
||||
ptrdiff_t oldOffset = offset;
|
||||
char* buffer = reserve(size);
|
||||
if (!buffer)
|
||||
return -1;
|
||||
|
||||
JSLinearString* linear = s->ensureLinear(context);
|
||||
if (!linear)
|
||||
return -1;
|
||||
|
||||
AutoCheckCannotGC nogc;
|
||||
if (linear->hasLatin1Chars())
|
||||
mozilla::PodCopy(reinterpret_cast<Latin1Char*>(buffer), linear->latin1Chars(nogc), length);
|
||||
else
|
||||
DeflateStringToBuffer(nullptr, linear->twoByteChars(nogc), length, buffer, &size);
|
||||
|
||||
buffer[size] = 0;
|
||||
return oldOffset;
|
||||
}
|
||||
|
||||
int
|
||||
Sprinter::printf(const char* fmt, ...)
|
||||
{
|
||||
InvariantChecker ic(this);
|
||||
|
||||
do {
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
int i = vsnprintf(base + offset, size - offset, fmt, va);
|
||||
va_end(va);
|
||||
|
||||
if (i > -1 && (size_t) i < size - offset) {
|
||||
offset += i;
|
||||
return i;
|
||||
}
|
||||
} while (realloc_(size * 2));
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
ptrdiff_t
|
||||
Sprinter::getOffset() const
|
||||
{
|
||||
return offset;
|
||||
}
|
||||
|
||||
void
|
||||
Sprinter::reportOutOfMemory()
|
||||
{
|
||||
if (reportedOOM)
|
||||
return;
|
||||
if (context)
|
||||
ReportOutOfMemory(context);
|
||||
reportedOOM = true;
|
||||
}
|
||||
|
||||
bool
|
||||
Sprinter::hadOutOfMemory() const
|
||||
{
|
||||
return reportedOOM;
|
||||
}
|
||||
|
||||
ptrdiff_t
|
||||
js::Sprint(Sprinter* sp, const char* format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
char* bp;
|
||||
ptrdiff_t offset;
|
||||
|
||||
va_start(ap, format);
|
||||
bp = JS_vsmprintf(format, ap); /* XXX vsaprintf */
|
||||
va_end(ap);
|
||||
if (!bp) {
|
||||
sp->reportOutOfMemory();
|
||||
return -1;
|
||||
}
|
||||
offset = sp->put(bp);
|
||||
js_free(bp);
|
||||
return offset;
|
||||
}
|
||||
|
||||
const char js_EscapeMap[] = {
|
||||
'\b', 'b',
|
||||
'\f', 'f',
|
||||
'\n', 'n',
|
||||
'\r', 'r',
|
||||
'\t', 't',
|
||||
'\v', 'v',
|
||||
'"', '"',
|
||||
'\'', '\'',
|
||||
'\\', '\\',
|
||||
'\0'
|
||||
};
|
||||
|
||||
template <typename CharT>
|
||||
static char*
|
||||
QuoteString(Sprinter* sp, const CharT* s, size_t length, char16_t quote)
|
||||
{
|
||||
/* Sample off first for later return value pointer computation. */
|
||||
ptrdiff_t offset = sp->getOffset();
|
||||
|
||||
if (quote && Sprint(sp, "%c", char(quote)) < 0)
|
||||
return nullptr;
|
||||
|
||||
const CharT* end = s + length;
|
||||
|
||||
/* Loop control variables: end points at end of string sentinel. */
|
||||
for (const CharT* t = s; t < end; s = ++t) {
|
||||
/* Move t forward from s past un-quote-worthy characters. */
|
||||
char16_t c = *t;
|
||||
while (c < 127 && isprint(c) && c != quote && c != '\\' && c != '\t') {
|
||||
c = *++t;
|
||||
if (t == end)
|
||||
break;
|
||||
}
|
||||
|
||||
{
|
||||
ptrdiff_t len = t - s;
|
||||
ptrdiff_t base = sp->getOffset();
|
||||
if (!sp->reserve(len))
|
||||
return nullptr;
|
||||
|
||||
for (ptrdiff_t i = 0; i < len; ++i)
|
||||
(*sp)[base + i] = char(*s++);
|
||||
(*sp)[base + len] = 0;
|
||||
}
|
||||
|
||||
if (t == end)
|
||||
break;
|
||||
|
||||
/* Use js_EscapeMap, \u, or \x only if necessary. */
|
||||
const char* escape;
|
||||
if (!(c >> 8) && c != 0 && (escape = strchr(js_EscapeMap, int(c))) != nullptr) {
|
||||
if (Sprint(sp, "\\%c", escape[1]) < 0)
|
||||
return nullptr;
|
||||
} else {
|
||||
/*
|
||||
* Use \x only if the high byte is 0 and we're in a quoted string,
|
||||
* because ECMA-262 allows only \u, not \x, in Unicode identifiers
|
||||
* (see bug 621814).
|
||||
*/
|
||||
if (Sprint(sp, (quote && !(c >> 8)) ? "\\x%02X" : "\\u%04X", c) < 0)
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/* Sprint the closing quote and return the quoted string. */
|
||||
if (quote && Sprint(sp, "%c", char(quote)) < 0)
|
||||
return nullptr;
|
||||
|
||||
/*
|
||||
* If we haven't Sprint'd anything yet, Sprint an empty string so that
|
||||
* the return below gives a valid result.
|
||||
*/
|
||||
if (offset == sp->getOffset() && Sprint(sp, "") < 0)
|
||||
return nullptr;
|
||||
|
||||
return sp->stringAt(offset);
|
||||
}
|
||||
|
||||
static char*
|
||||
QuoteString(Sprinter* sp, JSString* str, char16_t quote)
|
||||
{
|
||||
JSLinearString* linear = str->ensureLinear(sp->context);
|
||||
if (!linear)
|
||||
return nullptr;
|
||||
|
||||
AutoCheckCannotGC nogc;
|
||||
return linear->hasLatin1Chars()
|
||||
? QuoteString(sp, linear->latin1Chars(nogc), linear->length(), quote)
|
||||
: QuoteString(sp, linear->twoByteChars(nogc), linear->length(), quote);
|
||||
}
|
||||
|
||||
JSString*
|
||||
js::QuoteString(ExclusiveContext* cx, JSString* str, char16_t quote)
|
||||
{
|
||||
Sprinter sprinter(cx);
|
||||
if (!sprinter.init())
|
||||
return nullptr;
|
||||
char* bytes = QuoteString(&sprinter, str, quote);
|
||||
if (!bytes)
|
||||
return nullptr;
|
||||
return NewStringCopyZ<CanGC>(cx, bytes);
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
|
||||
namespace {
|
||||
/*
|
||||
* The expression decompiler is invoked by error handling code to produce a
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
|
||||
#include "frontend/SourceNotes.h"
|
||||
#include "vm/Opcodes.h"
|
||||
#include "vm/Printer.h"
|
||||
|
||||
/*
|
||||
* JS operation bytecodes.
|
||||
|
@ -383,6 +382,7 @@ struct JSCodeSpec {
|
|||
extern const JSCodeSpec js_CodeSpec[];
|
||||
extern const unsigned js_NumCodeSpecs;
|
||||
extern const char * const js_CodeName[];
|
||||
extern const char js_EscapeMap[];
|
||||
|
||||
/* Shorthand for type from opcode. */
|
||||
|
||||
|
@ -400,6 +400,14 @@ JOF_OPTYPE(JSOp op)
|
|||
|
||||
namespace js {
|
||||
|
||||
/*
|
||||
* Return a GC'ed string containing the chars in str, with any non-printing
|
||||
* chars or quotes (' or " as specified by the quote argument) escaped, and
|
||||
* with the quote character at the beginning and end of the result string.
|
||||
*/
|
||||
extern JSString*
|
||||
QuoteString(ExclusiveContext* cx, JSString* str, char16_t quote);
|
||||
|
||||
static inline bool
|
||||
IsJumpOpcode(JSOp op)
|
||||
{
|
||||
|
@ -569,6 +577,89 @@ DecompileValueGenerator(JSContext* cx, int spindex, HandleValue v,
|
|||
char*
|
||||
DecompileArgument(JSContext* cx, int formalIndex, HandleValue v);
|
||||
|
||||
/*
|
||||
* Sprintf, but with unlimited and automatically allocated buffering.
|
||||
*/
|
||||
class Sprinter
|
||||
{
|
||||
public:
|
||||
struct InvariantChecker
|
||||
{
|
||||
const Sprinter* parent;
|
||||
|
||||
explicit InvariantChecker(const Sprinter* p) : parent(p) {
|
||||
parent->checkInvariants();
|
||||
}
|
||||
|
||||
~InvariantChecker() {
|
||||
parent->checkInvariants();
|
||||
}
|
||||
};
|
||||
|
||||
ExclusiveContext* context; /* context executing the decompiler */
|
||||
|
||||
private:
|
||||
static const size_t DefaultSize;
|
||||
#ifdef DEBUG
|
||||
bool initialized; /* true if this is initialized, use for debug builds */
|
||||
#endif
|
||||
char* base; /* malloc'd buffer address */
|
||||
size_t size; /* size of buffer allocated at base */
|
||||
ptrdiff_t offset; /* offset of next free char in buffer */
|
||||
bool reportedOOM; /* this sprinter has reported OOM in string ops */
|
||||
|
||||
bool realloc_(size_t newSize);
|
||||
|
||||
public:
|
||||
explicit Sprinter(ExclusiveContext* cx);
|
||||
~Sprinter();
|
||||
|
||||
/* Initialize this sprinter, returns false on error */
|
||||
bool init();
|
||||
|
||||
void checkInvariants() const;
|
||||
|
||||
const char* string() const;
|
||||
const char* stringEnd() const;
|
||||
/* Returns the string at offset |off| */
|
||||
char* stringAt(ptrdiff_t off) const;
|
||||
/* Returns the char at offset |off| */
|
||||
char& operator[](size_t off);
|
||||
|
||||
/*
|
||||
* Attempt to reserve len + 1 space (for a trailing nullptr byte). If the
|
||||
* attempt succeeds, return a pointer to the start of that space and adjust the
|
||||
* internal content. The caller *must* completely fill this space on success.
|
||||
*/
|
||||
char* reserve(size_t len);
|
||||
|
||||
/*
|
||||
* Puts |len| characters from |s| at the current position and return an offset to
|
||||
* the beginning of this new data
|
||||
*/
|
||||
ptrdiff_t put(const char* s, size_t len);
|
||||
ptrdiff_t put(const char* s);
|
||||
ptrdiff_t putString(JSString* str);
|
||||
|
||||
/* Prints a formatted string into the buffer */
|
||||
int printf(const char* fmt, ...);
|
||||
|
||||
ptrdiff_t getOffset() const;
|
||||
|
||||
/*
|
||||
* Report that a string operation failed to get the memory it requested. The
|
||||
* first call to this function calls JS_ReportOutOfMemory, and sets this
|
||||
* Sprinter's outOfMemory flag; subsequent calls do nothing.
|
||||
*/
|
||||
void reportOutOfMemory();
|
||||
|
||||
/* Return true if this Sprinter ran out of memory. */
|
||||
bool hadOutOfMemory() const;
|
||||
};
|
||||
|
||||
extern ptrdiff_t
|
||||
Sprint(Sprinter* sp, const char* format, ...);
|
||||
|
||||
extern bool
|
||||
CallResultEscapes(jsbytecode* pc);
|
||||
|
||||
|
|
|
@ -1373,6 +1373,7 @@ class JSScript : public js::gc::TenuredCell
|
|||
if (hasIonScript())
|
||||
js::jit::IonScript::writeBarrierPre(zone(), ion);
|
||||
ion = ionScript;
|
||||
resetWarmUpResetCounter();
|
||||
MOZ_ASSERT_IF(hasIonScript(), hasBaselineScript());
|
||||
updateBaselineOrIonRaw(maybecx);
|
||||
}
|
||||
|
|
|
@ -39,7 +39,6 @@
|
|||
#include "vm/GlobalObject.h"
|
||||
#include "vm/Interpreter.h"
|
||||
#include "vm/Opcodes.h"
|
||||
#include "vm/Printer.h"
|
||||
#include "vm/RegExpObject.h"
|
||||
#include "vm/RegExpStatics.h"
|
||||
#include "vm/ScopeObject.h"
|
||||
|
@ -5040,19 +5039,19 @@ js::OneUcs4ToUtf8Char(uint8_t* utf8Buffer, uint32_t ucs4Char)
|
|||
}
|
||||
|
||||
size_t
|
||||
js::PutEscapedStringImpl(char* buffer, size_t bufferSize, GenericPrinter* out, JSLinearString* str,
|
||||
js::PutEscapedStringImpl(char* buffer, size_t bufferSize, FILE* fp, JSLinearString* str,
|
||||
uint32_t quote)
|
||||
{
|
||||
size_t len = str->length();
|
||||
AutoCheckCannotGC nogc;
|
||||
return str->hasLatin1Chars()
|
||||
? PutEscapedStringImpl(buffer, bufferSize, out, str->latin1Chars(nogc), len, quote)
|
||||
: PutEscapedStringImpl(buffer, bufferSize, out, str->twoByteChars(nogc), len, quote);
|
||||
? PutEscapedStringImpl(buffer, bufferSize, fp, str->latin1Chars(nogc), len, quote)
|
||||
: PutEscapedStringImpl(buffer, bufferSize, fp, str->twoByteChars(nogc), len, quote);
|
||||
}
|
||||
|
||||
template <typename CharT>
|
||||
size_t
|
||||
js::PutEscapedStringImpl(char* buffer, size_t bufferSize, GenericPrinter* out, const CharT* chars,
|
||||
js::PutEscapedStringImpl(char* buffer, size_t bufferSize, FILE* fp, const CharT* chars,
|
||||
size_t length, uint32_t quote)
|
||||
{
|
||||
enum {
|
||||
|
@ -5061,7 +5060,7 @@ js::PutEscapedStringImpl(char* buffer, size_t bufferSize, GenericPrinter* out, c
|
|||
|
||||
MOZ_ASSERT(quote == 0 || quote == '\'' || quote == '"');
|
||||
MOZ_ASSERT_IF(!buffer, bufferSize == 0);
|
||||
MOZ_ASSERT_IF(out, !buffer);
|
||||
MOZ_ASSERT_IF(fp, !buffer);
|
||||
|
||||
if (bufferSize == 0)
|
||||
buffer = nullptr;
|
||||
|
@ -5150,8 +5149,8 @@ js::PutEscapedStringImpl(char* buffer, size_t bufferSize, GenericPrinter* out, c
|
|||
buffer[n] = '\0';
|
||||
buffer = nullptr;
|
||||
}
|
||||
} else if (out) {
|
||||
if (out->put(&c, 1) < 0)
|
||||
} else if (fp) {
|
||||
if (fputc(c, fp) < 0)
|
||||
return size_t(-1);
|
||||
}
|
||||
n++;
|
||||
|
@ -5163,15 +5162,15 @@ js::PutEscapedStringImpl(char* buffer, size_t bufferSize, GenericPrinter* out, c
|
|||
}
|
||||
|
||||
template size_t
|
||||
js::PutEscapedStringImpl(char* buffer, size_t bufferSize, GenericPrinter* out, const Latin1Char* chars,
|
||||
js::PutEscapedStringImpl(char* buffer, size_t bufferSize, FILE* fp, const Latin1Char* chars,
|
||||
size_t length, uint32_t quote);
|
||||
|
||||
template size_t
|
||||
js::PutEscapedStringImpl(char* buffer, size_t bufferSize, GenericPrinter* out, const char* chars,
|
||||
js::PutEscapedStringImpl(char* buffer, size_t bufferSize, FILE* fp, const char* chars,
|
||||
size_t length, uint32_t quote);
|
||||
|
||||
template size_t
|
||||
js::PutEscapedStringImpl(char* buffer, size_t bufferSize, GenericPrinter* out, const char16_t* chars,
|
||||
js::PutEscapedStringImpl(char* buffer, size_t bufferSize, FILE* fp, const char16_t* chars,
|
||||
size_t length, uint32_t quote);
|
||||
|
||||
template size_t
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
|
||||
#include "gc/Rooting.h"
|
||||
#include "js/RootingAPI.h"
|
||||
#include "vm/Printer.h"
|
||||
#include "vm/Unicode.h"
|
||||
|
||||
class JSAutoByteString;
|
||||
|
@ -343,12 +342,11 @@ extern int
|
|||
OneUcs4ToUtf8Char(uint8_t* utf8Buffer, uint32_t ucs4Char);
|
||||
|
||||
extern size_t
|
||||
PutEscapedStringImpl(char* buffer, size_t size, GenericPrinter* out, JSLinearString* str,
|
||||
uint32_t quote);
|
||||
PutEscapedStringImpl(char* buffer, size_t size, FILE* fp, JSLinearString* str, uint32_t quote);
|
||||
|
||||
template <typename CharT>
|
||||
extern size_t
|
||||
PutEscapedStringImpl(char* buffer, size_t bufferSize, GenericPrinter* out, const CharT* chars,
|
||||
PutEscapedStringImpl(char* buffer, size_t bufferSize, FILE* fp, const CharT* chars,
|
||||
size_t length, uint32_t quote);
|
||||
|
||||
/*
|
||||
|
@ -381,18 +379,6 @@ PutEscapedString(char* buffer, size_t bufferSize, const CharT* chars, size_t len
|
|||
return n;
|
||||
}
|
||||
|
||||
inline bool
|
||||
EscapedStringPrinter(GenericPrinter& out, JSLinearString* str, uint32_t quote)
|
||||
{
|
||||
return PutEscapedStringImpl(nullptr, 0, &out, str, quote) != size_t(-1);
|
||||
}
|
||||
|
||||
inline bool
|
||||
EscapedStringPrinter(GenericPrinter& out, const char* chars, size_t length, uint32_t quote)
|
||||
{
|
||||
return PutEscapedStringImpl(nullptr, 0, &out, chars, length, quote) != size_t(-1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Write str into file escaping any non-printable or non-ASCII character.
|
||||
* If quote is not 0, it must be a single or double quote character that
|
||||
|
@ -401,19 +387,13 @@ EscapedStringPrinter(GenericPrinter& out, const char* chars, size_t length, uint
|
|||
inline bool
|
||||
FileEscapedString(FILE* fp, JSLinearString* str, uint32_t quote)
|
||||
{
|
||||
Fprinter out(fp);
|
||||
bool res = EscapedStringPrinter(out, str, quote);
|
||||
out.finish();
|
||||
return res;
|
||||
return PutEscapedStringImpl(nullptr, 0, fp, str, quote) != size_t(-1);
|
||||
}
|
||||
|
||||
inline bool
|
||||
FileEscapedString(FILE* fp, const char* chars, size_t length, uint32_t quote)
|
||||
{
|
||||
Fprinter out(fp);
|
||||
bool res = EscapedStringPrinter(out, chars, length, quote);
|
||||
out.finish();
|
||||
return res;
|
||||
return PutEscapedStringImpl(nullptr, 0, fp, chars, length, quote) != size_t(-1);
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -295,7 +295,6 @@ UNIFIED_SOURCES += [
|
|||
'vm/NativeObject.cpp',
|
||||
'vm/ObjectGroup.cpp',
|
||||
'vm/PIC.cpp',
|
||||
'vm/Printer.cpp',
|
||||
'vm/Probes.cpp',
|
||||
'vm/ProxyObject.cpp',
|
||||
'vm/ReceiverGuard.cpp',
|
||||
|
|
|
@ -58,27 +58,4 @@ js::Debugger::onExceptionUnwind(JSContext* cx, AbstractFramePtr frame)
|
|||
return slowPathOnExceptionUnwind(cx, frame);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
js::Debugger::observesIonCompilation(JSContext* cx)
|
||||
{
|
||||
// If the current compartment is observed by any Debugger.
|
||||
if (!cx->compartment()->isDebuggee())
|
||||
return false;
|
||||
|
||||
// If any attached Debugger watch for Jit compilation results.
|
||||
if (!Debugger::hasLiveHook(cx->global(), Debugger::OnIonCompilation))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
js::Debugger::onIonCompilation(JSContext* cx, AutoScriptVector& scripts, LSprinter& graph)
|
||||
{
|
||||
if (!observesIonCompilation(cx))
|
||||
return;
|
||||
|
||||
slowPathOnIonCompilation(cx, scripts, graph);
|
||||
}
|
||||
|
||||
#endif /* vm_Debugger_inl_h */
|
||||
|
|
|
@ -20,8 +20,6 @@
|
|||
#include "gc/Marking.h"
|
||||
#include "jit/BaselineDebugModeOSR.h"
|
||||
#include "jit/BaselineJIT.h"
|
||||
#include "jit/JSONSpewer.h"
|
||||
#include "jit/MIRGraph.h"
|
||||
#include "js/GCAPI.h"
|
||||
#include "js/UbiNodeTraverse.h"
|
||||
#include "js/Vector.h"
|
||||
|
@ -542,15 +540,33 @@ Debugger::hasAnyLiveHooks() const
|
|||
/* static */ JSTrapStatus
|
||||
Debugger::slowPathOnEnterFrame(JSContext* cx, AbstractFramePtr frame)
|
||||
{
|
||||
// Build the list of recipients.
|
||||
AutoValueVector triggered(cx);
|
||||
Handle<GlobalObject*> global = cx->global();
|
||||
|
||||
if (GlobalObject::DebuggerVector* debuggers = global->getDebuggers()) {
|
||||
for (Debugger** p = debuggers->begin(); p != debuggers->end(); p++) {
|
||||
Debugger* dbg = *p;
|
||||
if (dbg->observesFrame(frame) && dbg->observesEnterFrame() &&
|
||||
!triggered.append(ObjectValue(*dbg->toJSObject())))
|
||||
{
|
||||
cx->clearPendingException();
|
||||
return JSTRAP_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JSTrapStatus status = JSTRAP_CONTINUE;
|
||||
RootedValue rval(cx);
|
||||
JSTrapStatus status = dispatchHook(
|
||||
cx,
|
||||
[frame](Debugger* dbg) -> bool {
|
||||
return dbg->observesFrame(frame) && dbg->observesEnterFrame();
|
||||
},
|
||||
[&](Debugger* dbg) -> JSTrapStatus {
|
||||
return dbg->fireEnterFrame(cx, frame, &rval);
|
||||
});
|
||||
// Deliver the event, checking again as in dispatchHook.
|
||||
for (Value* p = triggered.begin(); p != triggered.end(); p++) {
|
||||
Debugger* dbg = Debugger::fromJSObject(&p->toObject());
|
||||
if (dbg->debuggees.has(global) && dbg->observesEnterFrame()) {
|
||||
status = dbg->fireEnterFrame(cx, frame, &rval);
|
||||
if (status != JSTRAP_CONTINUE)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch (status) {
|
||||
case JSTRAP_CONTINUE:
|
||||
|
@ -689,12 +705,7 @@ Debugger::slowPathOnLeaveFrame(JSContext* cx, AbstractFramePtr frame, bool frame
|
|||
Debugger::slowPathOnDebuggerStatement(JSContext* cx, AbstractFramePtr frame)
|
||||
{
|
||||
RootedValue rval(cx);
|
||||
JSTrapStatus status = dispatchHook(
|
||||
cx,
|
||||
[](Debugger* dbg) -> bool { return dbg->getHook(OnDebuggerStatement); },
|
||||
[&](Debugger* dbg) -> JSTrapStatus {
|
||||
return dbg->fireDebuggerStatement(cx, &rval);
|
||||
});
|
||||
JSTrapStatus status = dispatchHook(cx, &rval, OnDebuggerStatement, NullPtr());
|
||||
|
||||
switch (status) {
|
||||
case JSTRAP_CONTINUE:
|
||||
|
@ -725,12 +736,7 @@ Debugger::slowPathOnExceptionUnwind(JSContext* cx, AbstractFramePtr frame)
|
|||
return JSTRAP_CONTINUE;
|
||||
|
||||
RootedValue rval(cx);
|
||||
JSTrapStatus status = dispatchHook(
|
||||
cx,
|
||||
[](Debugger* dbg) -> bool { return dbg->getHook(OnExceptionUnwind); },
|
||||
[&](Debugger* dbg) -> JSTrapStatus {
|
||||
return dbg->fireExceptionUnwind(cx, &rval);
|
||||
});
|
||||
JSTrapStatus status = dispatchHook(cx, &rval, OnExceptionUnwind, NullPtr());
|
||||
|
||||
switch (status) {
|
||||
case JSTRAP_CONTINUE:
|
||||
|
@ -1286,80 +1292,14 @@ Debugger::fireOnGarbageCollectionHook(JSContext* cx,
|
|||
handleUncaughtException(ac, true);
|
||||
}
|
||||
|
||||
JSTrapStatus
|
||||
Debugger::fireOnIonCompilationHook(JSContext* cx, AutoScriptVector& scripts, LSprinter& graph)
|
||||
{
|
||||
RootedObject hook(cx, getHook(OnIonCompilation));
|
||||
MOZ_ASSERT(hook);
|
||||
MOZ_ASSERT(hook->isCallable());
|
||||
|
||||
Maybe<AutoCompartment> ac;
|
||||
ac.emplace(cx, object);
|
||||
|
||||
// Copy the vector of scripts to a JS Array of Debugger.Script
|
||||
RootedObject tmpObj(cx);
|
||||
RootedValue tmpVal(cx);
|
||||
AutoValueVector dbgScripts(cx);
|
||||
for (size_t i = 0; i < scripts.length(); i++) {
|
||||
tmpObj = wrapScript(cx, scripts[i]);
|
||||
if (!tmpObj)
|
||||
return handleUncaughtException(ac, false);
|
||||
|
||||
tmpVal.setObject(*tmpObj);
|
||||
if (!dbgScripts.append(tmpVal))
|
||||
return handleUncaughtException(ac, false);
|
||||
}
|
||||
|
||||
RootedObject dbgScriptsArray(cx, JS_NewArrayObject(cx, dbgScripts));
|
||||
if (!dbgScriptsArray)
|
||||
return handleUncaughtException(ac, false);
|
||||
|
||||
// Copy the JSON compilation graph to a JS String which is allocated as part
|
||||
// of the Debugger compartment.
|
||||
Sprinter jsonPrinter(cx);
|
||||
if (!jsonPrinter.init())
|
||||
return handleUncaughtException(ac, false);
|
||||
|
||||
graph.exportInto(jsonPrinter);
|
||||
if (jsonPrinter.hadOutOfMemory())
|
||||
return handleUncaughtException(ac, false);
|
||||
|
||||
RootedString json(cx, JS_NewStringCopyZ(cx, jsonPrinter.string()));
|
||||
if (!json)
|
||||
return handleUncaughtException(ac, false);
|
||||
|
||||
// Create a JS Object which has the array of scripts, and the string of the
|
||||
// JSON graph.
|
||||
const char* names[] = { "scripts", "json" };
|
||||
JS::AutoValueArray<2> values(cx);
|
||||
values[0].setObject(*dbgScriptsArray);
|
||||
values[1].setString(json);
|
||||
|
||||
RootedObject obj(cx, JS_NewObject(cx, nullptr));
|
||||
if (!obj)
|
||||
return handleUncaughtException(ac, false);
|
||||
|
||||
MOZ_ASSERT(mozilla::ArrayLength(names) == values.length());
|
||||
for (size_t i = 0; i < mozilla::ArrayLength(names); i++) {
|
||||
if (!JS_DefineProperty(cx, obj, names[i], values[i], JSPROP_ENUMERATE, nullptr, nullptr))
|
||||
return handleUncaughtException(ac, false);
|
||||
}
|
||||
|
||||
// Call Debugger.onIonCompilation hook.
|
||||
JS::AutoValueArray<1> argv(cx);
|
||||
argv[0].setObject(*obj);
|
||||
|
||||
RootedValue rv(cx);
|
||||
if (!Invoke(cx, ObjectValue(*object), ObjectValue(*hook), 1, argv.begin(), &rv))
|
||||
return handleUncaughtException(ac, true);
|
||||
return JSTRAP_CONTINUE;
|
||||
}
|
||||
|
||||
template <typename HookIsEnabledFun /* bool (Debugger*) */,
|
||||
typename FireHookFun /* JSTrapStatus (Debugger*) */>
|
||||
/* static */ JSTrapStatus
|
||||
Debugger::dispatchHook(JSContext* cx, HookIsEnabledFun hookIsEnabled, FireHookFun fireHook)
|
||||
Debugger::dispatchHook(JSContext* cx, MutableHandleValue vp, Hook which, HandleObject payload)
|
||||
{
|
||||
MOZ_ASSERT(which == OnDebuggerStatement ||
|
||||
which == OnExceptionUnwind ||
|
||||
which == OnNewPromise ||
|
||||
which == OnPromiseSettled);
|
||||
|
||||
/*
|
||||
* Determine which debuggers will receive this event, and in what order.
|
||||
* Make a copy of the list, since the original is mutable and we will be
|
||||
|
@ -1373,7 +1313,7 @@ Debugger::dispatchHook(JSContext* cx, HookIsEnabledFun hookIsEnabled, FireHookFu
|
|||
if (GlobalObject::DebuggerVector* debuggers = global->getDebuggers()) {
|
||||
for (Debugger** p = debuggers->begin(); p != debuggers->end(); p++) {
|
||||
Debugger* dbg = *p;
|
||||
if (dbg->enabled && hookIsEnabled(dbg)) {
|
||||
if (dbg->enabled && dbg->getHook(which)) {
|
||||
if (!triggered.append(ObjectValue(*dbg->toJSObject())))
|
||||
return JSTRAP_ERROR;
|
||||
}
|
||||
|
@ -1386,8 +1326,23 @@ Debugger::dispatchHook(JSContext* cx, HookIsEnabledFun hookIsEnabled, FireHookFu
|
|||
*/
|
||||
for (Value* p = triggered.begin(); p != triggered.end(); p++) {
|
||||
Debugger* dbg = Debugger::fromJSObject(&p->toObject());
|
||||
if (dbg->debuggees.has(global) && dbg->enabled && hookIsEnabled(dbg)) {
|
||||
JSTrapStatus st = fireHook(dbg);
|
||||
if (dbg->debuggees.has(global) && dbg->enabled && dbg->getHook(which)) {
|
||||
JSTrapStatus st;
|
||||
switch (which) {
|
||||
case OnDebuggerStatement:
|
||||
st = dbg->fireDebuggerStatement(cx, vp);
|
||||
break;
|
||||
case OnExceptionUnwind:
|
||||
st = dbg->fireExceptionUnwind(cx, vp);
|
||||
break;
|
||||
case OnNewPromise:
|
||||
case OnPromiseSettled:
|
||||
st = dbg->firePromiseHook(cx, which, payload, vp);
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSERT_UNREACHABLE("Unexpected debugger hook");
|
||||
st = JSTRAP_CONTINUE;
|
||||
}
|
||||
if (st != JSTRAP_CONTINUE)
|
||||
return st;
|
||||
}
|
||||
|
@ -1398,22 +1353,39 @@ Debugger::dispatchHook(JSContext* cx, HookIsEnabledFun hookIsEnabled, FireHookFu
|
|||
void
|
||||
Debugger::slowPathOnNewScript(JSContext* cx, HandleScript script)
|
||||
{
|
||||
JSTrapStatus status = dispatchHook(
|
||||
cx,
|
||||
[script](Debugger* dbg) -> bool {
|
||||
return dbg->observesNewScript() && dbg->observesScript(script);
|
||||
},
|
||||
[&](Debugger* dbg) -> JSTrapStatus {
|
||||
dbg->fireNewScript(cx, script);
|
||||
return JSTRAP_CONTINUE;
|
||||
});
|
||||
Rooted<GlobalObject*> global(cx, &script->global());
|
||||
|
||||
if (status == JSTRAP_ERROR) {
|
||||
ReportOutOfMemory(cx);
|
||||
return;
|
||||
/*
|
||||
* Build the list of recipients based on the debuggers observing the
|
||||
* script's compartment.
|
||||
*/
|
||||
AutoValueVector triggered(cx);
|
||||
GlobalObject::DebuggerVector* debuggers = global->getDebuggers();
|
||||
if (debuggers) {
|
||||
for (Debugger** p = debuggers->begin(); p != debuggers->end(); p++) {
|
||||
Debugger* dbg = *p;
|
||||
if (dbg->observesNewScript() && dbg->observesScript(script)) {
|
||||
if (!triggered.append(ObjectValue(*dbg->toJSObject()))) {
|
||||
ReportOutOfMemory(cx);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_ASSERT(status == JSTRAP_CONTINUE);
|
||||
/*
|
||||
* Deliver the event to each debugger, checking again as in
|
||||
* Debugger::dispatchHook.
|
||||
*/
|
||||
for (Value* p = triggered.begin(); p != triggered.end(); p++) {
|
||||
Debugger* dbg = Debugger::fromJSObject(&p->toObject());
|
||||
if (dbg->debuggees.has(global) &&
|
||||
dbg->enabled &&
|
||||
dbg->getHook(OnNewScript))
|
||||
{
|
||||
dbg->fireNewScript(cx, script);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ JSTrapStatus
|
||||
|
@ -1673,25 +1645,6 @@ Debugger::slowPathOnLogAllocationSite(JSContext* cx, HandleObject obj, HandleSav
|
|||
return true;
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
Debugger::slowPathOnIonCompilation(JSContext* cx, AutoScriptVector& scripts, LSprinter& graph)
|
||||
{
|
||||
JSTrapStatus status = dispatchHook(
|
||||
cx,
|
||||
[](Debugger* dbg) -> bool { return dbg->getHook(OnIonCompilation); },
|
||||
[&](Debugger* dbg) -> JSTrapStatus {
|
||||
(void) dbg->fireOnIonCompilationHook(cx, scripts, graph);
|
||||
return JSTRAP_CONTINUE;
|
||||
});
|
||||
|
||||
if (status == JSTRAP_ERROR) {
|
||||
cx->clearPendingException();
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(status == JSTRAP_CONTINUE);
|
||||
}
|
||||
|
||||
bool
|
||||
Debugger::isDebuggee(const JSCompartment* compartment) const
|
||||
{
|
||||
|
@ -1795,24 +1748,9 @@ Debugger::slowPathPromiseHook(JSContext* cx, Hook hook, HandleObject promise)
|
|||
MOZ_ASSERT(hook == OnNewPromise || hook == OnPromiseSettled);
|
||||
RootedValue rval(cx);
|
||||
|
||||
JSTrapStatus status = dispatchHook(
|
||||
cx,
|
||||
[hook](Debugger* dbg) -> bool { return dbg->getHook(hook); },
|
||||
[&](Debugger* dbg) -> JSTrapStatus {
|
||||
(void) dbg->firePromiseHook(cx, hook, promise, &rval);
|
||||
return JSTRAP_CONTINUE;
|
||||
});
|
||||
|
||||
if (status == JSTRAP_ERROR) {
|
||||
// The dispatch hook function might fail to append into the list of
|
||||
// Debuggers which are watching for the hook.
|
||||
cx->clearPendingException();
|
||||
return;
|
||||
}
|
||||
|
||||
// Promise hooks are infallible and we ignore errors from uncaught
|
||||
// exceptions by design.
|
||||
MOZ_ASSERT(status == JSTRAP_CONTINUE);
|
||||
(void) dispatchHook(cx, &rval, hook, promise);
|
||||
}
|
||||
|
||||
|
||||
|
@ -2905,20 +2843,6 @@ Debugger::getMemory(JSContext* cx, unsigned argc, Value* vp)
|
|||
return true;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
Debugger::getOnIonCompilation(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
THIS_DEBUGGER(cx, argc, vp, "(get onIonCompilation)", args, dbg);
|
||||
return getHookImpl(cx, args, *dbg, OnIonCompilation);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
Debugger::setOnIonCompilation(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
THIS_DEBUGGER(cx, argc, vp, "(set onIonCompilation)", args, dbg);
|
||||
return setHookImpl(cx, args, *dbg, OnIonCompilation);
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a value used to designate a global (there's quite a variety; see the
|
||||
* docs), return the actual designee.
|
||||
|
@ -4334,7 +4258,6 @@ const JSPropertySpec Debugger::properties[] = {
|
|||
JS_PSGS("allowUnobservedAsmJS", Debugger::getAllowUnobservedAsmJS,
|
||||
Debugger::setAllowUnobservedAsmJS, 0),
|
||||
JS_PSG("memory", Debugger::getMemory, 0),
|
||||
JS_PSGS("onIonCompilation", Debugger::getOnIonCompilation, Debugger::setOnIonCompilation, 0),
|
||||
JS_PS_END
|
||||
};
|
||||
const JSFunctionSpec Debugger::methods[] = {
|
||||
|
|
|
@ -35,8 +35,6 @@ enum JSTrapStatus {
|
|||
|
||||
namespace js {
|
||||
|
||||
class LSprinter;
|
||||
|
||||
class Breakpoint;
|
||||
class DebuggerMemory;
|
||||
|
||||
|
@ -206,7 +204,6 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
|
|||
OnNewPromise,
|
||||
OnPromiseSettled,
|
||||
OnGarbageCollection,
|
||||
OnIonCompilation,
|
||||
HookCount
|
||||
};
|
||||
enum {
|
||||
|
@ -482,8 +479,6 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
|
|||
static bool getAllowUnobservedAsmJS(JSContext* cx, unsigned argc, Value* vp);
|
||||
static bool setAllowUnobservedAsmJS(JSContext* cx, unsigned argc, Value* vp);
|
||||
static bool getMemory(JSContext* cx, unsigned argc, Value* vp);
|
||||
static bool getOnIonCompilation(JSContext* cx, unsigned argc, Value* vp);
|
||||
static bool setOnIonCompilation(JSContext* cx, unsigned argc, Value* vp);
|
||||
static bool addDebuggee(JSContext* cx, unsigned argc, Value* vp);
|
||||
static bool addAllGlobalsAsDebuggees(JSContext* cx, unsigned argc, Value* vp);
|
||||
static bool removeDebuggee(JSContext* cx, unsigned argc, Value* vp);
|
||||
|
@ -551,12 +546,8 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
|
|||
static bool slowPathOnLogAllocationSite(JSContext* cx, HandleObject obj, HandleSavedFrame frame,
|
||||
int64_t when, GlobalObject::DebuggerVector& dbgs);
|
||||
static void slowPathPromiseHook(JSContext* cx, Hook hook, HandleObject promise);
|
||||
static void slowPathOnIonCompilation(JSContext* cx, AutoScriptVector& scripts, LSprinter& graph);
|
||||
|
||||
template <typename HookIsEnabledFun /* bool (Debugger*) */,
|
||||
typename FireHookFun /* JSTrapStatus (Debugger*) */>
|
||||
static JSTrapStatus dispatchHook(JSContext* cx, HookIsEnabledFun hookIsEnabled,
|
||||
FireHookFun fireHook);
|
||||
static JSTrapStatus dispatchHook(JSContext* cx, MutableHandleValue vp, Hook which,
|
||||
HandleObject payload);
|
||||
|
||||
JSTrapStatus fireDebuggerStatement(JSContext* cx, MutableHandleValue vp);
|
||||
JSTrapStatus fireExceptionUnwind(JSContext* cx, MutableHandleValue vp);
|
||||
|
@ -589,13 +580,6 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
|
|||
void fireOnGarbageCollectionHook(JSContext* cx,
|
||||
const JS::dbg::GarbageCollectionEvent::Ptr& gcData);
|
||||
|
||||
/*
|
||||
* Receive a "Ion compilation" event from the engine. An Ion compilation with
|
||||
* the given summary just got linked.
|
||||
*/
|
||||
JSTrapStatus fireOnIonCompilationHook(JSContext* cx, AutoScriptVector& scripts,
|
||||
LSprinter& graph);
|
||||
|
||||
/*
|
||||
* Gets a Debugger.Frame object. If maybeIter is non-null, we eagerly copy
|
||||
* its data if we need to make a new Debugger.Frame.
|
||||
|
@ -713,8 +697,6 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
|
|||
static inline void onNewGlobalObject(JSContext* cx, Handle<GlobalObject*> global);
|
||||
static inline bool onLogAllocationSite(JSContext* cx, JSObject* obj, HandleSavedFrame frame,
|
||||
int64_t when);
|
||||
static inline bool observesIonCompilation(JSContext* cx);
|
||||
static inline void onIonCompilation(JSContext* cx, AutoScriptVector& scripts, LSprinter& graph);
|
||||
static JSTrapStatus onTrap(JSContext* cx, MutableHandleValue vp);
|
||||
static JSTrapStatus onSingleStep(JSContext* cx, MutableHandleValue vp);
|
||||
static bool handleBaselineOsr(JSContext* cx, InterpreterFrame* from, jit::BaselineFrame* to);
|
||||
|
|
|
@ -1054,7 +1054,6 @@ HelperThread::handleAsmJSWorkload()
|
|||
&asmData->mir->alloc());
|
||||
|
||||
int64_t before = PRMJ_Now();
|
||||
jit::AutoSpewEndFunction spewEndFunction(asmData->mir);
|
||||
|
||||
if (!OptimizeMIR(asmData->mir))
|
||||
break;
|
||||
|
|
|
@ -1,609 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "vm/Printer.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "jscntxt.h"
|
||||
#include "jsprf.h"
|
||||
#include "jsutil.h"
|
||||
|
||||
#include "ds/LifoAlloc.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
GenericPrinter::GenericPrinter()
|
||||
: reportedOOM_(false)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
GenericPrinter::reportOutOfMemory()
|
||||
{
|
||||
if (reportedOOM_)
|
||||
return;
|
||||
reportedOOM_ = true;
|
||||
}
|
||||
|
||||
bool
|
||||
GenericPrinter::hadOutOfMemory() const
|
||||
{
|
||||
return reportedOOM_;
|
||||
}
|
||||
|
||||
int
|
||||
GenericPrinter::put(const char* s)
|
||||
{
|
||||
return put(s, strlen(s));
|
||||
}
|
||||
|
||||
int
|
||||
GenericPrinter::printf(const char* fmt, ...)
|
||||
{
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
int i = vprintf(fmt, va);
|
||||
va_end(va);
|
||||
return i;
|
||||
}
|
||||
|
||||
int
|
||||
GenericPrinter::vprintf(const char* fmt, va_list ap)
|
||||
{
|
||||
// Simple shortcut to avoid allocating strings.
|
||||
if (strchr(fmt, '%') == nullptr)
|
||||
return put(fmt);
|
||||
|
||||
char* bp;
|
||||
bp = JS_vsmprintf(fmt, ap); /* XXX vsaprintf */
|
||||
if (!bp) {
|
||||
reportOutOfMemory();
|
||||
return -1;
|
||||
}
|
||||
int i = put(bp);
|
||||
js_free(bp);
|
||||
return i;
|
||||
}
|
||||
|
||||
const size_t Sprinter::DefaultSize = 64;
|
||||
|
||||
bool
|
||||
Sprinter::realloc_(size_t newSize)
|
||||
{
|
||||
MOZ_ASSERT(newSize > (size_t) offset);
|
||||
char* newBuf = (char*) js_realloc(base, newSize);
|
||||
if (!newBuf) {
|
||||
reportOutOfMemory();
|
||||
return false;
|
||||
}
|
||||
base = newBuf;
|
||||
size = newSize;
|
||||
base[size - 1] = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
Sprinter::Sprinter(ExclusiveContext* cx)
|
||||
: context(cx),
|
||||
#ifdef DEBUG
|
||||
initialized(false),
|
||||
#endif
|
||||
base(nullptr), size(0), offset(0)
|
||||
{ }
|
||||
|
||||
Sprinter::~Sprinter()
|
||||
{
|
||||
#ifdef DEBUG
|
||||
if (initialized)
|
||||
checkInvariants();
|
||||
#endif
|
||||
js_free(base);
|
||||
}
|
||||
|
||||
bool
|
||||
Sprinter::init()
|
||||
{
|
||||
MOZ_ASSERT(!initialized);
|
||||
base = (char*) js_malloc(DefaultSize);
|
||||
if (!base) {
|
||||
reportOutOfMemory();
|
||||
return false;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
initialized = true;
|
||||
#endif
|
||||
*base = 0;
|
||||
size = DefaultSize;
|
||||
base[size - 1] = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
Sprinter::checkInvariants() const
|
||||
{
|
||||
MOZ_ASSERT(initialized);
|
||||
MOZ_ASSERT((size_t) offset < size);
|
||||
MOZ_ASSERT(base[size - 1] == 0);
|
||||
}
|
||||
|
||||
const char*
|
||||
Sprinter::string() const
|
||||
{
|
||||
return base;
|
||||
}
|
||||
|
||||
const char*
|
||||
Sprinter::stringEnd() const
|
||||
{
|
||||
return base + offset;
|
||||
}
|
||||
|
||||
char*
|
||||
Sprinter::stringAt(ptrdiff_t off) const
|
||||
{
|
||||
MOZ_ASSERT(off >= 0 && (size_t) off < size);
|
||||
return base + off;
|
||||
}
|
||||
|
||||
char&
|
||||
Sprinter::operator[](size_t off)
|
||||
{
|
||||
MOZ_ASSERT(off < size);
|
||||
return *(base + off);
|
||||
}
|
||||
|
||||
char*
|
||||
Sprinter::reserve(size_t len)
|
||||
{
|
||||
InvariantChecker ic(this);
|
||||
|
||||
while (len + 1 > size - offset) { /* Include trailing \0 */
|
||||
if (!realloc_(size * 2))
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
char* sb = base + offset;
|
||||
offset += len;
|
||||
return sb;
|
||||
}
|
||||
|
||||
int
|
||||
Sprinter::put(const char* s, size_t len)
|
||||
{
|
||||
InvariantChecker ic(this);
|
||||
|
||||
const char* oldBase = base;
|
||||
const char* oldEnd = base + size;
|
||||
|
||||
ptrdiff_t oldOffset = offset;
|
||||
char* bp = reserve(len);
|
||||
if (!bp)
|
||||
return -1;
|
||||
|
||||
/* s is within the buffer already */
|
||||
if (s >= oldBase && s < oldEnd) {
|
||||
/* buffer was realloc'ed */
|
||||
if (base != oldBase)
|
||||
s = stringAt(s - oldBase); /* this is where it lives now */
|
||||
memmove(bp, s, len);
|
||||
} else {
|
||||
js_memcpy(bp, s, len);
|
||||
}
|
||||
|
||||
bp[len] = 0;
|
||||
return oldOffset;
|
||||
}
|
||||
|
||||
int
|
||||
Sprinter::vprintf(const char* fmt, va_list ap)
|
||||
{
|
||||
InvariantChecker ic(this);
|
||||
|
||||
do {
|
||||
va_list aq;
|
||||
va_copy(aq, ap);
|
||||
int i = vsnprintf(base + offset, size - offset, fmt, aq);
|
||||
va_end(aq);
|
||||
if (i > -1 && (size_t) i < size - offset) {
|
||||
offset += i;
|
||||
return i;
|
||||
}
|
||||
} while (realloc_(size * 2));
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
Sprinter::putString(JSString* s)
|
||||
{
|
||||
InvariantChecker ic(this);
|
||||
|
||||
size_t length = s->length();
|
||||
size_t size = length;
|
||||
|
||||
ptrdiff_t oldOffset = offset;
|
||||
char* buffer = reserve(size);
|
||||
if (!buffer)
|
||||
return -1;
|
||||
|
||||
JSLinearString* linear = s->ensureLinear(context);
|
||||
if (!linear)
|
||||
return -1;
|
||||
|
||||
JS::AutoCheckCannotGC nogc;
|
||||
if (linear->hasLatin1Chars())
|
||||
mozilla::PodCopy(reinterpret_cast<Latin1Char*>(buffer), linear->latin1Chars(nogc), length);
|
||||
else
|
||||
DeflateStringToBuffer(nullptr, linear->twoByteChars(nogc), length, buffer, &size);
|
||||
|
||||
buffer[size] = 0;
|
||||
return oldOffset;
|
||||
}
|
||||
|
||||
ptrdiff_t
|
||||
Sprinter::getOffset() const
|
||||
{
|
||||
return offset;
|
||||
}
|
||||
|
||||
void
|
||||
Sprinter::reportOutOfMemory()
|
||||
{
|
||||
if (reportedOOM_)
|
||||
return;
|
||||
if (context)
|
||||
ReportOutOfMemory(context);
|
||||
reportedOOM_ = true;
|
||||
}
|
||||
|
||||
ptrdiff_t
|
||||
Sprint(Sprinter* sp, const char* format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
char* bp;
|
||||
ptrdiff_t offset;
|
||||
|
||||
va_start(ap, format);
|
||||
bp = JS_vsmprintf(format, ap); /* XXX vsaprintf */
|
||||
va_end(ap);
|
||||
if (!bp) {
|
||||
sp->reportOutOfMemory();
|
||||
return -1;
|
||||
}
|
||||
offset = sp->put(bp);
|
||||
js_free(bp);
|
||||
return offset;
|
||||
}
|
||||
|
||||
const char js_EscapeMap[] = {
|
||||
'\b', 'b',
|
||||
'\f', 'f',
|
||||
'\n', 'n',
|
||||
'\r', 'r',
|
||||
'\t', 't',
|
||||
'\v', 'v',
|
||||
'"', '"',
|
||||
'\'', '\'',
|
||||
'\\', '\\',
|
||||
'\0'
|
||||
};
|
||||
|
||||
template <typename CharT>
|
||||
static char*
|
||||
QuoteString(Sprinter* sp, const CharT* s, size_t length, char16_t quote)
|
||||
{
|
||||
/* Sample off first for later return value pointer computation. */
|
||||
ptrdiff_t offset = sp->getOffset();
|
||||
|
||||
if (quote && Sprint(sp, "%c", char(quote)) < 0)
|
||||
return nullptr;
|
||||
|
||||
const CharT* end = s + length;
|
||||
|
||||
/* Loop control variables: end points at end of string sentinel. */
|
||||
for (const CharT* t = s; t < end; s = ++t) {
|
||||
/* Move t forward from s past un-quote-worthy characters. */
|
||||
char16_t c = *t;
|
||||
while (c < 127 && isprint(c) && c != quote && c != '\\' && c != '\t') {
|
||||
c = *++t;
|
||||
if (t == end)
|
||||
break;
|
||||
}
|
||||
|
||||
{
|
||||
ptrdiff_t len = t - s;
|
||||
ptrdiff_t base = sp->getOffset();
|
||||
if (!sp->reserve(len))
|
||||
return nullptr;
|
||||
|
||||
for (ptrdiff_t i = 0; i < len; ++i)
|
||||
(*sp)[base + i] = char(*s++);
|
||||
(*sp)[base + len] = 0;
|
||||
}
|
||||
|
||||
if (t == end)
|
||||
break;
|
||||
|
||||
/* Use js_EscapeMap, \u, or \x only if necessary. */
|
||||
const char* escape;
|
||||
if (!(c >> 8) && c != 0 && (escape = strchr(js_EscapeMap, int(c))) != nullptr) {
|
||||
if (Sprint(sp, "\\%c", escape[1]) < 0)
|
||||
return nullptr;
|
||||
} else {
|
||||
/*
|
||||
* Use \x only if the high byte is 0 and we're in a quoted string,
|
||||
* because ECMA-262 allows only \u, not \x, in Unicode identifiers
|
||||
* (see bug 621814).
|
||||
*/
|
||||
if (Sprint(sp, (quote && !(c >> 8)) ? "\\x%02X" : "\\u%04X", c) < 0)
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/* Sprint the closing quote and return the quoted string. */
|
||||
if (quote && Sprint(sp, "%c", char(quote)) < 0)
|
||||
return nullptr;
|
||||
|
||||
/*
|
||||
* If we haven't Sprint'd anything yet, Sprint an empty string so that
|
||||
* the return below gives a valid result.
|
||||
*/
|
||||
if (offset == sp->getOffset() && Sprint(sp, "") < 0)
|
||||
return nullptr;
|
||||
|
||||
return sp->stringAt(offset);
|
||||
}
|
||||
|
||||
char*
|
||||
QuoteString(Sprinter* sp, JSString* str, char16_t quote)
|
||||
{
|
||||
JSLinearString* linear = str->ensureLinear(sp->context);
|
||||
if (!linear)
|
||||
return nullptr;
|
||||
|
||||
JS::AutoCheckCannotGC nogc;
|
||||
return linear->hasLatin1Chars()
|
||||
? QuoteString(sp, linear->latin1Chars(nogc), linear->length(), quote)
|
||||
: QuoteString(sp, linear->twoByteChars(nogc), linear->length(), quote);
|
||||
}
|
||||
|
||||
JSString*
|
||||
QuoteString(ExclusiveContext* cx, JSString* str, char16_t quote)
|
||||
{
|
||||
Sprinter sprinter(cx);
|
||||
if (!sprinter.init())
|
||||
return nullptr;
|
||||
char* bytes = QuoteString(&sprinter, str, quote);
|
||||
if (!bytes)
|
||||
return nullptr;
|
||||
return NewStringCopyZ<CanGC>(cx, bytes);
|
||||
}
|
||||
|
||||
Fprinter::Fprinter(FILE* fp)
|
||||
: file_(nullptr)
|
||||
{
|
||||
init(fp);
|
||||
}
|
||||
|
||||
Fprinter::Fprinter()
|
||||
: file_(nullptr)
|
||||
{ }
|
||||
|
||||
Fprinter::~Fprinter()
|
||||
{
|
||||
MOZ_ASSERT_IF(init_, !file_);
|
||||
}
|
||||
|
||||
bool
|
||||
Fprinter::init(const char* path)
|
||||
{
|
||||
MOZ_ASSERT(!file_);
|
||||
file_ = fopen(path, "w");
|
||||
if (!file_)
|
||||
return false;
|
||||
init_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
Fprinter::init(FILE *fp)
|
||||
{
|
||||
MOZ_ASSERT(!file_);
|
||||
file_ = fp;
|
||||
init_ = false;
|
||||
}
|
||||
|
||||
void
|
||||
Fprinter::flush()
|
||||
{
|
||||
MOZ_ASSERT(file_);
|
||||
fflush(file_);
|
||||
}
|
||||
|
||||
void
|
||||
Fprinter::finish()
|
||||
{
|
||||
MOZ_ASSERT(file_);
|
||||
if (init_)
|
||||
fclose(file_);
|
||||
file_ = nullptr;
|
||||
}
|
||||
|
||||
int
|
||||
Fprinter::put(const char* s, size_t len)
|
||||
{
|
||||
MOZ_ASSERT(file_);
|
||||
int i = fwrite(s, len, 1, file_);
|
||||
if (i == -1 || i != int(len))
|
||||
reportOutOfMemory();
|
||||
return i;
|
||||
}
|
||||
|
||||
int
|
||||
Fprinter::put(const char* s)
|
||||
{
|
||||
MOZ_ASSERT(file_);
|
||||
int i = fputs(s, file_);
|
||||
if (i == -1)
|
||||
reportOutOfMemory();
|
||||
return i;
|
||||
}
|
||||
|
||||
int
|
||||
Fprinter::printf(const char* fmt, ...)
|
||||
{
|
||||
MOZ_ASSERT(file_);
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
int i = vfprintf(file_, fmt, ap);
|
||||
if (i == -1)
|
||||
reportOutOfMemory();
|
||||
va_end(ap);
|
||||
return i;
|
||||
}
|
||||
|
||||
int
|
||||
Fprinter::vprintf(const char* fmt, va_list ap)
|
||||
{
|
||||
MOZ_ASSERT(file_);
|
||||
int i = vfprintf(file_, fmt, ap);
|
||||
if (i == -1)
|
||||
reportOutOfMemory();
|
||||
return i;
|
||||
}
|
||||
|
||||
LSprinter::LSprinter(LifoAlloc* lifoAlloc)
|
||||
: alloc_(lifoAlloc),
|
||||
head_(nullptr),
|
||||
tail_(nullptr),
|
||||
unused_(0)
|
||||
{ }
|
||||
|
||||
LSprinter::~LSprinter()
|
||||
{
|
||||
// This LSprinter might be allocated as part of the same LifoAlloc, so we
|
||||
// should not expect the destructor to be called.
|
||||
}
|
||||
|
||||
void
|
||||
LSprinter::exportInto(GenericPrinter& out) const
|
||||
{
|
||||
if (!head_)
|
||||
return;
|
||||
|
||||
for (Chunk* it = head_; it != tail_; it = it->next)
|
||||
out.put(it->chars(), it->length);
|
||||
out.put(tail_->chars(), tail_->length - unused_);
|
||||
}
|
||||
|
||||
void
|
||||
LSprinter::clear()
|
||||
{
|
||||
head_ = nullptr;
|
||||
tail_ = nullptr;
|
||||
unused_ = 0;
|
||||
reportedOOM_ = false;
|
||||
}
|
||||
|
||||
int
|
||||
LSprinter::put(const char* s, size_t len)
|
||||
{
|
||||
size_t origLen = len;
|
||||
if (unused_ > 0 && tail_) {
|
||||
size_t minLen = unused_ < len ? unused_ : len;
|
||||
js_memcpy(tail_->end() - unused_, s, minLen);
|
||||
unused_ -= minLen;
|
||||
len -= minLen;
|
||||
s += minLen;
|
||||
}
|
||||
|
||||
if (len == 0)
|
||||
return origLen;
|
||||
|
||||
size_t allocLength = AlignBytes(sizeof(Chunk) + len, js::detail::LIFO_ALLOC_ALIGN);
|
||||
Chunk* last = reinterpret_cast<Chunk*>(alloc_->alloc(allocLength));
|
||||
if (!last) {
|
||||
reportOutOfMemory();
|
||||
return origLen - len;
|
||||
}
|
||||
|
||||
if (tail_ && reinterpret_cast<char*>(last) == tail_->end()) {
|
||||
// tail_ and last are next to each others in memory, knowing that the
|
||||
// TempAlloctator has no meta data and is just a bump allocator, we
|
||||
// append the new allocated space to the tail_.
|
||||
unused_ = allocLength;
|
||||
tail_->length += allocLength;
|
||||
} else {
|
||||
// Remove the size of the header from the allocated length.
|
||||
allocLength -= sizeof(Chunk);
|
||||
last->next = nullptr;
|
||||
last->length = allocLength;
|
||||
unused_ = allocLength;
|
||||
if (!head_)
|
||||
head_ = last;
|
||||
else
|
||||
tail_->next = last;
|
||||
|
||||
tail_ = last;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(tail_->length >= unused_);
|
||||
js_memcpy(tail_->end() - unused_, s, len);
|
||||
unused_ -= len;
|
||||
return origLen;
|
||||
}
|
||||
|
||||
int
|
||||
LSprinter::put(const char* s)
|
||||
{
|
||||
return put(s, strlen(s));
|
||||
}
|
||||
|
||||
int
|
||||
LSprinter::printf(const char* fmt, ...)
|
||||
{
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
int i = vprintf(fmt, va);
|
||||
va_end(va);
|
||||
return i;
|
||||
}
|
||||
|
||||
int
|
||||
LSprinter::vprintf(const char* fmt, va_list ap)
|
||||
{
|
||||
// Simple shortcut to avoid allocating strings.
|
||||
if (strchr(fmt, '%') == nullptr)
|
||||
return put(fmt);
|
||||
|
||||
char* bp;
|
||||
bp = JS_vsmprintf(fmt, ap); /* XXX vsaprintf */
|
||||
if (!bp) {
|
||||
reportOutOfMemory();
|
||||
return -1;
|
||||
}
|
||||
int i = put(bp);
|
||||
js_free(bp);
|
||||
return i;
|
||||
}
|
||||
|
||||
void
|
||||
LSprinter::reportOutOfMemory()
|
||||
{
|
||||
if (reportedOOM_)
|
||||
return;
|
||||
reportedOOM_ = true;
|
||||
}
|
||||
|
||||
bool
|
||||
LSprinter::hadOutOfMemory() const
|
||||
{
|
||||
return reportedOOM_;
|
||||
}
|
||||
|
||||
} // namespace js
|
|
@ -1,223 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef vm_Printer_h
|
||||
#define vm_Printer_h
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
|
||||
class JSString;
|
||||
|
||||
namespace js {
|
||||
|
||||
class ExclusiveContext;
|
||||
class LifoAlloc;
|
||||
|
||||
// Generic printf interface, similar to an ostream in the standard library.
|
||||
//
|
||||
// This class is useful to make generic printers which can work either with a
|
||||
// file backend, with a buffer allocated with an ExclusiveContext or a link-list
|
||||
// of chunks allocated with a LifoAlloc.
|
||||
class GenericPrinter
|
||||
{
|
||||
protected:
|
||||
bool reportedOOM_; // record reported OOM.
|
||||
|
||||
GenericPrinter();
|
||||
|
||||
public:
|
||||
// Puts |len| characters from |s| at the current position and return an offset to
|
||||
// the beginning of this new data.
|
||||
virtual int put(const char* s, size_t len) = 0;
|
||||
virtual int put(const char* s);
|
||||
|
||||
// Prints a formatted string into the buffer.
|
||||
virtual int printf(const char* fmt, ...);
|
||||
virtual int vprintf(const char* fmt, va_list ap);
|
||||
|
||||
// Report that a string operation failed to get the memory it requested. The
|
||||
// first call to this function calls JS_ReportOutOfMemory, and sets this
|
||||
// Sprinter's outOfMemory flag; subsequent calls do nothing.
|
||||
virtual void reportOutOfMemory();
|
||||
|
||||
// Return true if this Sprinter ran out of memory.
|
||||
virtual bool hadOutOfMemory() const;
|
||||
};
|
||||
|
||||
// Sprintf, but with unlimited and automatically allocated buffering.
|
||||
class Sprinter final : public GenericPrinter
|
||||
{
|
||||
public:
|
||||
struct InvariantChecker
|
||||
{
|
||||
const Sprinter* parent;
|
||||
|
||||
explicit InvariantChecker(const Sprinter* p) : parent(p) {
|
||||
parent->checkInvariants();
|
||||
}
|
||||
|
||||
~InvariantChecker() {
|
||||
parent->checkInvariants();
|
||||
}
|
||||
};
|
||||
|
||||
ExclusiveContext* context; // context executing the decompiler
|
||||
|
||||
private:
|
||||
static const size_t DefaultSize;
|
||||
#ifdef DEBUG
|
||||
bool initialized; // true if this is initialized, use for debug builds
|
||||
#endif
|
||||
char* base; // malloc'd buffer address
|
||||
size_t size; // size of buffer allocated at base
|
||||
ptrdiff_t offset; // offset of next free char in buffer
|
||||
|
||||
bool realloc_(size_t newSize);
|
||||
|
||||
public:
|
||||
explicit Sprinter(ExclusiveContext* cx);
|
||||
~Sprinter();
|
||||
|
||||
// Initialize this sprinter, returns false on error.
|
||||
bool init();
|
||||
|
||||
void checkInvariants() const;
|
||||
|
||||
const char* string() const;
|
||||
const char* stringEnd() const;
|
||||
// Returns the string at offset |off|.
|
||||
char* stringAt(ptrdiff_t off) const;
|
||||
// Returns the char at offset |off|.
|
||||
char& operator[](size_t off);
|
||||
|
||||
// Attempt to reserve len + 1 space (for a trailing nullptr byte). If the
|
||||
// attempt succeeds, return a pointer to the start of that space and adjust the
|
||||
// internal content. The caller *must* completely fill this space on success.
|
||||
char* reserve(size_t len);
|
||||
|
||||
// Puts |len| characters from |s| at the current position and return an offset to
|
||||
// the beginning of this new data.
|
||||
using GenericPrinter::put;
|
||||
virtual int put(const char* s, size_t len) override;
|
||||
|
||||
// Prints a formatted string into the buffer.
|
||||
virtual int vprintf(const char* fmt, va_list ap) override;
|
||||
|
||||
int putString(JSString* str);
|
||||
|
||||
ptrdiff_t getOffset() const;
|
||||
|
||||
// Report that a string operation failed to get the memory it requested. The
|
||||
// first call to this function calls JS_ReportOutOfMemory, and sets this
|
||||
// Sprinter's outOfMemory flag; subsequent calls do nothing.
|
||||
virtual void reportOutOfMemory() override;
|
||||
};
|
||||
|
||||
// Fprinter, print a string directly into a file.
|
||||
class Fprinter final : public GenericPrinter
|
||||
{
|
||||
private:
|
||||
FILE* file_;
|
||||
bool init_;
|
||||
|
||||
public:
|
||||
explicit Fprinter(FILE* fp);
|
||||
Fprinter();
|
||||
~Fprinter();
|
||||
|
||||
// Initialize this printer, returns false on error.
|
||||
bool init(const char* path);
|
||||
void init(FILE* fp);
|
||||
bool isInitialized() {
|
||||
return file_ != nullptr;
|
||||
}
|
||||
void flush();
|
||||
void finish();
|
||||
|
||||
// Puts |len| characters from |s| at the current position and return an
|
||||
// offset to the beginning of this new data.
|
||||
virtual int put(const char* s, size_t len) override;
|
||||
virtual int put(const char* s) override;
|
||||
|
||||
// Prints a formatted string into the buffer.
|
||||
virtual int printf(const char* fmt, ...) override;
|
||||
virtual int vprintf(const char* fmt, va_list ap) override;
|
||||
};
|
||||
|
||||
// LSprinter, is similar to Sprinter except that instead of using an
|
||||
// ExclusiveContext to allocate strings, it use a LifoAlloc as a backend for the
|
||||
// allocation of the chunk of the string.
|
||||
class LSprinter final : public GenericPrinter
|
||||
{
|
||||
private:
|
||||
struct Chunk
|
||||
{
|
||||
Chunk* next;
|
||||
size_t length;
|
||||
|
||||
char* chars() {
|
||||
return reinterpret_cast<char*>(this + 1);
|
||||
}
|
||||
char* end() {
|
||||
return chars() + length;
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
LifoAlloc* alloc_; // LifoAlloc used as a backend of chunk allocations.
|
||||
Chunk* head_;
|
||||
Chunk* tail_;
|
||||
size_t unused_;
|
||||
|
||||
public:
|
||||
explicit LSprinter(LifoAlloc* lifoAlloc);
|
||||
~LSprinter();
|
||||
|
||||
// Copy the content of the chunks into another printer, such that we can
|
||||
// flush the content of this printer to a file.
|
||||
void exportInto(GenericPrinter& out) const;
|
||||
|
||||
// Drop the current string, and let them be free with the LifoAlloc.
|
||||
void clear();
|
||||
|
||||
// Puts |len| characters from |s| at the current position and return an
|
||||
// offset to the beginning of this new data.
|
||||
virtual int put(const char* s, size_t len) override;
|
||||
virtual int put(const char* s) override;
|
||||
|
||||
// Prints a formatted string into the buffer.
|
||||
virtual int printf(const char* fmt, ...) override;
|
||||
virtual int vprintf(const char* fmt, va_list ap) override;
|
||||
|
||||
// Report that a string operation failed to get the memory it requested. The
|
||||
// first call to this function calls JS_ReportOutOfMemory, and sets this
|
||||
// Sprinter's outOfMemory flag; subsequent calls do nothing.
|
||||
virtual void reportOutOfMemory() override;
|
||||
|
||||
// Return true if this Sprinter ran out of memory.
|
||||
virtual bool hadOutOfMemory() const override;
|
||||
};
|
||||
|
||||
extern ptrdiff_t
|
||||
Sprint(Sprinter* sp, const char* format, ...);
|
||||
|
||||
// Map escaped code to the letter/symbol escaped with a backslash.
|
||||
extern const char js_EscapeMap[];
|
||||
|
||||
// Return a GC'ed string containing the chars in str, with any non-printing
|
||||
// chars or quotes (' or " as specified by the quote argument) escaped, and
|
||||
// with the quote character at the beginning and end of the result string.
|
||||
extern JSString*
|
||||
QuoteString(ExclusiveContext* cx, JSString* str, char16_t quote);
|
||||
|
||||
extern char*
|
||||
QuoteString(Sprinter* sp, JSString* str, char16_t quote);
|
||||
|
||||
|
||||
} // namespace js
|
||||
|
||||
#endif // vm_Printer_h
|
Загрузка…
Ссылка в новой задаче