mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 848122 - Generalize CallScripted stubs once callee-specific stubs get too numerous. r=jandem
This commit is contained in:
parent
1f27eb1ac2
commit
ad763d6e73
@ -5328,14 +5328,15 @@ TryAttachCallStub(JSContext *cx, ICCall_Fallback *stub, HandleScript script, JSO
|
||||
if (useNewType || op == JSOP_EVAL)
|
||||
return true;
|
||||
|
||||
RootedValue callee(cx, vp[0]);
|
||||
RootedValue thisv(cx, vp[1]);
|
||||
|
||||
if (stub->numOptimizedStubs() >= ICCall_Fallback::MAX_OPTIMIZED_STUBS) {
|
||||
// TODO: Discard all stubs in this IC and replace with generic call stub.
|
||||
// TODO: Discard all stubs in this IC and replace with inert megamorphic stub.
|
||||
// But for now we just bail.
|
||||
return true;
|
||||
}
|
||||
|
||||
RootedValue callee(cx, vp[0]);
|
||||
RootedValue thisv(cx, vp[1]);
|
||||
|
||||
if (!callee.isObject())
|
||||
return true;
|
||||
|
||||
@ -5349,12 +5350,37 @@ TryAttachCallStub(JSContext *cx, ICCall_Fallback *stub, HandleScript script, JSO
|
||||
if (!calleeScript->hasBaselineScript() && !calleeScript->hasIonScript())
|
||||
return true;
|
||||
|
||||
// Check if this stub chain has already generalized scripted calls.
|
||||
if (stub->scriptedStubsAreGeneralized()) {
|
||||
IonSpew(IonSpew_BaselineIC, " Chain already has generalized scripted call stub!");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (stub->scriptedStubCount() >= ICCall_Fallback::MAX_SCRIPTED_STUBS) {
|
||||
// Create a Call_AnyScripted stub.
|
||||
IonSpew(IonSpew_BaselineIC, " Generating Call_AnyScripted stub (cons=%s)",
|
||||
constructing ? "yes" : "no");
|
||||
|
||||
ICCallScriptedCompiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(),
|
||||
constructing);
|
||||
ICStub *newStub = compiler.getStub(compiler.getStubSpace(script));
|
||||
if (!newStub)
|
||||
return false;
|
||||
|
||||
// Before adding new stub, unlink all previous Call_Scripted.
|
||||
stub->unlinkStubsWithKind(cx, ICStub::Call_Scripted);
|
||||
|
||||
// Add new generalized stub.
|
||||
stub->addNewStub(newStub);
|
||||
return true;
|
||||
}
|
||||
|
||||
IonSpew(IonSpew_BaselineIC,
|
||||
" Generating Call_Scripted stub (fun=%p, %s:%d, cons=%s)",
|
||||
fun.get(), fun->nonLazyScript()->filename, fun->nonLazyScript()->lineno,
|
||||
constructing ? "yes" : "no");
|
||||
ICCall_Scripted::Compiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(),
|
||||
calleeScript, constructing);
|
||||
ICCallScriptedCompiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(),
|
||||
calleeScript, constructing);
|
||||
ICStub *newStub = compiler.getStub(compiler.getStubSpace(script));
|
||||
if (!newStub)
|
||||
return false;
|
||||
@ -5364,6 +5390,15 @@ TryAttachCallStub(JSContext *cx, ICCall_Fallback *stub, HandleScript script, JSO
|
||||
}
|
||||
|
||||
if (fun->isNative() && (!constructing || (constructing && fun->isNativeConstructor()))) {
|
||||
|
||||
// Generalied native call stubs are not here yet!
|
||||
JS_ASSERT(!stub->nativeStubsAreGeneralized());
|
||||
|
||||
if (stub->nativeStubCount() >= ICCall_Fallback::MAX_NATIVE_STUBS) {
|
||||
IonSpew(IonSpew_BaselineIC, " Too many Call_Native stubs. TODO: add Call_AnyNative!");
|
||||
return true;
|
||||
}
|
||||
|
||||
IonSpew(IonSpew_BaselineIC, " Generating Call_Native stub (fun=%p, cons=%s)", fun.get(),
|
||||
constructing ? "yes" : "no");
|
||||
ICCall_Native::Compiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(),
|
||||
@ -5517,14 +5552,17 @@ ICCall_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
|
||||
|
||||
leaveStubFrame(masm, true);
|
||||
|
||||
// R1 and R0 are taken.
|
||||
regs = availableGeneralRegs(2);
|
||||
Register scratch = regs.takeAny();
|
||||
|
||||
// If this is a |constructing| call, if the callee returns a non-object, we replace it with
|
||||
// the |this| object passed in.
|
||||
JS_ASSERT(JSReturnOperand == R0);
|
||||
Label skipThisReplace;
|
||||
masm.branch32(Assembler::Equal,
|
||||
Address(BaselineStubReg, ICCall_Fallback::offsetOfIsConstructing()),
|
||||
Imm32(0),
|
||||
&skipThisReplace);
|
||||
masm.load16ZeroExtend(Address(BaselineStubReg, ICStub::offsetOfExtra()), scratch);
|
||||
masm.branchTest32(Assembler::Zero, scratch, Imm32(ICCall_Fallback::CONSTRUCTING_FLAG),
|
||||
&skipThisReplace);
|
||||
masm.branchTestObject(Assembler::Equal, JSReturnOperand, &skipThisReplace);
|
||||
masm.moveValue(R1, R0);
|
||||
#ifdef DEBUG
|
||||
@ -5557,7 +5595,7 @@ typedef bool (*CreateThisFn)(JSContext *cx, HandleObject callee, MutableHandleVa
|
||||
static const VMFunction CreateThisInfo = FunctionInfo<CreateThisFn>(CreateThis);
|
||||
|
||||
bool
|
||||
ICCall_Scripted::Compiler::generateStubCode(MacroAssembler &masm)
|
||||
ICCallScriptedCompiler::generateStubCode(MacroAssembler &masm)
|
||||
{
|
||||
Label failure;
|
||||
GeneralRegisterSet regs(availableGeneralRegs(0));
|
||||
@ -5572,22 +5610,33 @@ ICCall_Scripted::Compiler::generateStubCode(MacroAssembler &masm)
|
||||
regs.take(BaselineTailCallReg);
|
||||
|
||||
// Load the callee in R1.
|
||||
// Stack Layout: [ ..., CalleeVal, ThisVal, Arg0Val, ..., ArgNVal, +ICStackValueOffset+ ]
|
||||
BaseIndex calleeSlot(BaselineStackReg, argcReg, TimesEight, ICStackValueOffset + sizeof(Value));
|
||||
masm.loadValue(calleeSlot, R1);
|
||||
regs.take(R1);
|
||||
|
||||
// Ensure callee is an object.
|
||||
masm.branchTestObject(Assembler::NotEqual, R1, &failure);
|
||||
|
||||
// Ensure callee matches this stub's callee.
|
||||
// Ensure callee is a function.
|
||||
Register callee = masm.extractObject(R1, ExtractTemp0);
|
||||
masm.branchTestObjClass(Assembler::NotEqual, callee, regs.getAny(), &FunctionClass, &failure);
|
||||
|
||||
// Object is a function. Check if script matches.
|
||||
masm.loadPtr(Address(callee, JSFunction::offsetOfNativeOrScript()), callee);
|
||||
Address expectedScript(BaselineStubReg, ICCall_Scripted::offsetOfCalleeScript());
|
||||
masm.branchPtr(Assembler::NotEqual, expectedScript, callee, &failure);
|
||||
// If calling a specific script, check if the script matches. Otherwise, ensure that
|
||||
// callee function is scripted. Leave calleeScript in |callee| reg.
|
||||
if (calleeScript_) {
|
||||
JS_ASSERT(kind == ICStub::Call_Scripted);
|
||||
|
||||
// Call IonScript or BaselineScript.
|
||||
// Callee is a function. Check if script matches.
|
||||
masm.loadPtr(Address(callee, JSFunction::offsetOfNativeOrScript()), callee);
|
||||
Address expectedScript(BaselineStubReg, ICCall_Scripted::offsetOfCalleeScript());
|
||||
masm.branchPtr(Assembler::NotEqual, expectedScript, callee, &failure);
|
||||
} else {
|
||||
masm.branchIfFunctionHasNoScript(callee, &failure);
|
||||
masm.loadPtr(Address(callee, JSFunction::offsetOfNativeOrScript()), callee);
|
||||
}
|
||||
|
||||
// Load IonScript or BaselineScript.
|
||||
masm.loadBaselineOrIonCode(callee, regs.getAny(), &failure);
|
||||
|
||||
// Load the start of the target IonCode.
|
||||
@ -5620,9 +5669,18 @@ ICCall_Scripted::Compiler::generateStubCode(MacroAssembler &masm)
|
||||
if (!callVM(CreateThisInfo, masm))
|
||||
return false;
|
||||
|
||||
// Return of CreateThis must be an object.
|
||||
#ifdef DEBUG
|
||||
Label createdThisIsObject;
|
||||
masm.branchTestObject(Assembler::Equal, JSReturnOperand, &createdThisIsObject);
|
||||
masm.breakpoint();
|
||||
masm.bind(&createdThisIsObject);
|
||||
#endif
|
||||
|
||||
// Reset the register set from here on in.
|
||||
JS_ASSERT(JSReturnOperand == R0);
|
||||
regs = availableGeneralRegs(0);
|
||||
regs.take(JSReturnOperand);
|
||||
regs.take(R0);
|
||||
regs.take(ArgumentsRectifierReg);
|
||||
argcReg = regs.takeAny();
|
||||
|
||||
@ -5630,6 +5688,12 @@ ICCall_Scripted::Compiler::generateStubCode(MacroAssembler &masm)
|
||||
// the resulting this object to.
|
||||
masm.pop(argcReg);
|
||||
|
||||
// Save "this" value back into pushed arguments on stack. R0 can be clobbered after that.
|
||||
// Stack now looks like:
|
||||
// [..., Callee, ThisV, Arg0V, ..., ArgNV, StubFrameHeader ]
|
||||
BaseIndex thisSlot(BaselineStackReg, argcReg, TimesEight, STUB_FRAME_SIZE);
|
||||
masm.storeValue(R0, thisSlot);
|
||||
|
||||
// Restore the stub register from the baseline stub frame.
|
||||
masm.loadPtr(Address(BaselineStackReg, STUB_FRAME_SAVED_STUB_OFFSET), BaselineStubReg);
|
||||
|
||||
@ -5637,19 +5701,22 @@ ICCall_Scripted::Compiler::generateStubCode(MacroAssembler &masm)
|
||||
// have destroyed the callee BaselineScript and IonScript. CreateThis is
|
||||
// safely repeatable though, so in this case we just leave the stub frame
|
||||
// and jump to the next stub.
|
||||
callee = regs.takeAny();
|
||||
|
||||
// Just need to load the script now. This can be done from the IC stub because
|
||||
// it has already been verified that callee's script is the same as the stub's script.
|
||||
masm.loadPtr(expectedScript, callee);
|
||||
// Just need to load the script now.
|
||||
BaseIndex calleeSlot3(BaselineStackReg, argcReg, TimesEight,
|
||||
sizeof(Value) + STUB_FRAME_SIZE);
|
||||
masm.loadValue(calleeSlot3, R0);
|
||||
callee = masm.extractObject(R0, ExtractTemp0);
|
||||
regs.add(R0);
|
||||
regs.takeUnchecked(callee);
|
||||
masm.loadPtr(Address(callee, JSFunction::offsetOfNativeOrScript()), callee);
|
||||
Register loadScratch = ArgumentsRectifierReg;
|
||||
masm.loadBaselineOrIonCode(callee, loadScratch, &failureLeaveStubFrame);
|
||||
regs.add(callee);
|
||||
|
||||
// Save "this" value back into pushed arguments on stack.
|
||||
BaseIndex thisSlot(BaselineStackReg, argcReg, TimesEight, STUB_FRAME_SIZE);
|
||||
masm.storeValue(JSReturnOperand, thisSlot);
|
||||
regs.add(JSReturnOperand);
|
||||
// Release callee register, but don't add ExtractTemp0 back into the pool
|
||||
// ExtractTemp0 is used later, and if it's allocated to some other register at that
|
||||
// point, it will get clobbered when used.
|
||||
if (callee != ExtractTemp0)
|
||||
regs.add(callee);
|
||||
|
||||
// Load the start of the target IonCode.
|
||||
code = regs.takeAny();
|
||||
|
@ -321,6 +321,7 @@ class ICEntry
|
||||
\
|
||||
_(Call_Fallback) \
|
||||
_(Call_Scripted) \
|
||||
_(Call_AnyScripted) \
|
||||
_(Call_Native) \
|
||||
\
|
||||
_(GetElem_Fallback) \
|
||||
@ -713,6 +714,7 @@ class ICStub
|
||||
static bool CanMakeCalls(ICStub::Kind kind) {
|
||||
switch (kind) {
|
||||
case Call_Scripted:
|
||||
case Call_AnyScripted:
|
||||
case Call_Native:
|
||||
case Call_Fallback:
|
||||
case UseCount_Fallback:
|
||||
@ -799,26 +801,32 @@ class ICFallbackStub : public ICStub
|
||||
lastStubPtrAddr_ = stub->addressOfNext();
|
||||
numOptimizedStubs_++;
|
||||
}
|
||||
bool hasStub(ICStub::Kind kind) {
|
||||
ICStub *stub = icEntry_->firstStub();
|
||||
do {
|
||||
if (stub->kind() == kind)
|
||||
return true;
|
||||
|
||||
stub = stub->next();
|
||||
} while (stub);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
ICStubConstIterator beginChainConst() {
|
||||
return ICStubConstIterator(this->icEntry()->firstStub());
|
||||
ICStubConstIterator beginChainConst() const {
|
||||
return ICStubConstIterator(icEntry_->firstStub());
|
||||
}
|
||||
|
||||
ICStubIterator beginChain() {
|
||||
return ICStubIterator(this);
|
||||
}
|
||||
|
||||
bool hasStub(ICStub::Kind kind) const {
|
||||
for (ICStubConstIterator iter = beginChainConst(); !iter.atEnd(); iter++) {
|
||||
if (iter->kind() == kind)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned numStubsWithKind(ICStub::Kind kind) const {
|
||||
unsigned count = 0;
|
||||
for (ICStubConstIterator iter = beginChainConst(); !iter.atEnd(); iter++) {
|
||||
if (iter->kind() == kind)
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
void unlinkStub(Zone *zone, ICStub *prev, ICStub *stub);
|
||||
void unlinkStubsWithKind(JSContext *cx, ICStub::Kind kind);
|
||||
};
|
||||
@ -4368,15 +4376,23 @@ class ICCallStubCompiler : public ICStubCompiler
|
||||
class ICCall_Fallback : public ICMonitoredFallbackStub
|
||||
{
|
||||
friend class ICStubSpace;
|
||||
uint32_t isConstructing_;
|
||||
public:
|
||||
static const unsigned CONSTRUCTING_FLAG = 0x0001;
|
||||
|
||||
static const uint32_t MAX_OPTIMIZED_STUBS = 16;
|
||||
static const uint32_t MAX_SCRIPTED_STUBS = 7;
|
||||
static const uint32_t MAX_NATIVE_STUBS = 7;
|
||||
private:
|
||||
|
||||
ICCall_Fallback(IonCode *stubCode, bool isConstructing)
|
||||
: ICMonitoredFallbackStub(ICStub::Call_Fallback, stubCode),
|
||||
isConstructing_(isConstructing ? 1 : 0)
|
||||
{ }
|
||||
: ICMonitoredFallbackStub(ICStub::Call_Fallback, stubCode)
|
||||
{
|
||||
extra_ = 0;
|
||||
if (isConstructing)
|
||||
extra_ |= CONSTRUCTING_FLAG;
|
||||
}
|
||||
|
||||
public:
|
||||
static const uint32_t MAX_OPTIMIZED_STUBS = 8;
|
||||
|
||||
static inline ICCall_Fallback *New(ICStubSpace *space, IonCode *code, bool isConstructing)
|
||||
{
|
||||
@ -4386,10 +4402,22 @@ class ICCall_Fallback : public ICMonitoredFallbackStub
|
||||
}
|
||||
|
||||
bool isConstructing() const {
|
||||
return isConstructing_;
|
||||
return extra_ & CONSTRUCTING_FLAG;
|
||||
}
|
||||
static size_t offsetOfIsConstructing() {
|
||||
return offsetof(ICCall_Fallback, isConstructing_);
|
||||
|
||||
unsigned scriptedStubCount() const {
|
||||
return numStubsWithKind(Call_Scripted);
|
||||
}
|
||||
bool scriptedStubsAreGeneralized() const {
|
||||
return hasStub(Call_AnyScripted);
|
||||
}
|
||||
|
||||
unsigned nativeStubCount() const {
|
||||
return numStubsWithKind(Call_Native);
|
||||
}
|
||||
bool nativeStubsAreGeneralized() const {
|
||||
// Return hasStub(Call_AnyNative) after Call_AnyNative stub is added.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Compiler for this stub kind.
|
||||
@ -4441,32 +4469,59 @@ class ICCall_Scripted : public ICMonitoredStub
|
||||
HeapPtrScript &calleeScript() {
|
||||
return calleeScript_;
|
||||
}
|
||||
};
|
||||
|
||||
// Compiler for this stub kind.
|
||||
class Compiler : public ICCallStubCompiler {
|
||||
protected:
|
||||
ICStub *firstMonitorStub_;
|
||||
bool isConstructing_;
|
||||
RootedScript calleeScript_;
|
||||
bool generateStubCode(MacroAssembler &masm);
|
||||
class ICCall_AnyScripted : public ICMonitoredStub
|
||||
{
|
||||
friend class ICStubSpace;
|
||||
|
||||
virtual int32_t getKey() const {
|
||||
return static_cast<int32_t>(kind) | (static_cast<int32_t>(isConstructing_) << 16);
|
||||
}
|
||||
ICCall_AnyScripted(IonCode *stubCode, ICStub *firstMonitorStub)
|
||||
: ICMonitoredStub(ICStub::Call_AnyScripted, stubCode, firstMonitorStub)
|
||||
{ }
|
||||
|
||||
public:
|
||||
Compiler(JSContext *cx, ICStub *firstMonitorStub, HandleScript calleeScript,
|
||||
bool isConstructing)
|
||||
: ICCallStubCompiler(cx, ICStub::Call_Scripted),
|
||||
firstMonitorStub_(firstMonitorStub),
|
||||
isConstructing_(isConstructing),
|
||||
calleeScript_(cx, calleeScript)
|
||||
{ }
|
||||
public:
|
||||
static inline ICCall_AnyScripted *New(ICStubSpace *space, IonCode *code,
|
||||
ICStub *firstMonitorStub)
|
||||
{
|
||||
if (!code)
|
||||
return NULL;
|
||||
return space->allocate<ICCall_AnyScripted>(code, firstMonitorStub);
|
||||
}
|
||||
};
|
||||
|
||||
ICStub *getStub(ICStubSpace *space) {
|
||||
// Compiler for Call_Scripted and Call_AnyScripted stubs.
|
||||
class ICCallScriptedCompiler : public ICCallStubCompiler {
|
||||
protected:
|
||||
ICStub *firstMonitorStub_;
|
||||
bool isConstructing_;
|
||||
RootedScript calleeScript_;
|
||||
bool generateStubCode(MacroAssembler &masm);
|
||||
|
||||
virtual int32_t getKey() const {
|
||||
return static_cast<int32_t>(kind) | (static_cast<int32_t>(isConstructing_) << 16);
|
||||
}
|
||||
|
||||
public:
|
||||
ICCallScriptedCompiler(JSContext *cx, ICStub *firstMonitorStub, HandleScript calleeScript,
|
||||
bool isConstructing)
|
||||
: ICCallStubCompiler(cx, ICStub::Call_Scripted),
|
||||
firstMonitorStub_(firstMonitorStub),
|
||||
isConstructing_(isConstructing),
|
||||
calleeScript_(cx, calleeScript)
|
||||
{ }
|
||||
|
||||
ICCallScriptedCompiler(JSContext *cx, ICStub *firstMonitorStub, bool isConstructing)
|
||||
: ICCallStubCompiler(cx, ICStub::Call_AnyScripted),
|
||||
firstMonitorStub_(firstMonitorStub),
|
||||
isConstructing_(isConstructing),
|
||||
calleeScript_(cx, NULL)
|
||||
{ }
|
||||
|
||||
ICStub *getStub(ICStubSpace *space) {
|
||||
if (calleeScript_)
|
||||
return ICCall_Scripted::New(space, getStubCode(), firstMonitorStub_, calleeScript_);
|
||||
}
|
||||
};
|
||||
return ICCall_AnyScripted::New(space, getStubCode(), firstMonitorStub_);
|
||||
}
|
||||
};
|
||||
|
||||
class ICCall_Native : public ICMonitoredStub
|
||||
|
Loading…
Reference in New Issue
Block a user