Bug 881882 - OdinMonkey: report compile time and slow functions in asm.js success message (r=sstangl)

--HG--
extra : rebase_source : b72090feaf9e250e5085dcb8731edf4a1ff0daec
This commit is contained in:
Luke Wagner 2013-06-12 16:29:47 -07:00
parent 31025066ec
commit 4b8935fe96
4 changed files with 98 additions and 11 deletions

View File

@ -932,6 +932,7 @@ class MOZ_STACK_CLASS ModuleCompiler
MIRTypeVector argTypes_;
RetType returnType_;
mutable Label code_;
unsigned compileTime_;
public:
Func(ParseNode *fn, ParseNode *body, MoveRef<MIRTypeVector> types, RetType returnType)
@ -939,7 +940,8 @@ class MOZ_STACK_CLASS ModuleCompiler
body_(body),
argTypes_(types),
returnType_(returnType),
code_()
code_(),
compileTime_(0)
{}
Func(MoveRef<Func> rhs)
@ -947,7 +949,8 @@ class MOZ_STACK_CLASS ModuleCompiler
body_(rhs->body_),
argTypes_(Move(rhs->argTypes_)),
returnType_(rhs->returnType_),
code_(rhs->code_)
code_(rhs->code_),
compileTime_(rhs->compileTime_)
{}
~Func()
@ -964,6 +967,8 @@ class MOZ_STACK_CLASS ModuleCompiler
const MIRTypeVector &argMIRTypes() const { return argTypes_; }
RetType returnType() const { return returnType_; }
Label *codeLabel() const { return &code_; }
unsigned compileTime() const { return compileTime_; }
void accumulateCompileTime(unsigned ms) { compileTime_ += ms; }
};
class Global
@ -1100,10 +1105,19 @@ class MOZ_STACK_CLASS ModuleCompiler
typedef HashMap<ExitDescriptor, unsigned, ExitDescriptor, ContextAllocPolicy> ExitMap;
private:
struct SlowFunction
{
PropertyName *name;
unsigned ms;
unsigned line;
unsigned column;
};
typedef HashMap<PropertyName*, AsmJSMathBuiltin> MathNameMap;
typedef HashMap<PropertyName*, Global> GlobalMap;
typedef Vector<Func> FuncVector;
typedef Vector<AsmJSGlobalAccess> GlobalAccessVector;
typedef Vector<SlowFunction> SlowFunctionVector;
JSContext * cx_;
MacroAssembler masm_;
@ -1123,6 +1137,10 @@ class MOZ_STACK_CLASS ModuleCompiler
char * errorString_;
ParseNode * errorNode_;
int64_t usecBefore_;
SlowFunctionVector slowFunctions_;
TokenStream & tokenStream_;
DebugOnly<int> currentPass_;
@ -1147,6 +1165,8 @@ class MOZ_STACK_CLASS ModuleCompiler
globalAccesses_(cx),
errorString_(NULL),
errorNode_(NULL),
usecBefore_(PRMJ_Now()),
slowFunctions_(cx),
tokenStream_(ts),
currentPass_(1)
{}
@ -1156,7 +1176,7 @@ class MOZ_STACK_CLASS ModuleCompiler
tokenStream_.reportAsmJSError(errorNode_->pn_pos.begin,
JSMSG_USE_ASM_TYPE_FAIL,
errorString_);
JS_smprintf_free(errorString_);
js_free(errorString_);
}
// Avoid spurious Label assertions on compilation failure.
@ -1235,6 +1255,18 @@ class MOZ_STACK_CLASS ModuleCompiler
return false;
}
static const unsigned SLOW_FUNCTION_THRESHOLD_MS = 250;
bool maybeReportCompileTime(ParseNode *fn, unsigned ms) {
if (ms < SLOW_FUNCTION_THRESHOLD_MS)
return true;
SlowFunction sf;
sf.name = FunctionName(fn);
sf.ms = ms;
tokenStream_.srcCoords.lineNumAndColumnIndex(fn->pn_pos.begin, &sf.line, &sf.column);
return slowFunctions_.append(sf);
}
/*************************************************** Read-only interface */
JSContext *cx() const { return cx_; }
@ -1456,6 +1488,31 @@ class MOZ_STACK_CLASS ModuleCompiler
module_->exportedFunction(exportIndex).initCodeOffset(masm_.size());
}
void buildCompilationTimeReport(ScopedJSFreePtr<char> *out) {
int64_t usecAfter = PRMJ_Now();
int msTotal = (usecAfter - usecBefore_) / PRMJ_USEC_PER_MSEC;
ScopedJSFreePtr<char> slowFuns;
if (!slowFunctions_.empty()) {
slowFuns.reset(JS_smprintf("; %d functions compiled slowly: ", slowFunctions_.length()));
if (!slowFuns)
return;
for (unsigned i = 0; i < slowFunctions_.length(); i++) {
SlowFunction &func = slowFunctions_[i];
JSAutoByteString name;
if (!js_AtomToPrintableString(cx_, func.name, &name))
return;
slowFuns.reset(JS_smprintf("%s%s:%u:%u (%ums, %g%%)%s", slowFuns.get(),
name.ptr(), func.line, func.column, func.ms,
double(func.ms)/double(msTotal),
i+1 < slowFunctions_.length() ? ", " : ""));
if (!slowFuns)
return;
}
}
out->reset(JS_smprintf("total compilation time %dms%s",
msTotal, slowFuns ? slowFuns.get() : ""));
}
bool finish(ScopedJSDeletePtr<AsmJSModule> *module) {
// After finishing, the only valid operation on an ModuleCompiler is
// destruction.
@ -4633,6 +4690,8 @@ CheckVariableDecls(ModuleCompiler &m, FunctionCompiler::LocalMap *locals, ParseN
static MIRGenerator *
CheckFunctionBody(ModuleCompiler &m, ModuleCompiler::Func &func, LifoAlloc &lifo)
{
int64_t before = PRMJ_Now();
// CheckFunctionSignature already has already checked the
// function head as well as argument type declarations. The ParseNode*
// stored in f.body points to the first non-argument statement.
@ -4675,6 +4734,8 @@ CheckFunctionBody(ModuleCompiler &m, ModuleCompiler::Func &func, LifoAlloc &lifo
f.returnVoid();
JS_ASSERT(!tempAlloc->rootList());
func.accumulateCompileTime((PRMJ_Now() - before) / PRMJ_USEC_PER_MSEC);
return mirGen;
}
@ -4682,6 +4743,8 @@ static bool
GenerateAsmJSCode(ModuleCompiler &m, ModuleCompiler::Func &func,
MIRGenerator &mirGen, LIRGraph &lir)
{
int64_t before = PRMJ_Now();
m.masm().bind(func.codeLabel());
ScopedJSDeletePtr<CodeGenerator> codegen(GenerateCode(&mirGen, &lir, &m.masm()));
@ -4714,6 +4777,10 @@ GenerateAsmJSCode(ModuleCompiler &m, ModuleCompiler::Func &func,
// Align internal function headers.
m.masm().align(CodeAlignment);
func.accumulateCompileTime((PRMJ_Now() - before) / PRMJ_USEC_PER_MSEC);
if (!m.maybeReportCompileTime(func.fn(), func.compileTime()))
return false;
// Unlike regular IonMonkey which links and generates a new IonCode for
// every function, we accumulate all the functions in the module in a
// single MacroAssembler and link at end. Linking asm.js doesn't require a
@ -4743,6 +4810,8 @@ CheckFunctionBodiesSequential(ModuleCompiler &m)
IonContext icx(m.cx()->compartment(), &mirGen->temp());
int64_t before = PRMJ_Now();
if (!OptimizeMIR(mirGen))
return m.fail(func.fn(), "internal compiler failure (probably out of memory)");
@ -4750,6 +4819,8 @@ CheckFunctionBodiesSequential(ModuleCompiler &m)
if (!lir)
return m.fail(func.fn(), "internal compiler failure (probably out of memory)");
func.accumulateCompileTime((PRMJ_Now() - before) / PRMJ_USEC_PER_MSEC);
if (!GenerateAsmJSCode(m, func, *mirGen, *lir))
return false;
@ -4798,8 +4869,12 @@ GenerateCodeForFinishedJob(ModuleCompiler &m, ParallelGroupState &group, AsmJSPa
if (!task)
return false;
ModuleCompiler::Func &func = m.function(task->funcNum);
func.accumulateCompileTime(task->compileTime);
// Perform code generation on the main thread.
if (!GenerateAsmJSCode(m, m.function(task->funcNum), *task->mir, *task->lir))
if (!GenerateAsmJSCode(m, func, *task->mir, *task->lir))
return false;
group.compiledJobs++;
@ -5996,7 +6071,8 @@ GenerateExits(ModuleCompiler &m)
}
static bool
CheckModule(JSContext *cx, TokenStream &ts, ParseNode *fn, ScopedJSDeletePtr<AsmJSModule> *module)
CheckModule(JSContext *cx, TokenStream &ts, ParseNode *fn, ScopedJSDeletePtr<AsmJSModule> *module,
ScopedJSFreePtr<char> *compilationTimeReport)
{
ModuleCompiler m(cx, ts);
if (!m.init())
@ -6057,11 +6133,15 @@ CheckModule(JSContext *cx, TokenStream &ts, ParseNode *fn, ScopedJSDeletePtr<Asm
if (!GenerateExits(m))
return false;
return m.finish(module);
if (!m.finish(module))
return false;
m.buildCompilationTimeReport(compilationTimeReport);
return true;
}
static bool
Warn(JSContext *cx, int code, const char *str = NULL)
Warn(JSContext *cx, int code, const char *str)
{
return JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING, js_GetErrorMessage,
NULL, code, str);
@ -6094,8 +6174,9 @@ js::CompileAsmJS(JSContext *cx, TokenStream &ts, ParseNode *fn, const CompileOpt
}
# endif
ScopedJSFreePtr<char> compilationTimeReport;
ScopedJSDeletePtr<AsmJSModule> module;
if (!CheckModule(cx, ts, fn, &module))
if (!CheckModule(cx, ts, fn, &module, &compilationTimeReport))
return !cx->isExceptionPending();
module->initPostLinkFailureInfo(options, scriptSource, bufStart, bufEnd);
@ -6118,7 +6199,7 @@ js::CompileAsmJS(JSContext *cx, TokenStream &ts, ParseNode *fn, const CompileOpt
SetAsmJSModuleObject(moduleFun, moduleObj);
return Warn(cx, JSMSG_USE_ASM_TYPE_OK);
return Warn(cx, JSMSG_USE_ASM_TYPE_OK, compilationTimeReport);
}
JSBool

View File

@ -139,10 +139,11 @@ struct AsmJSParallelTask
uint32_t funcNum; // Index |i| of function in |Module.function(i)|.
ion::MIRGenerator *mir; // Passed from main thread to worker.
ion::LIRGraph *lir; // Passed from worker to main thread.
unsigned compileTime;
AsmJSParallelTask(size_t defaultChunkSize)
: lifo(defaultChunkSize),
funcNum(0), mir(NULL), lir(NULL)
funcNum(0), mir(NULL), lir(NULL), compileTime(0)
{ }
void init(uint32_t newFuncNum, ion::MIRGenerator *newMir) {

View File

@ -394,7 +394,7 @@ MSG_DEF(JSMSG_CURLY_AFTER_MODULE, 340, 0, JSEXN_SYNTAXERR, "missing } after
MSG_DEF(JSMSG_USE_ASM_DIRECTIVE_FAIL, 341, 0, JSEXN_SYNTAXERR, "'use asm' directive only works on function code")
MSG_DEF(JSMSG_USE_ASM_TYPE_FAIL, 342, 1, JSEXN_TYPEERR, "asm.js type error: {0}")
MSG_DEF(JSMSG_USE_ASM_LINK_FAIL, 343, 1, JSEXN_TYPEERR, "asm.js link error: {0}")
MSG_DEF(JSMSG_USE_ASM_TYPE_OK, 344, 0, JSEXN_ERR, "successfully compiled asm.js code")
MSG_DEF(JSMSG_USE_ASM_TYPE_OK, 344, 1, JSEXN_ERR, "successfully compiled asm.js code ({0})")
MSG_DEF(JSMSG_BAD_ARROW_ARGS, 345, 0, JSEXN_SYNTAXERR, "invalid arrow-function arguments (parentheses around the arrow-function may help)")
MSG_DEF(JSMSG_YIELD_IN_ARROW, 346, 0, JSEXN_SYNTAXERR, "arrow function may not contain yield")
MSG_DEF(JSMSG_WRONG_VALUE, 347, 2, JSEXN_ERR, "expected {0} but found {1}")

View File

@ -346,6 +346,8 @@ WorkerThread::handleAsmJSWorkload(WorkerThreadState &state)
do {
ion::IonContext icx(asmData->mir->compartment, &asmData->mir->temp());
int64_t before = PRMJ_Now();
if (!OptimizeMIR(asmData->mir))
break;
@ -353,6 +355,9 @@ WorkerThread::handleAsmJSWorkload(WorkerThreadState &state)
if (!asmData->lir)
break;
int64_t after = PRMJ_Now();
asmData->compileTime = (after - before) / PRMJ_USEC_PER_MSEC;
success = true;
} while(0);
state.lock();