Bug 781979 - Part 2: Track the line number for profiling in IonMonkey. r=nbp,djvj

This commit is contained in:
Alex Crichton 2012-08-15 01:16:41 -07:00
parent f3cf0ef464
commit 30c4756c19
25 changed files with 422 additions and 217 deletions

View File

@ -748,9 +748,11 @@ CodeGenerator::visitCallGeneric(LCallGeneric *call)
// Finally call the function in objreg.
masm.bind(&makeCall);
masm.leaveBeforeCall();
masm.callIon(objreg);
if (!markSafepoint(call))
return false;
masm.reenterAfterCall();
// Increment to remove IonFramePrefix; decrement to fill FrameSizeClass.
// The return address has already been removed from the Ion frame.
@ -819,11 +821,13 @@ CodeGenerator::visitCallKnown(LCallKnown *call)
masm.Push(Imm32(call->numActualArgs()));
masm.Push(calleereg);
masm.Push(Imm32(descriptor));
masm.leaveBeforeCall();
// Finally call the function in objreg.
masm.callIon(objreg);
if (!markSafepoint(call))
return false;
masm.reenterAfterCall();
// Increment to remove IonFramePrefix; decrement to fill FrameSizeClass.
// The return address has already been removed from the Ion frame.
@ -1075,11 +1079,13 @@ CodeGenerator::visitApplyArgsGeneric(LApplyArgsGeneric *apply)
}
masm.bind(&rejoin);
masm.leaveBeforeCall();
// Finally call the function in objreg, as assigned by one of the paths above.
masm.callIon(objreg);
if (!markSafepoint(apply))
return false;
masm.reenterAfterCall();
// Recover the number of arguments from the frame descriptor.
masm.movePtr(Address(StackPointer, 0), copyreg);
@ -4041,54 +4047,79 @@ CodeGenerator::visitSetDOMProperty(LSetDOMProperty *ins)
}
bool
CodeGenerator::visitProfilingEnter(LProfilingEnter *lir)
CodeGenerator::visitFunctionBoundary(LFunctionBoundary *lir)
{
#if 0
SPSProfiler *profiler = &gen->compartment->rt->spsProfiler;
JS_ASSERT(profiler->enabled());
Register temp = ToRegister(lir->temp()->output());
const char *string = lir->profileString();
switch (lir->type()) {
case MFunctionBoundary::Inline_Enter:
// Multiple scripts can be inlined at one depth, but there is only
// one Inline_Exit node to signify this. To deal with this, if we
// reach the entry of another inline script on the same level, then
// just reset the sps metadata about the frame. We must balance
// calls to leave()/reenter(), so perform the balance without
// emitting any instrumentation. Technically the previous inline
// call at this same depth has reentered, but the instrumentation
// will be emitted at the common join point for all inlines at the
// same depth.
if (sps.inliningDepth() == lir->inlineLevel()) {
sps.leaveInlineFrame();
sps.skipNextReenter();
sps.reenter(masm, temp);
}
Register size = ToRegister(lir->temp1()->output());
Register base = ToRegister(lir->temp2()->output());
sps.leave(lastPC, masm, temp);
if (!sps.enterInlineFrame())
return false;
// fallthrough
// Check if there's still space on the stack
masm.movePtr(ImmWord(profiler->sizePointer()), size);
masm.load32(Address(size, 0), size);
Label stackFull;
masm.branch32(Assembler::GreaterThanOrEqual, size, Imm32(profiler->maxSize()),
&stackFull);
case MFunctionBoundary::Enter:
if (sps.slowAssertions()) {
typedef bool(*pf)(JSContext *, HandleScript);
static const VMFunction SPSEnterInfo = FunctionInfo<pf>(SPSEnter);
// With room, store our string onto the stack
masm.movePtr(ImmWord(profiler->stack()), base);
JS_STATIC_ASSERT(sizeof(ProfileEntry) == 2 * sizeof(void*));
masm.lshiftPtr(Imm32(sizeof(void*) == 4 ? 3 : 4), size);
masm.addPtr(size, base);
saveLive(lir);
pushArg(ImmGCPtr(lir->script()));
if (!callVM(SPSEnterInfo, lir))
return false;
restoreLive(lir);
sps.pushManual(lir->script(), masm, temp);
return true;
}
masm.storePtr(ImmWord(string), Address(base, offsetof(ProfileEntry, string)));
masm.storePtr(ImmWord((uintptr_t) 0), Address(base, offsetof(ProfileEntry, sp)));
return sps.push(GetIonContext()->cx, lir->script(), masm, temp);
// Always increment the stack size (paired with a decrement in pop)
masm.bind(&stackFull);
masm.movePtr(ImmWord(profiler->sizePointer()), size);
Address addr(size, 0);
masm.add32(Imm32(1), addr);
#endif
return true;
}
case MFunctionBoundary::Inline_Exit:
// all inline returns were covered with ::Exit, so we just need to
// maintain the state of inline frames currently active and then
// reenter the caller
sps.leaveInlineFrame();
sps.reenter(masm, temp);
return true;
bool
CodeGenerator::visitProfilingExit(LProfilingExit *exit)
{
#if 0
SPSProfiler *profiler = &gen->compartment->rt->spsProfiler;
JS_ASSERT(profiler->enabled());
Register temp = ToRegister(exit->temp());
masm.movePtr(ImmWord(profiler->sizePointer()), temp);
Address addr(temp, 0);
masm.add32(Imm32(-1), addr);
#endif
return true;
case MFunctionBoundary::Exit:
if (sps.slowAssertions()) {
typedef bool(*pf)(JSContext *, HandleScript);
static const VMFunction SPSExitInfo = FunctionInfo<pf>(SPSExit);
saveLive(lir);
pushArg(ImmGCPtr(lir->script()));
// Once we've exited, then we shouldn't emit instrumentation for
// the corresponding reenter() because we no longer have a
// frame.
sps.skipNextReenter();
if (!callVM(SPSExitInfo, lir))
return false;
restoreLive(lir);
return true;
}
sps.pop(masm, temp);
return true;
default:
JS_NOT_REACHED("invalid LFunctionBoundary type");
}
}
} // namespace ion

View File

@ -163,8 +163,7 @@ class CodeGenerator : public CodeGeneratorSpecific
bool emitInstanceOf(LInstruction *ins, Register rhs);
bool visitInstanceOfO(LInstanceOfO *ins);
bool visitInstanceOfV(LInstanceOfV *ins);
bool visitProfilingEnter(LProfilingEnter *lir);
bool visitProfilingExit(LProfilingExit *lir);
bool visitFunctionBoundary(LFunctionBoundary *lir);
bool visitGetDOMProperty(LGetDOMProperty *lir);
bool visitSetDOMProperty(LSetDOMProperty *lir);
bool visitCallDOMNative(LCallDOMNative *lir);

View File

@ -289,13 +289,8 @@ IonBuilder::build()
// Emit the start instruction, so we can begin real instructions.
current->makeStart(MStart::New(MStart::StartType_Default));
if (instrumentedProfiling()) {
SPSProfiler *profiler = &cx->runtime->spsProfiler;
const char *string = profiler->profileString(cx, script, script->function());
if (!string)
return false;
current->add(MProfilingEnter::New(string));
}
if (instrumentedProfiling())
current->add(MFunctionBoundary::New(script, MFunctionBoundary::Enter));
// Parameters have been checked to correspond to the typeset, now we unbox
// what we can in an infallible manner.
@ -407,18 +402,19 @@ IonBuilder::buildInline(IonBuilder *callerBuilder, MResumePoint *callerResumePoi
current->setCallerResumePoint(callerResumePoint);
// Flag the entry into an inlined function with a special MStart block
if (instrumentedProfiling()) {
SPSProfiler *profiler = &cx->runtime->spsProfiler;
const char *string = profiler->profileString(cx, script, script->function());
if (!string)
return false;
current->add(MProfilingEnter::New(string));
}
// Connect the entrance block to the last block in the caller's graph.
MBasicBlock *predecessor = callerBuilder->current;
JS_ASSERT(predecessor == callerResumePoint->block());
// All further instructions generated in from this scope should be
// considered as part of the function that we're inlining. We also need to
// keep track of the inlining depth because all scripts inlined on the same
// level contiguously have only one Inline_Exit node.
if (instrumentedProfiling())
predecessor->add(MFunctionBoundary::New(script,
MFunctionBoundary::Inline_Enter,
inliningDepth));
predecessor->end(MGoto::New(current));
if (!current->addPredecessorWithoutPhis(predecessor))
return false;
@ -2561,7 +2557,7 @@ IonBuilder::processReturn(JSOp op)
}
if (instrumentedProfiling())
current->add(MProfilingExit::New());
current->add(MFunctionBoundary::New(script, MFunctionBoundary::Exit));
MReturn *ret = MReturn::New(def);
current->end(ret);
@ -3229,6 +3225,12 @@ IonBuilder::inlineScriptedCall(AutoObjectVector &targets, uint32 argc, bool cons
RootedFunction target(cx, func);
if (!jsop_call_inline(target, argc, constructing, constFun, bottom, retvalDefns))
return false;
// The Inline_Enter node is handled by buildInline, we're responsible
// for the Inline_Exit node (mostly for the case below)
if (instrumentedProfiling())
bottom->add(MFunctionBoundary::New(NULL, MFunctionBoundary::Inline_Exit));
} else {
// In the polymorphic case, we end the current block with a MPolyInlineDispatch instruction.
@ -3254,6 +3256,20 @@ IonBuilder::inlineScriptedCall(AutoObjectVector &targets, uint32 argc, bool cons
}
top->end(disp);
// If profiling is enabled, then we need a clear-cut boundary of all of
// the inlined functions which is distinct from the fallback path where
// no inline functions are entered. In the case that there's a fallback
// path and a set of inline functions, we create a new block as a join
// point for all of the inline paths which will then go to the real end
// block: 'bottom'. This 'inlineBottom' block is never different from
// 'bottom' except for this one case where profiling is turned on.
MBasicBlock *inlineBottom = bottom;
if (instrumentedProfiling() && disp->inlinePropertyTable()) {
inlineBottom = newBlock(NULL, pc);
if (inlineBottom == NULL)
return false;
}
for (size_t i = 0; i < disp->numCallees(); i++) {
// Do the inline function build.
MConstant *constFun = disp->getFunctionConstant(i);
@ -3261,8 +3277,46 @@ IonBuilder::inlineScriptedCall(AutoObjectVector &targets, uint32 argc, bool cons
MBasicBlock *block = disp->getSuccessor(i);
graph().moveBlockToEnd(block);
current = block;
if (!jsop_call_inline(target, argc, constructing, constFun, bottom, retvalDefns))
if (!jsop_call_inline(target, argc, constructing, constFun, inlineBottom, retvalDefns))
return false;
}
// Regardless of whether inlineBottom != bottom, demarcate these exits
// with an Inline_Exit instruction signifying that the inlined functions
// on this level have all ceased running.
if (instrumentedProfiling())
inlineBottom->add(MFunctionBoundary::New(NULL, MFunctionBoundary::Inline_Exit));
// In the case where we had to create a new block, all of the returns of
// the inline functions need to be merged together with a phi node. This
// phi node resident in the 'inlineBottom' block is then an input to the
// phi node for this entire call sequence in the 'bottom' block.
if (inlineBottom != bottom) {
graph().moveBlockToEnd(inlineBottom);
inlineBottom->inheritSlots(top);
if (!inlineBottom->initEntrySlots())
return false;
// Only need to phi returns together if there's more than one
if (retvalDefns.length() > 1) {
// This is the same depth as the phi node of the 'bottom' block
// after all of the 'pops' happen (see pop() sequence below)
MPhi *phi = MPhi::New(inlineBottom->stackDepth() - argc - 2);
inlineBottom->addPhi(phi);
for (MDefinition **it = retvalDefns.begin(), **end = retvalDefns.end(); it != end; ++it) {
if (!phi->addInput(*it))
return false;
}
// retvalDefns should become a singleton vector of 'phi'
retvalDefns.clear();
if (!retvalDefns.append(phi))
return false;
}
inlineBottom->end(MGoto::New(bottom));
if (!bottom->addPredecessorWithoutPhis(inlineBottom))
return false;
}

View File

@ -183,10 +183,6 @@ class IonBuilder : public MIRGenerator
return js_IonOptions.inlining;
}
bool instrumentedProfiling() {
return cx->runtime->spsProfiler.enabled();
}
JSFunction *getSingleCallTarget(uint32 argc, jsbytecode *pc);
unsigned getPolyCallTargets(uint32 argc, jsbytecode *pc,
AutoObjectVector &targets, uint32_t maxTargets);

View File

@ -24,6 +24,10 @@
namespace js {
namespace ion {
class MacroAssembler;
typedef SPSInstrumentation<MacroAssembler, Register> IonInstrumentation;
// The public entrypoint for emitting assembly. Note that a MacroAssembler can
// use cx->lifoAlloc, so take care not to interleave masm use with other
@ -55,10 +59,16 @@ class MacroAssembler : public MacroAssemblerSpecific
Maybe<AutoIonContextAlloc> alloc_;
bool enoughMemory_;
private:
IonInstrumentation *sps;
jsbytecode **pc_;
public:
MacroAssembler()
MacroAssembler(IonInstrumentation *sps = NULL, jsbytecode **pc = NULL)
: autoRooter_(GetIonContext()->cx, thisFromCtor()),
enoughMemory_(true)
enoughMemory_(true),
sps(sps),
pc_(pc)
{
if (!GetIonContext()->temp)
alloc_.construct(GetIonContext()->cx);
@ -71,7 +81,9 @@ class MacroAssembler : public MacroAssemblerSpecific
// (for example, Trampoline-$(ARCH).cpp).
MacroAssembler(JSContext *cx)
: autoRooter_(cx, thisFromCtor()),
enoughMemory_(true)
enoughMemory_(true),
sps(NULL),
pc_(NULL)
{
ionContext_.construct(cx, cx->compartment, (js::ion::TempAllocator *)NULL);
alloc_.construct(cx);
@ -465,6 +477,110 @@ class MacroAssembler : public MacroAssemblerSpecific
// Generates code used to complete a bailout.
void generateBailoutTail(Register scratch);
// These functions exist as small wrappers around sites where execution can
// leave the currently running stream of instructions. They exist so that
// instrumentation may be put in place around them if necessary and the
// instrumentation is enabled.
void callWithABI(void *fun, Result result = GENERAL) {
leaveBeforeCall();
MacroAssemblerSpecific::callWithABI(fun, result);
reenterAfterCall();
}
void handleException() {
// Re-entry code is irrelevant because the exception will leave the
// running function and never come back
if (sps)
sps->skipNextReenter();
leaveBeforeCall();
MacroAssemblerSpecific::handleException();
// Doesn't actually emit code, but balances the leave()
if (sps)
sps->reenter(*this, InvalidReg);
}
private:
void spsProfileEntryAddress(SPSProfiler *p, int offset, Register temp,
Label *full)
{
movePtr(ImmWord(p->sizePointer()), temp);
load32(Address(temp, 0), temp);
if (offset != 0)
add32(Imm32(offset), temp);
branch32(Assembler::GreaterThanOrEqual, temp, Imm32(p->maxSize()), full);
// 4 * sizeof(void*) * idx = idx << (2 + log(sizeof(void*)))
JS_STATIC_ASSERT(sizeof(ProfileEntry) == 4 * sizeof(void*));
lshiftPtr(Imm32(2 + (sizeof(void*) == 4 ? 2 : 3)), temp);
addPtr(ImmWord(p->stack()), temp);
}
public:
// These functions are needed by the IonInstrumentation interface defined in
// vm/SPSProfiler.h. They will modify the pseudostack provided to SPS to
// perform the actual instrumentation.
void spsUpdatePCIdx(SPSProfiler *p, int32_t idx, Register temp) {
Label stackFull;
spsProfileEntryAddress(p, -1, temp, &stackFull);
store32(Imm32(idx), Address(temp, ProfileEntry::offsetOfPCIdx()));
bind(&stackFull);
}
void spsPushFrame(SPSProfiler *p, const char *str, JSScript *s, Register temp) {
Label stackFull;
spsProfileEntryAddress(p, 0, temp, &stackFull);
storePtr(ImmWord(str), Address(temp, ProfileEntry::offsetOfString()));
storePtr(ImmGCPtr(s), Address(temp, ProfileEntry::offsetOfScript()));
storePtr(ImmWord((void*) NULL),
Address(temp, ProfileEntry::offsetOfStackAddress()));
store32(Imm32(ProfileEntry::NullPCIndex),
Address(temp, ProfileEntry::offsetOfPCIdx()));
/* Always increment the stack size, tempardless if we actually pushed */
bind(&stackFull);
movePtr(ImmWord(p->sizePointer()), temp);
add32(Imm32(1), Address(temp, 0));
}
void spsPopFrame(SPSProfiler *p, Register temp) {
movePtr(ImmWord(p->sizePointer()), temp);
add32(Imm32(-1), Address(temp, 0));
}
// These two functions are helpers used around call sites throughout the
// assembler. They are mainly called from the call wrappers above, but can
// also be found within callVM and around callIon.
void leaveBeforeCall() {
if (!sps || !sps->enabled())
return;
GeneralRegisterSet regs(Registers::TempMask);
Register r = regs.getAny();
push(r);
sps->leave(*pc_, *this, r);
pop(r);
}
void reenterAfterCall() {
if (!sps || !sps->enabled())
return;
GeneralRegisterSet regs(Registers::TempMask & ~Registers::JSCallMask &
~Registers::CallMask);
if (regs.empty()) {
regs = GeneralRegisterSet(Registers::TempMask);
Register r = regs.getAny();
push(r);
sps->reenter(*this, r);
pop(r);
} else {
sps->reenter(*this, regs.getAny());
}
}
};
} // namespace ion

View File

@ -2979,41 +2979,30 @@ class LInstanceOfV : public LInstructionHelper<1, BOX_PIECES+1, 2>
static const size_t RHS = BOX_PIECES;
};
class LProfilingEnter : public LInstructionHelper<0, 0, 2>
class LFunctionBoundary : public LInstructionHelper<0, 0, 1>
{
public:
LIR_HEADER(ProfilingEnter);
LIR_HEADER(FunctionBoundary);
LProfilingEnter(const LDefinition &temp1, const LDefinition &temp2) {
setTemp(0, temp1);
setTemp(1, temp2);
}
const LDefinition *temp1() {
return getTemp(0);
}
const LDefinition *temp2() {
return getTemp(1);
}
const char *profileString() const {
return mir_->toProfilingEnter()->profileString();
}
};
class LProfilingExit : public LInstructionHelper<0, 0, 1>
{
public:
LIR_HEADER(ProfilingExit);
LProfilingExit(const LDefinition &temp) {
LFunctionBoundary(const LDefinition &temp) {
setTemp(0, temp);
}
const LDefinition *temp() {
return getTemp(0);
}
JSScript *script() {
return mir_->toFunctionBoundary()->script();
}
MFunctionBoundary::Type type() {
return mir_->toFunctionBoundary()->type();
}
unsigned inlineLevel() {
return mir_->toFunctionBoundary()->inlineLevel();
}
};

View File

@ -657,26 +657,27 @@ class LInstruction
class LInstructionVisitor
{
#ifdef TRACK_SNAPSHOTS
LInstruction *ins_;
protected:
jsbytecode *lastPC;
LInstruction *instruction() {
return ins_;
}
#endif
public:
void setInstruction(LInstruction *ins) {
#ifdef TRACK_SNAPSHOTS
ins_ = ins;
#endif
if (ins->mirRaw()) {
lastPC = ins->mirRaw()->trackedPc();
JS_ASSERT(lastPC != NULL);
}
}
LInstructionVisitor()
#ifdef TRACK_SNAPSHOTS
: ins_(NULL)
#endif
: ins_(NULL),
lastPC(NULL)
{}
public:

View File

@ -161,8 +161,7 @@
_(InstanceOfO) \
_(InstanceOfV) \
_(InterruptCheck) \
_(ProfilingEnter) \
_(ProfilingExit) \
_(FunctionBoundary) \
_(GetDOMProperty) \
_(SetDOMProperty) \
_(CallDOMNative)

View File

@ -1808,15 +1808,15 @@ LIRGenerator::visitInstanceOf(MInstanceOf *ins)
}
bool
LIRGenerator::visitProfilingEnter(MProfilingEnter *ins)
LIRGenerator::visitFunctionBoundary(MFunctionBoundary *ins)
{
return add(new LProfilingEnter(temp(), temp()), ins);
}
bool
LIRGenerator::visitProfilingExit(MProfilingExit *ins)
{
return add(new LProfilingExit(temp()), ins);
LFunctionBoundary *lir = new LFunctionBoundary(temp());
if (!add(lir, ins))
return false;
// If slow assertions are enabled, then this node will result in a callVM
// out to a C++ function for the assertions, so we will need a safepoint.
return !gen->compartment->rt->spsProfiler.slowAssertionsEnabled() ||
assignSafepoint(lir, ins);
}
bool

View File

@ -176,8 +176,7 @@ class LIRGenerator : public LIRGeneratorSpecific
bool visitGetArgument(MGetArgument *ins);
bool visitThrow(MThrow *ins);
bool visitInstanceOf(MInstanceOf *ins);
bool visitProfilingEnter(MProfilingEnter *ins);
bool visitProfilingExit(MProfilingExit *ins);
bool visitFunctionBoundary(MFunctionBoundary *ins);
bool visitSetDOMProperty(MSetDOMProperty *ins);
bool visitGetDOMProperty(MGetDOMProperty *ins);
};

View File

@ -239,6 +239,10 @@ class MDefinition : public MNode
uint32 virtualRegister_; // Used by lowering to map definitions to virtual registers.
};
// Track bailouts by storing the current pc in MIR instruction. Also used
// for profiling and keeping track of what the last known pc was.
jsbytecode *trackedPc_;
private:
enum Flag {
None = 0,
@ -262,21 +266,6 @@ class MDefinition : public MNode
flags_ |= flags;
}
#ifdef TRACK_SNAPSHOTS
// Track bailouts by storing the current pc in MIR instruction.
jsbytecode *trackedPc_;
public:
void setTrackedPc(jsbytecode *pc) {
if (!trackedPc_)
trackedPc_ = pc;
}
jsbytecode *trackedPc() {
return trackedPc_;
}
#endif
public:
MDefinition()
: id_(0),
@ -284,10 +273,8 @@ class MDefinition : public MNode
range_(),
resultType_(MIRType_None),
flags_(0),
dependency_(NULL)
#ifdef TRACK_SNAPSHOTS
, trackedPc_(NULL)
#endif
dependency_(NULL),
trackedPc_(NULL)
{ }
virtual Opcode op() const = 0;
@ -295,6 +282,14 @@ class MDefinition : public MNode
static void PrintOpcodeName(FILE *fp, Opcode op);
virtual void printOpcode(FILE *fp);
void setTrackedPc(jsbytecode *pc) {
trackedPc_ = pc;
}
jsbytecode *trackedPc() {
return trackedPc_;
}
Range *range() {
return &range_;
}
@ -5292,44 +5287,50 @@ class MNewCallObject : public MUnaryInstruction
// Node that represents that a script has begun executing. This comes at the
// start of the function and is called once per function (including inline
// ones)
class MProfilingEnter : public MNullaryInstruction
class MFunctionBoundary : public MNullaryInstruction
{
const char *string_;
public:
enum Type {
Enter, // a function has begun executing and it is not inline
Exit, // any function has exited (inlined or normal)
Inline_Enter, // an inline function has begun executing
MProfilingEnter(const char *string) : string_(string) {
JS_ASSERT(string != NULL);
Inline_Exit // all instructions of an inline function are done, a
// return from the inline function could have occurred
// before this boundary
};
private:
JSScript *script_;
Type type_;
unsigned inlineLevel_;
MFunctionBoundary(JSScript *script, Type type, unsigned inlineLevel)
: script_(script), type_(type), inlineLevel_(inlineLevel)
{
JS_ASSERT_IF(type != Inline_Exit, script != NULL);
JS_ASSERT_IF(type == Inline_Enter, inlineLevel != 0);
setGuard();
}
public:
INSTRUCTION_HEADER(ProfilingEnter);
INSTRUCTION_HEADER(FunctionBoundary);
static MProfilingEnter *New(const char *string) {
return new MProfilingEnter(string);
static MFunctionBoundary *New(JSScript *script, Type type,
unsigned inlineLevel = 0) {
return new MFunctionBoundary(script, type, inlineLevel);
}
const char *profileString() {
return string_;
JSScript *script() {
return script_;
}
AliasSet getAliasSet() const {
return AliasSet::None();
}
};
// Pairing of MProfilingEnter. Each Enter is paired with an eventual
// Exit. This includes inline functions.
class MProfilingExit : public MNullaryInstruction
{
MProfilingExit() {
setGuard();
Type type() {
return type_;
}
public:
INSTRUCTION_HEADER(ProfilingExit);
static MProfilingExit *New() {
return new MProfilingExit();
unsigned inlineLevel() {
return inlineLevel_;
}
AliasSet getAliasSet() const {

View File

@ -60,6 +60,10 @@ class MIRGenerator
return error_;
}
bool instrumentedProfiling() {
return compartment->rt->spsProfiler.enabled();
}
public:
JSCompartment *compartment;

View File

@ -132,10 +132,8 @@ MBasicBlock::MBasicBlock(MIRGraph &graph, CompileInfo &info, jsbytecode *pc, Kin
mark_(false),
immediateDominator_(NULL),
numDominated_(0),
loopHeader_(NULL)
#ifdef TRACK_SNAPSHOTS
, trackedPc_(pc)
#endif
loopHeader_(NULL),
trackedPc_(pc)
{
}
@ -485,9 +483,7 @@ MBasicBlock::insertBefore(MInstruction *at, MInstruction *ins)
ins->setBlock(this);
graph().allocDefinitionId(ins);
instructions_.insertBefore(at, ins);
#ifdef TRACK_SNAPSHOTS
ins->setTrackedPc(at->trackedPc());
#endif
}
void
@ -496,9 +492,7 @@ MBasicBlock::insertAfter(MInstruction *at, MInstruction *ins)
ins->setBlock(this);
graph().allocDefinitionId(ins);
instructions_.insertAfter(at, ins);
#ifdef TRACK_SNAPSHOTS
ins->setTrackedPc(at->trackedPc());
#endif
}
void
@ -508,9 +502,7 @@ MBasicBlock::add(MInstruction *ins)
ins->setBlock(this);
graph().allocDefinitionId(ins);
instructions_.pushBack(ins);
#ifdef TRACK_SNAPSHOTS
ins->setTrackedPc(trackedPc_);
#endif
}
void

View File

@ -386,13 +386,15 @@ class MBasicBlock : public TempObject, public InlineListNode<MBasicBlock>
void dumpStack(FILE *fp);
#ifdef TRACK_SNAPSHOTS
// Track bailouts by storing the current pc in MIR instruction added at this
// cycle.
// cycle. This is also used for tracking calls when profiling.
void updateTrackedPc(jsbytecode *pc) {
trackedPc_ = pc;
}
#endif
jsbytecode *trackedPc() {
return trackedPc_;
}
private:
MIRGraph &graph_;
@ -422,11 +424,7 @@ class MBasicBlock : public TempObject, public InlineListNode<MBasicBlock>
size_t numDominated_;
MBasicBlock *loopHeader_;
#ifdef TRACK_SNAPSHOTS
// Track bailouts by storing the current pc in MIR instruction added at this
// cycle.
jsbytecode *trackedPc_;
#endif
};
typedef InlineListIterator<MBasicBlock> MBasicBlockIterator;

View File

@ -127,8 +127,7 @@ namespace ion {
_(Round) \
_(InstanceOf) \
_(InterruptCheck) \
_(ProfilingEnter) \
_(ProfilingExit) \
_(FunctionBoundary) \
_(GetDOMProperty) \
_(SetDOMProperty)

View File

@ -358,5 +358,16 @@ NewCallObject(JSContext *cx, HandleShape shape, HandleTypeObject type, HeapSlot
return CallObject::create(cx, shape, type, slots, global);
}
bool SPSEnter(JSContext *cx, HandleScript script)
{
return cx->runtime->spsProfiler.enter(cx, script, script->function());
}
bool SPSExit(JSContext *cx, HandleScript script)
{
cx->runtime->spsProfiler.exit(cx, script, script->function());
return true;
}
} // namespace ion
} // namespace js

View File

@ -439,6 +439,9 @@ HeapSlot *NewSlots(JSRuntime *rt, unsigned nslots);
JSObject *NewCallObject(JSContext *cx, HandleShape shape, HandleTypeObject type, HeapSlot *slots,
HandleObject global);
bool SPSEnter(JSContext *cx, HandleScript script);
bool SPSExit(JSContext *cx, HandleScript script);
} // namespace ion
} // namespace js

View File

@ -132,7 +132,8 @@ class Registers
// Registers returned from a JS -> C call.
static const uint32 CallMask =
(1 << Registers::r0);
(1 << Registers::r0) |
(1 << Registers::r1); // used for double-size returns
static const uint32 AllocatableMask = AllMask & ~NonAllocatableMask;
};

View File

@ -990,6 +990,9 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM
void subPtr(Imm32 imm, const Register dest);
void addPtr(Imm32 imm, const Register dest);
void addPtr(Imm32 imm, const Address &dest);
void addPtr(ImmWord imm, const Register dest) {
addPtr(Imm32(imm.value), dest);
}
void setStackArg(const Register &reg, uint32 arg);

View File

@ -19,13 +19,17 @@ namespace js {
namespace ion {
CodeGeneratorShared::CodeGeneratorShared(MIRGenerator *gen, LIRGraph &graph)
: gen(gen),
: oolIns(NULL),
masm(&sps, &lastPC),
gen(gen),
graph(graph),
current(NULL),
deoptTable_(NULL),
#ifdef DEBUG
pushedArgs_(0),
#endif
lastOsiPointOffset_(0),
sps(&gen->compartment->rt->spsProfiler),
osrEntryOffset_(0),
frameDepth_(graph.localSlotCount() * sizeof(STACK_SLOT_SIZE) +
graph.argumentSlotCount() * sizeof(Value))
@ -40,11 +44,15 @@ CodeGeneratorShared::generateOutOfLineCode()
if (!gen->temp().ensureBallast())
return false;
masm.setFramePushed(outOfLineCode_[i]->framePushed());
lastPC = outOfLineCode_[i]->pc();
sps.setPushed(outOfLineCode_[i]->script());
outOfLineCode_[i]->bind(&masm);
oolIns = outOfLineCode_[i];
if (!outOfLineCode_[i]->generate(this))
return false;
}
oolIns = NULL;
return true;
}
@ -53,6 +61,13 @@ bool
CodeGeneratorShared::addOutOfLineCode(OutOfLineCode *code)
{
code->setFramePushed(masm.framePushed());
// If an OOL instruction adds another OOL instruction, then use the original
// instruction's script/pc instead of the basic block's that we're on
// because they're probably not relevant any more.
if (oolIns)
code->setSource(oolIns->script(), oolIns->pc());
else
code->setSource(current ? current->mir()->info().script() : NULL, lastPC);
return outOfLineCode_.append(code);
}
@ -343,6 +358,7 @@ CodeGeneratorShared::callVM(const VMFunction &fun, LInstruction *ins, const Regi
IonCode *wrapper = ion->generateVMWrapper(cx, fun);
if (!wrapper)
return false;
masm.leaveBeforeCall();
// Call the wrapper function. The wrapper is in charge to unwind the stack
// when returning from the call. Failures are handled with exceptions based
@ -355,6 +371,7 @@ CodeGeneratorShared::callVM(const VMFunction &fun, LInstruction *ins, const Regi
if (!markSafepoint(ins))
return false;
masm.reenterAfterCall();
// Remove rest of the frame left on the stack. We remove the return address
// which is implicitly poped when returning.

View File

@ -33,6 +33,7 @@ class OutOfLineTruncateSlow;
class CodeGeneratorShared : public LInstructionVisitor
{
js::Vector<OutOfLineCode *, 0, SystemAllocPolicy> outOfLineCode_;
OutOfLineCode *oolIns;
protected:
MacroAssembler masm;
@ -64,6 +65,8 @@ class CodeGeneratorShared : public LInstructionVisitor
// List of stack slots that have been pushed as arguments to an MCall.
js::Vector<uint32, 0, SystemAllocPolicy> pushedArgumentSlots_;
IonInstrumentation sps;
protected:
// The offset of the first instruction of the OSR entry block from the
// beginning of the code buffer.
@ -290,10 +293,14 @@ class OutOfLineCode : public TempObject
Label entry_;
Label rejoin_;
uint32 framePushed_;
jsbytecode *pc_;
JSScript *script_;
public:
OutOfLineCode()
: framePushed_(0)
: framePushed_(0),
pc_(NULL),
script_(NULL)
{ }
virtual bool generate(CodeGeneratorShared *codegen) = 0;
@ -313,6 +320,16 @@ class OutOfLineCode : public TempObject
uint32 framePushed() const {
return framePushed_;
}
void setSource(JSScript *script, jsbytecode *pc) {
script_ = script;
pc_ = pc;
}
jsbytecode *pc() {
return pc_;
}
JSScript *script() {
return script_;
}
};
// For OOL paths that want a specific-typed code generator.

View File

@ -53,7 +53,7 @@ bool
LIRGeneratorX86Shared::visitInterruptCheck(MInterruptCheck *ins)
{
LInterruptCheck *lir = new LInterruptCheck();
if (!add(lir))
if (!add(lir, ins))
return false;
if (!assignSafepoint(lir, ins))
return false;

View File

@ -351,6 +351,11 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared
void addPtr(Imm32 imm, const Address &dest) {
addq(imm, Operand(dest));
}
void addPtr(ImmWord imm, const Register &dest) {
JS_ASSERT(dest != ScratchReg);
movq(imm, ScratchReg);
addq(ScratchReg, dest);
}
void subPtr(Imm32 imm, const Register &dest) {
subq(imm, dest);
}

View File

@ -381,6 +381,9 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared
void addPtr(Imm32 imm, const Register &dest) {
addl(imm, dest);
}
void addPtr(ImmWord imm, const Register &dest) {
addl(Imm32(imm.value), dest);
}
void addPtr(Imm32 imm, const Address &dest) {
addl(imm, Operand(dest));
}

View File

@ -297,41 +297,6 @@ class SPSInstrumentation
FrameState *frame;
public:
/*
* SPS instruments calls to track leaving and reentering a function. A call
* is one made to C++ other other JS. This is a helper class to assist with
* the calls to leave()/reenter() to an SPSInstrumentation instance. It uses
* RAII to invoke leave() on construction and reenter() on destruction.
*/
class CallScope
{
SPSInstrumentation *sps;
jsbytecode *pc;
Assembler &masm;
Register reg;
JS_DECL_USE_GUARD_OBJECT_NOTIFIER
public:
/*
* Each parameter will be passed along to the instrumentation's
* leave()/reenter() methods. The given instrumentation can be NULL, in
* which case this object will do nothing.
*/
CallScope(SPSInstrumentation *sps, jsbytecode *pc, Assembler &masm,
Register reg JS_GUARD_OBJECT_NOTIFIER_PARAM)
: sps(sps), pc(pc), masm(masm), reg(reg)
{
JS_GUARD_OBJECT_NOTIFIER_INIT;
if (sps)
sps->leave(pc, masm, reg);
}
~CallScope() {
if (sps)
sps->reenter(masm, reg);
}
};
/*
* Creates instrumentation which writes information out the the specified
* profiler's stack and constituent fields.
@ -403,7 +368,6 @@ class SPSInstrumentation
void setPushed(JSScript *script) {
if (!enabled())
return;
JS_ASSERT(frame->script == NULL);
JS_ASSERT(frame->left == 0);
frame->script = script;
}
@ -444,8 +408,11 @@ class SPSInstrumentation
* corresponding reenter() actually emits instrumentation.
*/
void leave(jsbytecode *pc, Assembler &masm, Register scratch) {
if (enabled() && frame->script && frame->left++ == 0)
if (enabled() && frame->script && frame->left++ == 0) {
JS_ASSERT(frame->script->code <= pc &&
pc < frame->script->code + frame->script->length);
masm.spsUpdatePCIdx(profiler_, pc - frame->script->code, scratch);
}
}
/*