mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 784127 - Purge JM caches when a shape guard fails in Ion. r=sstangl
This commit is contained in:
parent
482c9a8d8a
commit
8e73694d1e
@ -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)
|
||||
{
|
||||
|
@ -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
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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));
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
|
@ -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
|
||||
|
@ -1098,6 +1098,9 @@ class LIRGraph
|
||||
public:
|
||||
LIRGraph(MIRGraph *mir);
|
||||
|
||||
MIRGraph &mir() const {
|
||||
return mir_;
|
||||
}
|
||||
size_t numBlocks() const {
|
||||
return blocks_.length();
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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 *
|
||||
|
Loading…
Reference in New Issue
Block a user