Bug 784127 - Purge JM caches when a shape guard fails in Ion. r=sstangl

This commit is contained in:
Jan de Mooij 2012-09-19 13:18:54 +02:00
parent 482c9a8d8a
commit 8e73694d1e
15 changed files with 141 additions and 26 deletions

View File

@ -308,6 +308,8 @@ ConvertFrames(JSContext *cx, IonActivation *activation, IonBailoutIterator &it)
return BAILOUT_RETURN_BOUNDS_CHECK;
case Bailout_Invalidate:
return BAILOUT_RETURN_INVALIDATE;
case Bailout_CachedShapeGuard:
return BAILOUT_RETURN_CACHED_SHAPE_GUARD;
// When bailing out from an argument check, none of the code of the
// function has run yet. When profiling, this means that the function
@ -552,6 +554,25 @@ ion::ForceInvalidation()
return Invalidate(cx, script);
}
uint32
ion::CachedShapeGuardFailure()
{
JSContext *cx = GetIonContext()->cx;
JSScript *script = GetBailedJSScript(cx);
JS_ASSERT(script->hasIonScript());
JS_ASSERT(!script->ion->invalidated());
// Purge JM caches in the script and all inlined script, to avoid baking in
// the same shape guard next time.
for (size_t i = 0; i < script->ion->scriptEntries(); i++)
mjit::PurgeCaches(script->ion->getScript(i));
IonSpew(IonSpew_Invalidate, "Invalidating due to shape guard failure");
return Invalidate(cx, script);
}
uint32
ion::ThunkToInterpreter(Value *vp)
{

View File

@ -108,6 +108,7 @@ static const uint32 BAILOUT_RETURN_RECOMPILE_CHECK = 5;
static const uint32 BAILOUT_RETURN_BOUNDS_CHECK = 6;
static const uint32 BAILOUT_RETURN_INVALIDATE = 7;
static const uint32 BAILOUT_RETURN_OVERRECURSED = 8;
static const uint32 BAILOUT_RETURN_CACHED_SHAPE_GUARD = 9;
// Attached to the compartment for easy passing through from ::Bailout to
// ::ThunkToInterpreter.
@ -223,6 +224,8 @@ uint32 BoundsCheckFailure();
uint32 ForceInvalidation();
uint32 CachedShapeGuardFailure();
} // namespace ion
} // namespace js

View File

@ -2815,7 +2815,7 @@ CodeGenerator::generate()
bailouts_.length(), graph.numConstants(),
safepointIndices_.length(), osiIndices_.length(),
cacheList_.length(), barrierOffsets_.length(),
safepoints_.size());
safepoints_.size(), graph.mir().numScripts());
if (!script->ion)
return false;
invalidateEpilogueData_.fixup(&masm);
@ -2851,6 +2851,9 @@ CodeGenerator::generate()
if (safepoints_.size())
script->ion->copySafepoints(&safepoints_);
JS_ASSERT(graph.mir().numScripts() > 0);
script->ion->copyScriptEntries(graph.mir().scripts());
linkAbsoluteLabels();
// The correct state for prebarriers is unknown until the end of compilation,

View File

@ -423,6 +423,8 @@ IonScript::IonScript()
prebarrierEntries_(0),
safepointsStart_(0),
safepointsSize_(0),
scriptList_(0),
scriptEntries_(0),
refcount_(0),
slowCallCount(0),
recompileInfo_()
@ -433,7 +435,7 @@ IonScript *
IonScript::New(JSContext *cx, uint32 frameSlots, uint32 frameSize, size_t snapshotsSize,
size_t bailoutEntries, size_t constants, size_t safepointIndices,
size_t osiIndices, size_t cacheEntries, size_t prebarrierEntries,
size_t safepointsSize)
size_t safepointsSize, size_t scriptEntries)
{
if (snapshotsSize >= MAX_BUFFER_SIZE ||
(bailoutEntries >= MAX_BUFFER_SIZE / sizeof(uint32)))
@ -454,6 +456,7 @@ IonScript::New(JSContext *cx, uint32 frameSlots, uint32 frameSize, size_t snapsh
size_t paddedPrebarrierEntriesSize =
AlignBytes(prebarrierEntries * sizeof(CodeOffsetLabel), DataAlignment);
size_t paddedSafepointSize = AlignBytes(safepointsSize, DataAlignment);
size_t paddedScriptSize = AlignBytes(scriptEntries * sizeof(JSScript *), DataAlignment);
size_t bytes = paddedSnapshotsSize +
paddedBailoutSize +
paddedConstantsSize +
@ -461,7 +464,8 @@ IonScript::New(JSContext *cx, uint32 frameSlots, uint32 frameSize, size_t snapsh
paddedOsiIndicesSize +
paddedCacheEntriesSize +
paddedPrebarrierEntriesSize +
paddedSafepointSize;
paddedSafepointSize +
paddedScriptSize;
uint8 *buffer = (uint8 *)cx->malloc_(sizeof(IonScript) + bytes);
if (!buffer)
return NULL;
@ -503,6 +507,10 @@ IonScript::New(JSContext *cx, uint32 frameSlots, uint32 frameSize, size_t snapsh
script->safepointsSize_ = safepointsSize;
offsetCursor += paddedSafepointSize;
script->scriptList_ = offsetCursor;
script->scriptEntries_ = scriptEntries;
offsetCursor += paddedScriptSize;
script->frameSlots_ = frameSlots;
script->frameSize_ = frameSize;
@ -551,6 +559,13 @@ IonScript::copyConstants(const HeapValue *vp)
constants()[i].init(vp[i]);
}
void
IonScript::copyScriptEntries(JSScript **scripts)
{
for (size_t i = 0; i < scriptEntries_; i++)
scriptList()[i] = scripts[i];
}
void
IonScript::copySafepointIndices(const SafepointIndex *si, MacroAssembler &masm)
{

View File

@ -267,6 +267,9 @@ IonBuilder::build()
IonSpew(IonSpew_Scripts, "Analyzing script %s:%d (%p) (usecount=%d) (maxloopcount=%d)",
script->filename, script->lineno, (void *) script, (int) script->getUseCount(), (int) script->getMaxLoopCount());
if (!graph().addScript(script))
return false;
if (!initParameters())
return false;
@ -389,6 +392,9 @@ IonBuilder::buildInline(IonBuilder *callerBuilder, MResumePoint *callerResumePoi
IonSpew(IonSpew_Scripts, "Inlining script %s:%d (%p)",
script->filename, script->lineno, (void *)script);
if (!graph().addScript(script))
return false;
callerBuilder_ = callerBuilder;
callerResumePoint_ = callerResumePoint;
@ -4667,7 +4673,7 @@ IonBuilder::jsop_getgname(HandlePropertyName name)
// If we have a property typeset, the isOwnProperty call will trigger recompilation if
// the property is deleted or reconfigured.
if (!propertyTypes && shape->configurable()) {
MGuardShape *guard = MGuardShape::New(global, globalObj->lastProperty());
MGuardShape *guard = MGuardShape::New(global, globalObj->lastProperty(), Bailout_Invalidate);
current->add(guard);
}
@ -4720,7 +4726,7 @@ IonBuilder::jsop_setgname(HandlePropertyName name)
// if the property is deleted or reconfigured. Without TI, we always need a shape guard
// to guard against the property being reconfigured as non-writable.
if (!propertyTypes) {
MGuardShape *guard = MGuardShape::New(global, globalObj->lastProperty());
MGuardShape *guard = MGuardShape::New(global, globalObj->lastProperty(), Bailout_Invalidate);
current->add(guard);
}
@ -5470,7 +5476,7 @@ IonBuilder::TestCommonPropFunc(JSContext *cx, types::StackTypeSet *types, Handle
// are no lookup hooks for this property.
MInstruction *wrapper = MConstant::New(ObjectValue(*foundProto));
current->add(wrapper);
MGuardShape *guard = MGuardShape::New(wrapper, foundProto->lastProperty());
MGuardShape *guard = MGuardShape::New(wrapper, foundProto->lastProperty(), Bailout_Invalidate);
current->add(guard);
// Now we have to freeze all the property typesets to ensure there isn't a
@ -5898,7 +5904,7 @@ IonBuilder::jsop_getprop(HandlePropertyName name)
// that the shape is still a lastProperty, and calling
// Shape::search() on dictionary mode shapes that aren't
// lastProperty is invalid.
MGuardShape *guard = MGuardShape::New(obj, objShape);
MGuardShape *guard = MGuardShape::New(obj, objShape, Bailout_CachedShapeGuard);
current->add(guard);
spew("Inlining monomorphic GETPROP");
@ -6020,7 +6026,7 @@ IonBuilder::jsop_setprop(HandlePropertyName name)
// long as the shape is not in dictionary mode. We cannot be sure
// that the shape is still a lastProperty, and calling Shape::search
// on dictionary mode shapes that aren't lastProperty is invalid.
MGuardShape *guard = MGuardShape::New(obj, objShape);
MGuardShape *guard = MGuardShape::New(obj, objShape, Bailout_CachedShapeGuard);
current->add(guard);
Shape *shape = objShape->search(cx, NameToId(name));

View File

@ -201,6 +201,10 @@ struct IonScript
uint32 safepointsStart_;
uint32 safepointsSize_;
// List of compiled/inlined JSScript's.
uint32 scriptList_;
uint32 scriptEntries_;
// Number of references from invalidation records.
size_t refcount_;
@ -233,6 +237,9 @@ struct IonScript
CodeOffsetLabel *prebarrierList() {
return (CodeOffsetLabel *)(reinterpret_cast<uint8 *>(this) + prebarrierList_);
}
JSScript **scriptList() const {
return (JSScript **)(reinterpret_cast<const uint8 *>(this) + scriptList_);
}
private:
void trace(JSTracer *trc);
@ -244,7 +251,8 @@ struct IonScript
static IonScript *New(JSContext *cx, uint32 frameLocals, uint32 frameSize,
size_t snapshotsSize, size_t snapshotEntries,
size_t constants, size_t safepointIndexEntries, size_t osiIndexEntries,
size_t cacheEntries, size_t prebarrierEntries, size_t safepointsSize);
size_t cacheEntries, size_t prebarrierEntries, size_t safepointsSize,
size_t scriptEntries);
static void Trace(JSTracer *trc, IonScript *script);
static void Destroy(FreeOp *fop, IonScript *script);
@ -321,8 +329,15 @@ struct IonScript
size_t safepointsSize() const {
return safepointsSize_;
}
JSScript *getScript(size_t i) const {
JS_ASSERT(i < scriptEntries_);
return scriptList()[i];
}
size_t scriptEntries() const {
return scriptEntries_;
}
size_t size() const {
return safepointsStart_ + safepointsSize_;
return scriptList_ + scriptEntries_ * sizeof(JSScript *);
}
HeapValue &getConstant(size_t index) {
JS_ASSERT(index < numConstants());
@ -366,6 +381,7 @@ struct IonScript
void copyCacheEntries(const IonCache *caches, MacroAssembler &masm);
void copyPrebarrierEntries(const CodeOffsetLabel *barriers, MacroAssembler &masm);
void copySafepoints(const SafepointWriter *writer);
void copyScriptEntries(JSScript **scripts);
bool invalidated() const {
return refcount_ != 0;

View File

@ -502,6 +502,7 @@ MacroAssembler::generateBailoutTail(Register scratch)
Label recompile;
Label boundscheck;
Label overrecursed;
Label invalidate;
// The return value from Bailout is tagged as:
// - 0x0: done (thunk to interpreter)
@ -512,6 +513,8 @@ MacroAssembler::generateBailoutTail(Register scratch)
// - 0x5: recompile to inline calls
// - 0x6: bounds check failure
// - 0x7: force invalidation
// - 0x8: overrecursed
// - 0x9: cached shape guard failure
branch32(LessThan, ReturnReg, Imm32(BAILOUT_RETURN_FATAL_ERROR), &interpret);
branch32(Equal, ReturnReg, Imm32(BAILOUT_RETURN_FATAL_ERROR), &exception);
@ -519,10 +522,21 @@ MacroAssembler::generateBailoutTail(Register scratch)
branch32(LessThan, ReturnReg, Imm32(BAILOUT_RETURN_RECOMPILE_CHECK), &reflow);
branch32(Equal, ReturnReg, Imm32(BAILOUT_RETURN_RECOMPILE_CHECK), &recompile);
branch32(LessThan, ReturnReg, Imm32(BAILOUT_RETURN_INVALIDATE), &boundscheck);
branch32(Equal, ReturnReg, Imm32(BAILOUT_RETURN_BOUNDS_CHECK), &boundscheck);
branch32(Equal, ReturnReg, Imm32(BAILOUT_RETURN_OVERRECURSED), &overrecursed);
branch32(Equal, ReturnReg, Imm32(BAILOUT_RETURN_INVALIDATE), &invalidate);
// Fall-through: force invalidation.
// Fall-through: cached shape guard failure.
{
setupUnalignedABICall(0, scratch);
callWithABI(JS_FUNC_TO_DATA_PTR(void *, CachedShapeGuardFailure));
branchTest32(Zero, ReturnReg, ReturnReg, &exception);
jump(&interpret);
}
// Force invalidation.
bind(&invalidate);
{
setupUnalignedABICall(0, scratch);
callWithABI(JS_FUNC_TO_DATA_PTR(void *, ForceInvalidation));

View File

@ -53,7 +53,10 @@ enum BailoutKind
Bailout_BoundsCheck,
// Like Bailout_Normal, but invalidate the current IonScript.
Bailout_Invalidate
Bailout_Invalidate,
// A shape guard based on JM ICs failed.
Bailout_CachedShapeGuard
};
#ifdef DEBUG

View File

@ -1098,6 +1098,9 @@ class LIRGraph
public:
LIRGraph(MIRGraph *mir);
MIRGraph &mir() const {
return mir_;
}
size_t numBlocks() const {
return blocks_.length();
}

View File

@ -4354,10 +4354,12 @@ class MGuardShape
public SingleObjectPolicy
{
const Shape *shape_;
BailoutKind bailoutKind_;
MGuardShape(MDefinition *obj, const Shape *shape)
MGuardShape(MDefinition *obj, const Shape *shape, BailoutKind bailoutKind)
: MUnaryInstruction(obj),
shape_(shape)
shape_(shape),
bailoutKind_(bailoutKind)
{
setGuard();
setMovable();
@ -4366,8 +4368,8 @@ class MGuardShape
public:
INSTRUCTION_HEADER(GuardShape);
static MGuardShape *New(MDefinition *obj, const Shape *shape) {
return new MGuardShape(obj, shape);
static MGuardShape *New(MDefinition *obj, const Shape *shape, BailoutKind bailoutKind) {
return new MGuardShape(obj, shape, bailoutKind);
}
TypePolicy *typePolicy() {
@ -4379,11 +4381,16 @@ class MGuardShape
const Shape *shape() const {
return shape_;
}
BailoutKind bailoutKind() const {
return bailoutKind_;
}
bool congruentTo(MDefinition * const &ins) const {
if (!ins->isGuardShape())
return false;
if (shape() != ins->toGuardShape()->shape())
return false;
if (bailoutKind() != ins->toGuardShape()->bailoutKind())
return false;
return congruentIfOperandsEqual(ins);
}
AliasSet getAliasSet() const {

View File

@ -442,6 +442,10 @@ class MIRGraph
uint32 idGen_;
MBasicBlock *osrBlock_;
MStart *osrStart_;
// List of compiled/inlined scripts.
Vector<JSScript *, 4, IonAllocPolicy> scripts_;
#ifdef DEBUG
size_t numBlocks_;
#endif
@ -571,6 +575,20 @@ class MIRGraph
MStart *osrStart() {
return osrStart_;
}
bool addScript(JSScript *script) {
// The same script may be inlined multiple times, add it only once.
for (size_t i = 0; i < scripts_.length(); i++) {
if (scripts_[i] == script)
return true;
}
return scripts_.append(script);
}
size_t numScripts() const {
return scripts_.length();
}
JSScript **scripts() {
return scripts_.begin();
}
};
class MDefinitionIterator

View File

@ -316,7 +316,7 @@ LIRGeneratorARM::visitGuardShape(MGuardShape *ins)
{
LDefinition tempObj = temp(LDefinition::OBJECT);
LGuardShape *guard = new LGuardShape(useRegister(ins->obj()), tempObj);
return assignSnapshot(guard, Bailout_Invalidate) && add(guard, ins);
return assignSnapshot(guard, ins->bailoutKind()) && add(guard, ins);
}
bool

View File

@ -64,7 +64,7 @@ bool
LIRGeneratorX86Shared::visitGuardShape(MGuardShape *ins)
{
LGuardShape *guard = new LGuardShape(useRegister(ins->obj()));
return assignSnapshot(guard, Bailout_Invalidate) && add(guard, ins);
return assignSnapshot(guard, ins->bailoutKind()) && add(guard, ins);
}
bool

View File

@ -5838,13 +5838,7 @@ PurgeJITCaches(JSCompartment *c)
JSScript *script = i.get<JSScript>();
/* Discard JM caches. */
for (int constructing = 0; constructing <= 1; constructing++) {
for (int barriers = 0; barriers <= 1; barriers++) {
mjit::JITScript *jit = script->getJIT((bool) constructing, (bool) barriers);
if (jit)
jit->purgeCaches();
}
}
mjit::PurgeCaches(script);
#ifdef JS_ION

View File

@ -1016,6 +1016,18 @@ IsLowerableFunCallOrApply(jsbytecode *pc)
Shape *
GetPICSingleShape(JSContext *cx, JSScript *script, jsbytecode *pc, bool constructing);
static inline void
PurgeCaches(JSScript *script)
{
for (int constructing = 0; constructing <= 1; constructing++) {
for (int barriers = 0; barriers <= 1; barriers++) {
mjit::JITScript *jit = script->getJIT((bool) constructing, (bool) barriers);
if (jit)
jit->purgeCaches();
}
}
}
} /* namespace mjit */
inline mjit::JITChunk *