зеркало из https://github.com/mozilla/gecko-dev.git
Bug 884473: Integrate perf with OdinMonkey: basic blocks profiling; r=sstangl
This commit is contained in:
Родитель
fedb281b89
Коммит
1c44ac2ae6
|
@ -1204,6 +1204,7 @@ class MOZ_STACK_CLASS ModuleCompiler
|
|||
|
||||
JSContext *cx() const { return cx_; }
|
||||
MacroAssembler &masm() { return masm_; }
|
||||
TokenStream &tokenStream() { return tokenStream_; }
|
||||
Label &stackOverflowLabel() { return stackOverflowLabel_; }
|
||||
Label &operationCallbackLabel() { return operationCallbackLabel_; }
|
||||
bool hasError() const { return errorString_ != NULL; }
|
||||
|
@ -1373,6 +1374,13 @@ class MOZ_STACK_CLASS ModuleCompiler
|
|||
unsigned startCodeOffset = func.codeLabel()->offset();
|
||||
return module_->trackPerfProfiledFunction(name, startCodeOffset, endCodeOffset, lineno, columnIndex);
|
||||
}
|
||||
|
||||
bool trackPerfProfiledBlocks(AsmJSPerfSpewer &perfSpewer, const Func &func, unsigned endCodeOffset) {
|
||||
JSAtom *name = FunctionName(func.fn());
|
||||
unsigned startCodeOffset = func.codeLabel()->offset();
|
||||
perfSpewer.noteBlocksOffsets(masm_);
|
||||
return module_->trackPerfProfiledBlocks(name, startCodeOffset, endCodeOffset, perfSpewer.basicBlocks());
|
||||
}
|
||||
#endif
|
||||
|
||||
void setFirstPassComplete() {
|
||||
|
@ -1610,7 +1618,7 @@ class FunctionCompiler
|
|||
labeledContinues_(m.cx())
|
||||
{}
|
||||
|
||||
bool init()
|
||||
bool init(ParseNode *pn)
|
||||
{
|
||||
if (!unlabeledBreaks_.init() ||
|
||||
!unlabeledContinues_.init() ||
|
||||
|
@ -1620,7 +1628,7 @@ class FunctionCompiler
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!newBlock(/* pred = */ NULL, &curBlock_))
|
||||
if (!newBlock(/* pred = */ NULL, &curBlock_, pn))
|
||||
return false;
|
||||
|
||||
curBlock_->add(MAsmJSCheckOverRecursed::New(&m_.stackOverflowLabel()));
|
||||
|
@ -2032,14 +2040,14 @@ class FunctionCompiler
|
|||
curBlock_ = NULL;
|
||||
}
|
||||
|
||||
bool branchAndStartThen(MDefinition *cond, MBasicBlock **thenBlock, MBasicBlock **elseBlock)
|
||||
bool branchAndStartThen(MDefinition *cond, MBasicBlock **thenBlock, MBasicBlock **elseBlock, ParseNode *thenPn, ParseNode* elsePn)
|
||||
{
|
||||
if (!curBlock_) {
|
||||
*thenBlock = NULL;
|
||||
*elseBlock = NULL;
|
||||
return true;
|
||||
}
|
||||
if (!newBlock(curBlock_, thenBlock) || !newBlock(curBlock_, elseBlock))
|
||||
if (!newBlock(curBlock_, thenBlock, thenPn) || !newBlock(curBlock_, elseBlock, elsePn))
|
||||
return false;
|
||||
curBlock_->end(MTest::New(cond, *thenBlock, *elseBlock));
|
||||
curBlock_ = *thenBlock;
|
||||
|
@ -2073,13 +2081,13 @@ class FunctionCompiler
|
|||
mirGraph().moveBlockToEnd(curBlock_);
|
||||
}
|
||||
|
||||
bool joinIfElse(const BlockVector &thenBlocks)
|
||||
bool joinIfElse(const BlockVector &thenBlocks, ParseNode *pn)
|
||||
{
|
||||
if (!curBlock_ && thenBlocks.empty())
|
||||
return true;
|
||||
MBasicBlock *pred = curBlock_ ? curBlock_ : thenBlocks[0];
|
||||
MBasicBlock *join;
|
||||
if (!newBlock(pred, &join))
|
||||
if (!newBlock(pred, &join, pn))
|
||||
return false;
|
||||
if (curBlock_)
|
||||
curBlock_->end(MGoto::New(join));
|
||||
|
@ -2108,7 +2116,7 @@ class FunctionCompiler
|
|||
return curBlock_->pop();
|
||||
}
|
||||
|
||||
bool startPendingLoop(ParseNode *pn, MBasicBlock **loopEntry)
|
||||
bool startPendingLoop(ParseNode *pn, MBasicBlock **loopEntry, ParseNode *bodyStmt)
|
||||
{
|
||||
if (!loopStack_.append(pn) || !breakableStack_.append(pn))
|
||||
return false;
|
||||
|
@ -2121,13 +2129,14 @@ class FunctionCompiler
|
|||
if (!*loopEntry)
|
||||
return false;
|
||||
mirGraph().addBlock(*loopEntry);
|
||||
noteBasicBlockPosition(*loopEntry, bodyStmt);
|
||||
(*loopEntry)->setLoopDepth(loopStack_.length());
|
||||
curBlock_->end(MGoto::New(*loopEntry));
|
||||
curBlock_ = *loopEntry;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool branchAndStartLoopBody(MDefinition *cond, MBasicBlock **afterLoop)
|
||||
bool branchAndStartLoopBody(MDefinition *cond, MBasicBlock **afterLoop, ParseNode *bodyPn, ParseNode *afterPn)
|
||||
{
|
||||
if (!curBlock_) {
|
||||
*afterLoop = NULL;
|
||||
|
@ -2135,13 +2144,13 @@ class FunctionCompiler
|
|||
}
|
||||
JS_ASSERT(curBlock_->loopDepth() > 0);
|
||||
MBasicBlock *body;
|
||||
if (!newBlock(curBlock_, &body))
|
||||
if (!newBlock(curBlock_, &body, bodyPn))
|
||||
return false;
|
||||
if (cond->isConstant() && ToBoolean(cond->toConstant()->value())) {
|
||||
*afterLoop = NULL;
|
||||
curBlock_->end(MGoto::New(body));
|
||||
} else {
|
||||
if (!newBlockWithDepth(curBlock_, curBlock_->loopDepth() - 1, afterLoop))
|
||||
if (!newBlockWithDepth(curBlock_, curBlock_->loopDepth() - 1, afterLoop, afterPn))
|
||||
return false;
|
||||
curBlock_->end(MTest::New(cond, body, *afterLoop));
|
||||
}
|
||||
|
@ -2182,7 +2191,7 @@ class FunctionCompiler
|
|||
return bindUnlabeledBreaks(pn);
|
||||
}
|
||||
|
||||
bool branchAndCloseDoWhileLoop(MDefinition *cond, MBasicBlock *loopEntry)
|
||||
bool branchAndCloseDoWhileLoop(MDefinition *cond, MBasicBlock *loopEntry, ParseNode *afterLoopStmt)
|
||||
{
|
||||
ParseNode *pn = popLoop();
|
||||
if (!loopEntry) {
|
||||
|
@ -2200,14 +2209,14 @@ class FunctionCompiler
|
|||
curBlock_ = NULL;
|
||||
} else {
|
||||
MBasicBlock *afterLoop;
|
||||
if (!newBlock(curBlock_, &afterLoop))
|
||||
if (!newBlock(curBlock_, &afterLoop, afterLoopStmt))
|
||||
return false;
|
||||
curBlock_->end(MGoto::New(afterLoop));
|
||||
curBlock_ = afterLoop;
|
||||
}
|
||||
} else {
|
||||
MBasicBlock *afterLoop;
|
||||
if (!newBlock(curBlock_, &afterLoop))
|
||||
if (!newBlock(curBlock_, &afterLoop, afterLoopStmt))
|
||||
return false;
|
||||
curBlock_->end(MTest::New(cond, loopEntry, afterLoop));
|
||||
loopEntry->setBackedge(curBlock_);
|
||||
|
@ -2221,17 +2230,17 @@ class FunctionCompiler
|
|||
{
|
||||
bool createdJoinBlock = false;
|
||||
if (UnlabeledBlockMap::Ptr p = unlabeledContinues_.lookup(pn)) {
|
||||
if (!bindBreaksOrContinues(&p->value, &createdJoinBlock))
|
||||
if (!bindBreaksOrContinues(&p->value, &createdJoinBlock, pn))
|
||||
return false;
|
||||
unlabeledContinues_.remove(p);
|
||||
}
|
||||
return bindLabeledBreaksOrContinues(maybeLabels, &labeledContinues_, &createdJoinBlock);
|
||||
return bindLabeledBreaksOrContinues(maybeLabels, &labeledContinues_, &createdJoinBlock, pn);
|
||||
}
|
||||
|
||||
bool bindLabeledBreaks(const LabelVector *maybeLabels)
|
||||
bool bindLabeledBreaks(const LabelVector *maybeLabels, ParseNode *pn)
|
||||
{
|
||||
bool createdJoinBlock = false;
|
||||
return bindLabeledBreaksOrContinues(maybeLabels, &labeledBreaks_, &createdJoinBlock);
|
||||
return bindLabeledBreaksOrContinues(maybeLabels, &labeledBreaks_, &createdJoinBlock, pn);
|
||||
}
|
||||
|
||||
bool addBreak(PropertyName *maybeLabel) {
|
||||
|
@ -2261,13 +2270,13 @@ class FunctionCompiler
|
|||
return true;
|
||||
}
|
||||
|
||||
bool startSwitchCase(MBasicBlock *switchBlock, MBasicBlock **next)
|
||||
bool startSwitchCase(MBasicBlock *switchBlock, MBasicBlock **next, ParseNode *pn)
|
||||
{
|
||||
if (!switchBlock) {
|
||||
*next = NULL;
|
||||
return true;
|
||||
}
|
||||
if (!newBlock(switchBlock, next))
|
||||
if (!newBlock(switchBlock, next, pn))
|
||||
return false;
|
||||
if (curBlock_) {
|
||||
curBlock_->end(MGoto::New(*next));
|
||||
|
@ -2277,16 +2286,16 @@ class FunctionCompiler
|
|||
return true;
|
||||
}
|
||||
|
||||
bool startSwitchDefault(MBasicBlock *switchBlock, BlockVector *cases, MBasicBlock **defaultBlock)
|
||||
bool startSwitchDefault(MBasicBlock *switchBlock, BlockVector *cases, MBasicBlock **defaultBlock, ParseNode *pn)
|
||||
{
|
||||
if (!startSwitchCase(switchBlock, defaultBlock))
|
||||
if (!startSwitchCase(switchBlock, defaultBlock, pn))
|
||||
return false;
|
||||
if (!*defaultBlock)
|
||||
return true;
|
||||
for (unsigned i = 0; i < cases->length(); i++) {
|
||||
if (!(*cases)[i]) {
|
||||
MBasicBlock *bb;
|
||||
if (!newBlock(switchBlock, &bb))
|
||||
if (!newBlock(switchBlock, &bb, NULL))
|
||||
return false;
|
||||
bb->end(MGoto::New(*defaultBlock));
|
||||
(*defaultBlock)->addPredecessor(bb);
|
||||
|
@ -2308,7 +2317,7 @@ class FunctionCompiler
|
|||
mir->addCase(cases[i]);
|
||||
if (curBlock_) {
|
||||
MBasicBlock *next;
|
||||
if (!newBlock(curBlock_, &next))
|
||||
if (!newBlock(curBlock_, &next, pn))
|
||||
return false;
|
||||
curBlock_->end(MGoto::New(next));
|
||||
curBlock_ = next;
|
||||
|
@ -2318,22 +2327,35 @@ class FunctionCompiler
|
|||
|
||||
/*************************************************************************/
|
||||
private:
|
||||
bool newBlockWithDepth(MBasicBlock *pred, unsigned loopDepth, MBasicBlock **block)
|
||||
void noteBasicBlockPosition(MBasicBlock *blk, ParseNode *pn)
|
||||
{
|
||||
#if defined(JS_ION_PERF)
|
||||
if (pn) {
|
||||
unsigned line = 0U, column = 0U;
|
||||
m().tokenStream().srcCoords.lineNumAndColumnIndex(pn->pn_pos.begin, &line, &column);
|
||||
blk->setLineno(line);
|
||||
blk->setColumnIndex(column);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool newBlockWithDepth(MBasicBlock *pred, unsigned loopDepth, MBasicBlock **block, ParseNode *pn)
|
||||
{
|
||||
*block = MBasicBlock::New(mirGraph(), info(), pred, /* pc = */ NULL, MBasicBlock::NORMAL);
|
||||
if (!*block)
|
||||
return false;
|
||||
noteBasicBlockPosition(*block, pn);
|
||||
mirGraph().addBlock(*block);
|
||||
(*block)->setLoopDepth(loopDepth);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool newBlock(MBasicBlock *pred, MBasicBlock **block)
|
||||
bool newBlock(MBasicBlock *pred, MBasicBlock **block, ParseNode *pn)
|
||||
{
|
||||
return newBlockWithDepth(pred, loopStack_.length(), block);
|
||||
return newBlockWithDepth(pred, loopStack_.length(), block, pn);
|
||||
}
|
||||
|
||||
bool bindBreaksOrContinues(BlockVector *preds, bool *createdJoinBlock)
|
||||
bool bindBreaksOrContinues(BlockVector *preds, bool *createdJoinBlock, ParseNode *pn)
|
||||
{
|
||||
for (unsigned i = 0; i < preds->length(); i++) {
|
||||
MBasicBlock *pred = (*preds)[i];
|
||||
|
@ -2342,7 +2364,7 @@ class FunctionCompiler
|
|||
curBlock_->addPredecessor(pred);
|
||||
} else {
|
||||
MBasicBlock *next;
|
||||
if (!newBlock(pred, &next))
|
||||
if (!newBlock(pred, &next, pn))
|
||||
return false;
|
||||
pred->end(MGoto::New(next));
|
||||
if (curBlock_) {
|
||||
|
@ -2359,14 +2381,14 @@ class FunctionCompiler
|
|||
}
|
||||
|
||||
bool bindLabeledBreaksOrContinues(const LabelVector *maybeLabels, LabeledBlockMap *map,
|
||||
bool *createdJoinBlock)
|
||||
bool *createdJoinBlock, ParseNode *pn)
|
||||
{
|
||||
if (!maybeLabels)
|
||||
return true;
|
||||
const LabelVector &labels = *maybeLabels;
|
||||
for (unsigned i = 0; i < labels.length(); i++) {
|
||||
if (LabeledBlockMap::Ptr p = map->lookup(labels[i])) {
|
||||
if (!bindBreaksOrContinues(&p->value, createdJoinBlock))
|
||||
if (!bindBreaksOrContinues(&p->value, createdJoinBlock, pn))
|
||||
return false;
|
||||
map->remove(p);
|
||||
}
|
||||
|
@ -2395,7 +2417,7 @@ class FunctionCompiler
|
|||
{
|
||||
bool createdJoinBlock = false;
|
||||
if (UnlabeledBlockMap::Ptr p = unlabeledBreaks_.lookup(pn)) {
|
||||
if (!bindBreaksOrContinues(&p->value, &createdJoinBlock))
|
||||
if (!bindBreaksOrContinues(&p->value, &createdJoinBlock, pn))
|
||||
return false;
|
||||
unlabeledBreaks_.remove(p);
|
||||
}
|
||||
|
@ -3811,7 +3833,7 @@ CheckConditional(FunctionCompiler &f, ParseNode *ternary, MDefinition **def, Typ
|
|||
return f.failf(cond, "%s is not a subtype of int", condType.toChars());
|
||||
|
||||
MBasicBlock *thenBlock, *elseBlock;
|
||||
if (!f.branchAndStartThen(condDef, &thenBlock, &elseBlock))
|
||||
if (!f.branchAndStartThen(condDef, &thenBlock, &elseBlock, thenExpr, elseExpr))
|
||||
return false;
|
||||
|
||||
MDefinition *thenDef;
|
||||
|
@ -3832,7 +3854,10 @@ CheckConditional(FunctionCompiler &f, ParseNode *ternary, MDefinition **def, Typ
|
|||
return false;
|
||||
|
||||
f.pushPhiInput(elseDef);
|
||||
if (!f.joinIfElse(thenBlocks))
|
||||
|
||||
// next statement is actually not the else expr, but this is the closest stmt to the next
|
||||
// one that is directly reachable
|
||||
if (!f.joinIfElse(thenBlocks, elseExpr))
|
||||
return false;
|
||||
*def = f.popPhiOutput();
|
||||
|
||||
|
@ -4177,7 +4202,7 @@ CheckWhile(FunctionCompiler &f, ParseNode *whileStmt, const LabelVector *maybeLa
|
|||
ParseNode *body = BinaryRight(whileStmt);
|
||||
|
||||
MBasicBlock *loopEntry;
|
||||
if (!f.startPendingLoop(whileStmt, &loopEntry))
|
||||
if (!f.startPendingLoop(whileStmt, &loopEntry, body))
|
||||
return false;
|
||||
|
||||
MDefinition *condDef;
|
||||
|
@ -4189,7 +4214,7 @@ CheckWhile(FunctionCompiler &f, ParseNode *whileStmt, const LabelVector *maybeLa
|
|||
return f.failf(cond, "%s is not a subtype of int", condType.toChars());
|
||||
|
||||
MBasicBlock *afterLoop;
|
||||
if (!f.branchAndStartLoopBody(condDef, &afterLoop))
|
||||
if (!f.branchAndStartLoopBody(condDef, &afterLoop, body, NextNode(whileStmt)))
|
||||
return false;
|
||||
|
||||
if (!CheckStatement(f, body))
|
||||
|
@ -4223,7 +4248,7 @@ CheckFor(FunctionCompiler &f, ParseNode *forStmt, const LabelVector *maybeLabels
|
|||
}
|
||||
|
||||
MBasicBlock *loopEntry;
|
||||
if (!f.startPendingLoop(forStmt, &loopEntry))
|
||||
if (!f.startPendingLoop(forStmt, &loopEntry, body))
|
||||
return false;
|
||||
|
||||
MDefinition *condDef;
|
||||
|
@ -4239,7 +4264,7 @@ CheckFor(FunctionCompiler &f, ParseNode *forStmt, const LabelVector *maybeLabels
|
|||
}
|
||||
|
||||
MBasicBlock *afterLoop;
|
||||
if (!f.branchAndStartLoopBody(condDef, &afterLoop))
|
||||
if (!f.branchAndStartLoopBody(condDef, &afterLoop, body, NextNode(forStmt)))
|
||||
return false;
|
||||
|
||||
if (!CheckStatement(f, body))
|
||||
|
@ -4266,7 +4291,7 @@ CheckDoWhile(FunctionCompiler &f, ParseNode *whileStmt, const LabelVector *maybe
|
|||
ParseNode *cond = BinaryRight(whileStmt);
|
||||
|
||||
MBasicBlock *loopEntry;
|
||||
if (!f.startPendingLoop(whileStmt, &loopEntry))
|
||||
if (!f.startPendingLoop(whileStmt, &loopEntry, body))
|
||||
return false;
|
||||
|
||||
if (!CheckStatement(f, body))
|
||||
|
@ -4283,7 +4308,7 @@ CheckDoWhile(FunctionCompiler &f, ParseNode *whileStmt, const LabelVector *maybe
|
|||
if (!condType.isInt())
|
||||
return f.failf(cond, "%s is not a subtype of int", condType.toChars());
|
||||
|
||||
return f.branchAndCloseDoWhileLoop(condDef, loopEntry);
|
||||
return f.branchAndCloseDoWhileLoop(condDef, loopEntry, NextNode(whileStmt));
|
||||
}
|
||||
|
||||
static bool
|
||||
|
@ -4308,7 +4333,7 @@ CheckLabel(FunctionCompiler &f, ParseNode *labeledStmt, LabelVector *maybeLabels
|
|||
if (!CheckStatement(f, stmt, &labels))
|
||||
return false;
|
||||
|
||||
return f.bindLabeledBreaks(&labels);
|
||||
return f.bindLabeledBreaks(&labels, labeledStmt);
|
||||
}
|
||||
|
||||
static bool
|
||||
|
@ -4320,6 +4345,7 @@ CheckIf(FunctionCompiler &f, ParseNode *ifStmt)
|
|||
// for the entire if/else-if chain).
|
||||
BlockVector thenBlocks(f.cx());
|
||||
|
||||
ParseNode *nextStmt = NextNode(ifStmt);
|
||||
recurse:
|
||||
JS_ASSERT(ifStmt->isKind(PNK_IF));
|
||||
ParseNode *cond = TernaryKid1(ifStmt);
|
||||
|
@ -4335,7 +4361,15 @@ CheckIf(FunctionCompiler &f, ParseNode *ifStmt)
|
|||
return f.failf(cond, "%s is not a subtype of int", condType.toChars());
|
||||
|
||||
MBasicBlock *thenBlock, *elseBlock;
|
||||
if (!f.branchAndStartThen(condDef, &thenBlock, &elseBlock))
|
||||
|
||||
ParseNode *elseBlockStmt = NULL;
|
||||
// The second block given to branchAndStartThen contains either the else statement if
|
||||
// there is one, or the join block; so we need to give the next statement accordingly.
|
||||
elseBlockStmt = elseStmt;
|
||||
if (elseBlockStmt == NULL)
|
||||
elseBlockStmt = nextStmt;
|
||||
|
||||
if (!f.branchAndStartThen(condDef, &thenBlock, &elseBlock, thenStmt, elseBlockStmt))
|
||||
return false;
|
||||
|
||||
if (!CheckStatement(f, thenStmt))
|
||||
|
@ -4357,7 +4391,7 @@ CheckIf(FunctionCompiler &f, ParseNode *ifStmt)
|
|||
if (!CheckStatement(f, elseStmt))
|
||||
return false;
|
||||
|
||||
if (!f.joinIfElse(thenBlocks))
|
||||
if (!f.joinIfElse(thenBlocks, nextStmt))
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -4478,7 +4512,7 @@ CheckSwitch(FunctionCompiler &f, ParseNode *switchStmt)
|
|||
if (cases[caseIndex])
|
||||
return f.fail(stmt, "no duplicate case labels");
|
||||
|
||||
if (!f.startSwitchCase(switchBlock, &cases[caseIndex]))
|
||||
if (!f.startSwitchCase(switchBlock, &cases[caseIndex], stmt))
|
||||
return false;
|
||||
|
||||
if (!CheckStatement(f, CaseBody(stmt)))
|
||||
|
@ -4486,7 +4520,7 @@ CheckSwitch(FunctionCompiler &f, ParseNode *switchStmt)
|
|||
}
|
||||
|
||||
MBasicBlock *defaultBlock;
|
||||
if (!f.startSwitchDefault(switchBlock, &cases, &defaultBlock))
|
||||
if (!f.startSwitchDefault(switchBlock, &cases, &defaultBlock, stmt))
|
||||
return false;
|
||||
|
||||
if (stmt && stmt->isKind(PNK_DEFAULT)) {
|
||||
|
@ -4642,6 +4676,7 @@ CheckFunctionBody(ModuleCompiler &m, ModuleCompiler::Func &func, LifoAlloc &lifo
|
|||
// function head as well as argument type declarations. The ParseNode*
|
||||
// stored in f.body points to the first non-argument statement.
|
||||
ParseNode *stmtIter = func.body();
|
||||
ParseNode *funcBody = stmtIter;
|
||||
|
||||
FunctionCompiler::LocalMap locals(m.cx());
|
||||
if (!locals.init())
|
||||
|
@ -4671,7 +4706,7 @@ CheckFunctionBody(ModuleCompiler &m, ModuleCompiler::Func &func, LifoAlloc &lifo
|
|||
JS_ASSERT(tempAlloc && graph && info && mirGen);
|
||||
|
||||
FunctionCompiler f(m, func, Move(locals), mirGen);
|
||||
if (!f.init())
|
||||
if (!f.init(funcBody))
|
||||
return NULL;
|
||||
|
||||
if (!CheckStatements(f, stmtIter))
|
||||
|
@ -4714,7 +4749,10 @@ GenerateAsmJSCode(ModuleCompiler &m, ModuleCompiler::Func &func,
|
|||
#endif
|
||||
|
||||
#ifdef JS_ION_PERF
|
||||
if (PerfFuncEnabled()) {
|
||||
if (PerfBlockEnabled()) {
|
||||
if (!m.trackPerfProfiledBlocks(mirGen.perfSpewer(), func, m.masm().size()))
|
||||
return false;
|
||||
} else if (PerfFuncEnabled()) {
|
||||
if (!m.trackPerfProfiledFunction(func, m.masm().size()))
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -476,7 +476,7 @@ SendFunctionsToPerf(JSContext *cx, AsmJSModule &module)
|
|||
if (!PerfFuncEnabled())
|
||||
return true;
|
||||
|
||||
PerfSpewer perfSpewer;
|
||||
AsmJSPerfSpewer perfSpewer;
|
||||
|
||||
unsigned long base = (unsigned long) module.functionCode();
|
||||
|
||||
|
@ -500,7 +500,34 @@ SendFunctionsToPerf(JSContext *cx, AsmJSModule &module)
|
|||
unsigned lineno = func.lineno;
|
||||
unsigned columnIndex = func.columnIndex;
|
||||
|
||||
perfSpewer.writeAsmJSProfile(start, size, filename, lineno, columnIndex, method_name);
|
||||
perfSpewer.writeFunctionMap(start, size, filename, lineno, columnIndex, method_name);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
SendBlocksToPerf(JSContext *cx, AsmJSModule &module)
|
||||
{
|
||||
if (!PerfBlockEnabled())
|
||||
return true;
|
||||
|
||||
AsmJSPerfSpewer spewer;
|
||||
unsigned long funcBaseAddress = (unsigned long) module.functionCode();
|
||||
|
||||
const AsmJSModule::PostLinkFailureInfo &info = module.postLinkFailureInfo();
|
||||
const char *filename = const_cast<char *>(info.scriptSource_->filename());
|
||||
|
||||
for (unsigned i = 0; i < module.numPerfBlocksFunctions(); i++) {
|
||||
const AsmJSModule::ProfiledBlocksFunction &func = module.perfProfiledBlocksFunction(i);
|
||||
|
||||
unsigned long size = (unsigned long)func.endCodeOffset - (unsigned long)func.startCodeOffset;
|
||||
JSAutoByteString bytes;
|
||||
const char *method_name = js_AtomToPrintableString(cx, func.name, &bytes);
|
||||
if (!method_name)
|
||||
return false;
|
||||
|
||||
spewer.writeBlocksMap(funcBaseAddress, func.startCodeOffset, size, filename, method_name, func.blocks);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -528,6 +555,8 @@ js::LinkAsmJS(JSContext *cx, unsigned argc, JS::Value *vp)
|
|||
#endif
|
||||
|
||||
#if defined(JS_ION_PERF)
|
||||
if (!SendBlocksToPerf(cx, module))
|
||||
return false;
|
||||
if (!SendFunctionsToPerf(cx, module))
|
||||
return false;
|
||||
#endif
|
||||
|
|
|
@ -15,6 +15,10 @@
|
|||
#include "jsscript.h"
|
||||
#include "jstypedarrayinlines.h"
|
||||
|
||||
#if defined(JS_ION_PERF)
|
||||
# include "ion/PerfSpewer.h"
|
||||
#endif
|
||||
|
||||
#include "ion/IonMacroAssembler.h"
|
||||
|
||||
namespace js {
|
||||
|
@ -301,6 +305,21 @@ class AsmJSModule
|
|||
};
|
||||
#endif
|
||||
|
||||
#if defined(JS_ION_PERF)
|
||||
struct ProfiledBlocksFunction : public ProfiledFunction
|
||||
{
|
||||
ion::PerfSpewer::BasicBlocksVector blocks;
|
||||
|
||||
ProfiledBlocksFunction(JSAtom *name, unsigned start, unsigned end, ion::PerfSpewer::BasicBlocksVector &blocksVector)
|
||||
: ProfiledFunction(name, start, end), blocks(Move(blocksVector))
|
||||
{ }
|
||||
|
||||
ProfiledBlocksFunction(const ProfiledBlocksFunction ©)
|
||||
: ProfiledFunction(copy.name, copy.startCodeOffset, copy.endCodeOffset), blocks(Move(copy.blocks))
|
||||
{ }
|
||||
};
|
||||
#endif
|
||||
|
||||
// If linking fails, we recompile the function as if it's ordinary JS.
|
||||
// This struct holds the data required to do this.
|
||||
struct PostLinkFailureInfo
|
||||
|
@ -359,6 +378,7 @@ class AsmJSModule
|
|||
#endif
|
||||
#if defined(JS_ION_PERF)
|
||||
ProfiledFunctionVector perfProfiledFunctions_;
|
||||
Vector<ProfiledBlocksFunction, 0, SystemAllocPolicy> perfProfiledBlocksFunctions_;
|
||||
#endif
|
||||
|
||||
uint32_t numGlobalVars_;
|
||||
|
@ -521,6 +541,17 @@ class AsmJSModule
|
|||
const ProfiledFunction &perfProfiledFunction(unsigned i) const {
|
||||
return perfProfiledFunctions_[i];
|
||||
}
|
||||
|
||||
bool trackPerfProfiledBlocks(JSAtom *name, unsigned startCodeOffset, unsigned endCodeOffset, ion::PerfSpewer::BasicBlocksVector &basicBlocks) {
|
||||
ProfiledBlocksFunction func(name, startCodeOffset, endCodeOffset, basicBlocks);
|
||||
return perfProfiledBlocksFunctions_.append(func);
|
||||
}
|
||||
unsigned numPerfBlocksFunctions() const {
|
||||
return perfProfiledBlocksFunctions_.length();
|
||||
}
|
||||
const ProfiledBlocksFunction perfProfiledBlocksFunction(unsigned i) const {
|
||||
return perfProfiledBlocksFunctions_[i];
|
||||
}
|
||||
#endif
|
||||
bool hasArrayView() const {
|
||||
return hasArrayView_;
|
||||
|
|
|
@ -2573,6 +2573,12 @@ CodeGenerator::generateBody()
|
|||
{
|
||||
IonScriptCounts *counts = maybeCreateScriptCounts();
|
||||
|
||||
#if defined(JS_ION_PERF)
|
||||
PerfSpewer *perfSpewer = &perfSpewer_;
|
||||
if (gen->compilingAsmJS())
|
||||
perfSpewer = &gen->perfSpewer();
|
||||
#endif
|
||||
|
||||
for (size_t i = 0; i < graph.numBlocks(); i++) {
|
||||
current = graph.getBlock(i);
|
||||
|
||||
|
@ -2591,8 +2597,9 @@ CodeGenerator::generateBody()
|
|||
return false;
|
||||
}
|
||||
|
||||
if (PerfBlockEnabled())
|
||||
perfSpewer_.startBasicBlock(current->mir(), masm);
|
||||
#if defined(JS_ION_PERF)
|
||||
perfSpewer->startBasicBlock(current->mir(), masm);
|
||||
#endif
|
||||
|
||||
for (; iter != current->end(); iter++) {
|
||||
IonSpew(IonSpew_Codegen, "instruction %s", iter->opName());
|
||||
|
@ -2614,8 +2621,9 @@ CodeGenerator::generateBody()
|
|||
if (masm.oom())
|
||||
return false;
|
||||
|
||||
if (PerfBlockEnabled())
|
||||
perfSpewer_.endBasicBlock(masm);
|
||||
#if defined(JS_ION_PERF)
|
||||
perfSpewer->endBasicBlock(masm);
|
||||
#endif
|
||||
}
|
||||
|
||||
JS_ASSERT(pushedArgumentSlots_.empty());
|
||||
|
|
|
@ -18,6 +18,11 @@
|
|||
#include "ion/CompileInfo.h"
|
||||
#include "ion/RegisterSets.h"
|
||||
|
||||
#if defined(JS_ION_PERF)
|
||||
# include "ion/PerfSpewer.h"
|
||||
#endif
|
||||
|
||||
|
||||
namespace js {
|
||||
namespace ion {
|
||||
|
||||
|
@ -152,6 +157,13 @@ class MIRGenerator
|
|||
AsmJSHeapAccessVector asmJSHeapAccesses_;
|
||||
#endif
|
||||
AsmJSGlobalAccessVector asmJSGlobalAccesses_;
|
||||
|
||||
#if defined(JS_ION_PERF)
|
||||
AsmJSPerfSpewer asmJSPerfSpewer_;
|
||||
|
||||
public:
|
||||
AsmJSPerfSpewer &perfSpewer() { return asmJSPerfSpewer_; }
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace ion
|
||||
|
|
|
@ -245,6 +245,10 @@ MBasicBlock::MBasicBlock(MIRGraph &graph, CompileInfo &info, jsbytecode *pc, Kin
|
|||
numDominated_(0),
|
||||
loopHeader_(NULL),
|
||||
trackedPc_(pc)
|
||||
#if defined (JS_ION_PERF)
|
||||
, lineno_(0u),
|
||||
columnIndex_(0u)
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -510,6 +510,17 @@ class MBasicBlock : public TempObject, public InlineListNode<MBasicBlock>
|
|||
MBasicBlock *loopHeader_;
|
||||
|
||||
jsbytecode *trackedPc_;
|
||||
|
||||
#if defined (JS_ION_PERF)
|
||||
unsigned lineno_;
|
||||
unsigned columnIndex_;
|
||||
|
||||
public:
|
||||
void setLineno(unsigned l) { lineno_ = l; }
|
||||
unsigned lineno() const { return lineno_; }
|
||||
void setColumnIndex(unsigned c) { columnIndex_ = c; }
|
||||
unsigned columnIndex() const { return columnIndex_; }
|
||||
#endif
|
||||
};
|
||||
|
||||
typedef InlineListIterator<MBasicBlock> MBasicBlockIterator;
|
||||
|
|
|
@ -123,23 +123,13 @@ PerfSpewer::startBasicBlock(MBasicBlock *blk,
|
|||
bool
|
||||
PerfSpewer::endBasicBlock(MacroAssembler &masm)
|
||||
{
|
||||
if (!PerfBlockEnabled() || !fp_)
|
||||
return true;
|
||||
|
||||
masm.bind(&basicBlocks_[basicBlocks_.length() - 1].end);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
PerfSpewer::writeAsmJSProfile(unsigned long base, unsigned long size, const char *filename,
|
||||
unsigned lineno, unsigned colIndex, const char *funcName)
|
||||
{
|
||||
if (!fp_ || !PerfFuncEnabled() || size == 0U)
|
||||
return;
|
||||
|
||||
fprintf(fp_,
|
||||
"%lx %lx %s:%d:%d: Function %s\n",
|
||||
base, size,
|
||||
filename, lineno, colIndex, funcName);
|
||||
}
|
||||
|
||||
void
|
||||
PerfSpewer::writeProfile(JSScript *script,
|
||||
IonCode *code,
|
||||
|
@ -205,3 +195,97 @@ PerfSpewer::writeProfile(JSScript *script,
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(JS_ION_PERF)
|
||||
void
|
||||
AsmJSPerfSpewer::writeFunctionMap(unsigned long base, unsigned long size, const char *filename, unsigned lineno, unsigned colIndex, const char *funcName)
|
||||
{
|
||||
if (!fp_ || !PerfFuncEnabled() || size == 0U)
|
||||
return;
|
||||
|
||||
fprintf(fp_,
|
||||
"%lx %lx %s:%d:%d: Function %s\n",
|
||||
base, size,
|
||||
filename, lineno, colIndex, funcName);
|
||||
}
|
||||
|
||||
bool
|
||||
AsmJSPerfSpewer::startBasicBlock(MBasicBlock *blk, MacroAssembler &masm)
|
||||
{
|
||||
if (!PerfBlockEnabled() || !fp_)
|
||||
return true;
|
||||
|
||||
Record r("", blk->lineno(), blk->columnIndex(), blk->id()); // filename is retrieved later
|
||||
masm.bind(&r.start);
|
||||
return basicBlocks_.append(r);
|
||||
}
|
||||
|
||||
void
|
||||
AsmJSPerfSpewer::noteBlocksOffsets(MacroAssembler &masm)
|
||||
{
|
||||
if (!PerfBlockEnabled() || !fp_)
|
||||
return;
|
||||
|
||||
for (uint32_t i = 0; i < basicBlocks_.length(); i++) {
|
||||
Record &r = basicBlocks_[i];
|
||||
r.startOffset = masm.actualOffset(r.start.offset());
|
||||
r.endOffset = masm.actualOffset(r.end.offset());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AsmJSPerfSpewer::writeBlocksMap(unsigned long baseAddress, unsigned long funcStartOffset, unsigned long funcSize,
|
||||
const char *filename, const char *funcName, const BasicBlocksVector &basicBlocks)
|
||||
{
|
||||
if (!fp_ || !PerfBlockEnabled() || basicBlocks.length() == 0)
|
||||
return;
|
||||
|
||||
// function begins with the prologue, which is located before the first basic block
|
||||
unsigned long prologueSize = basicBlocks[0].startOffset - funcStartOffset;
|
||||
|
||||
unsigned long cur = baseAddress + funcStartOffset + prologueSize;
|
||||
unsigned long funcEnd = baseAddress + funcStartOffset + funcSize - prologueSize;
|
||||
|
||||
for (uint32_t i = 0; i < basicBlocks.length(); i++) {
|
||||
const Record &r = basicBlocks[i];
|
||||
|
||||
unsigned long blockStart = baseAddress + (unsigned long) r.startOffset;
|
||||
unsigned long blockEnd = baseAddress + (unsigned long) r.endOffset;
|
||||
|
||||
if (i == basicBlocks.length() - 1) {
|
||||
// for the last block, manually add the ret instruction
|
||||
blockEnd += 1u;
|
||||
}
|
||||
|
||||
JS_ASSERT(cur <= blockStart);
|
||||
if (cur < blockStart) {
|
||||
fprintf(fp_,
|
||||
"%lx %lx %s: Function %s - unknown block\n",
|
||||
cur, blockStart - cur,
|
||||
filename,
|
||||
funcName);
|
||||
}
|
||||
cur = blockEnd;
|
||||
|
||||
unsigned long size = blockEnd - blockStart;
|
||||
if (size > 0) {
|
||||
fprintf(fp_,
|
||||
"%lx %lx %s:%d:%d: Function %s - Block %d\n",
|
||||
blockStart, size,
|
||||
filename, r.lineNumber, r.columnNumber,
|
||||
funcName, r.id);
|
||||
}
|
||||
}
|
||||
|
||||
// Any stuff after the basic blocks is presumably OOL code,
|
||||
// which I do not currently categorize.
|
||||
JS_ASSERT(cur <= funcEnd);
|
||||
if (cur < funcEnd) {
|
||||
fprintf(fp_,
|
||||
"%lx %lx %s: Function %s - OOL\n",
|
||||
cur, funcEnd - cur,
|
||||
filename,
|
||||
funcName);
|
||||
}
|
||||
}
|
||||
#endif // defined (JS_ION_PERF)
|
||||
|
|
|
@ -37,39 +37,57 @@ static inline bool PerfEnabled() { return false; }
|
|||
|
||||
class PerfSpewer
|
||||
{
|
||||
private:
|
||||
protected:
|
||||
static uint32_t nextFunctionIndex;
|
||||
FILE *fp_;
|
||||
|
||||
public:
|
||||
struct Record {
|
||||
const char *filename;
|
||||
unsigned lineNumber;
|
||||
unsigned columnNumber;
|
||||
uint32_t id;
|
||||
Label start, end;
|
||||
unsigned startOffset, endOffset;
|
||||
|
||||
Record(const char *filename,
|
||||
unsigned lineNumber,
|
||||
unsigned columnNumber,
|
||||
uint32_t id)
|
||||
: filename(filename), lineNumber(lineNumber),
|
||||
columnNumber(columnNumber), id(id)
|
||||
columnNumber(columnNumber), id(id),
|
||||
startOffset(0u), endOffset(0u)
|
||||
{}
|
||||
};
|
||||
|
||||
FILE *fp_;
|
||||
Vector<Record, 1, SystemAllocPolicy> basicBlocks_;
|
||||
typedef Vector<Record, 1, SystemAllocPolicy> BasicBlocksVector;
|
||||
protected:
|
||||
BasicBlocksVector basicBlocks_;
|
||||
|
||||
public:
|
||||
PerfSpewer();
|
||||
~PerfSpewer();
|
||||
virtual ~PerfSpewer();
|
||||
|
||||
bool startBasicBlock(MBasicBlock *blk, MacroAssembler &masm);
|
||||
virtual bool startBasicBlock(MBasicBlock *blk, MacroAssembler &masm);
|
||||
bool endBasicBlock(MacroAssembler &masm);
|
||||
void writeProfile(JSScript *script,
|
||||
IonCode *code,
|
||||
MacroAssembler &masm);
|
||||
void writeAsmJSProfile(unsigned long base, unsigned long size, const char *filename,
|
||||
unsigned lineno, unsigned colIndex, const char *funcName);
|
||||
};
|
||||
|
||||
class AsmJSPerfSpewer : public PerfSpewer
|
||||
{
|
||||
public:
|
||||
bool startBasicBlock(MBasicBlock *blk, MacroAssembler &masm);
|
||||
|
||||
void noteBlocksOffsets(MacroAssembler &masm);
|
||||
BasicBlocksVector &basicBlocks() { return basicBlocks_; }
|
||||
|
||||
void writeBlocksMap(unsigned long baseAddress, unsigned long funcStartOffset,
|
||||
unsigned long funcSize, const char *filename, const char *funcName,
|
||||
const BasicBlocksVector &basicBlocks);
|
||||
void writeFunctionMap(unsigned long base, unsigned long size, const char *filename,
|
||||
unsigned lineno, unsigned colIndex, const char *funcName);
|
||||
};
|
||||
|
||||
} // namespace ion
|
||||
|
|
Загрузка…
Ссылка в новой задаче