Bug 884473: Integrate perf with OdinMonkey: basic blocks profiling; r=sstangl

This commit is contained in:
Benjamin Bouvier 2013-07-01 16:35:25 -07:00
parent fedb281b89
commit 1c44ac2ae6
9 changed files with 308 additions and 73 deletions

View File

@ -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;
}

View File

@ -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

View File

@ -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 &copy)
: 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_;

View File

@ -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());

View File

@ -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

View File

@ -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
{
}

View File

@ -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;

View File

@ -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)

View File

@ -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