mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 826148 - Part 4: Ion IC (r=dvander)
This commit is contained in:
parent
60ae5fb743
commit
021fedc58a
@ -3844,6 +3844,8 @@ class OutOfLineCache : public OutOfLineCodeBase<CodeGenerator>
|
|||||||
return codegen->visitOutOfLineBindNameCache(this);
|
return codegen->visitOutOfLineBindNameCache(this);
|
||||||
case LInstruction::LOp_GetNameCache:
|
case LInstruction::LOp_GetNameCache:
|
||||||
return codegen->visitOutOfLineGetNameCache(this);
|
return codegen->visitOutOfLineGetNameCache(this);
|
||||||
|
case LInstruction::LOp_CallsiteCloneCache:
|
||||||
|
return codegen->visitOutOfLineCallsiteCloneCache(this);
|
||||||
default:
|
default:
|
||||||
JS_NOT_REACHED("Bad instruction");
|
JS_NOT_REACHED("Bad instruction");
|
||||||
return false;
|
return false;
|
||||||
@ -3883,6 +3885,41 @@ CodeGenerator::visitCache(LInstruction *ins)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef JSObject *(*CallsiteCloneCacheFn)(JSContext *, size_t, HandleObject);
|
||||||
|
static const VMFunction CallsiteCloneCacheInfo =
|
||||||
|
FunctionInfo<CallsiteCloneCacheFn>(CallsiteCloneCache);
|
||||||
|
|
||||||
|
bool
|
||||||
|
CodeGenerator::visitOutOfLineCallsiteCloneCache(OutOfLineCache *ool)
|
||||||
|
{
|
||||||
|
LCallsiteCloneCache *lir = ool->cache()->toCallsiteCloneCache();
|
||||||
|
const MCallsiteCloneCache *mir = lir->mir();
|
||||||
|
Register callee = ToRegister(lir->callee());
|
||||||
|
RegisterSet liveRegs = lir->safepoint()->liveRegs();
|
||||||
|
Register output = ToRegister(lir->output());
|
||||||
|
|
||||||
|
IonCacheCallsiteClone cache(ool->getInlineJump(), ool->getInlineLabel(),
|
||||||
|
masm.labelForPatch(), liveRegs,
|
||||||
|
callee, mir->block()->info().script(), mir->callPc(), output);
|
||||||
|
|
||||||
|
JS_ASSERT(!mir->resumePoint());
|
||||||
|
|
||||||
|
size_t cacheIndex = allocateCache(cache);
|
||||||
|
|
||||||
|
saveLive(lir);
|
||||||
|
|
||||||
|
pushArg(callee);
|
||||||
|
pushArg(Imm32(cacheIndex));
|
||||||
|
if (!callVM(CallsiteCloneCacheInfo, lir))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
masm.storeCallResult(output);
|
||||||
|
restoreLive(lir);
|
||||||
|
|
||||||
|
masm.jump(ool->rejoin());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
typedef bool (*GetNameCacheFn)(JSContext *, size_t, HandleObject, MutableHandleValue);
|
typedef bool (*GetNameCacheFn)(JSContext *, size_t, HandleObject, MutableHandleValue);
|
||||||
static const VMFunction GetNameCacheInfo =
|
static const VMFunction GetNameCacheInfo =
|
||||||
FunctionInfo<GetNameCacheFn>(GetNameCache);
|
FunctionInfo<GetNameCacheFn>(GetNameCache);
|
||||||
|
@ -201,6 +201,7 @@ class CodeGenerator : public CodeGeneratorSpecific
|
|||||||
bool visitOutOfLineSetPropertyCache(OutOfLineCache *ool);
|
bool visitOutOfLineSetPropertyCache(OutOfLineCache *ool);
|
||||||
bool visitOutOfLineBindNameCache(OutOfLineCache *ool);
|
bool visitOutOfLineBindNameCache(OutOfLineCache *ool);
|
||||||
bool visitOutOfLineGetNameCache(OutOfLineCache *ool);
|
bool visitOutOfLineGetNameCache(OutOfLineCache *ool);
|
||||||
|
bool visitOutOfLineCallsiteCloneCache(OutOfLineCache *ool);
|
||||||
|
|
||||||
bool visitGetPropertyCacheV(LGetPropertyCacheV *ins) {
|
bool visitGetPropertyCacheV(LGetPropertyCacheV *ins) {
|
||||||
return visitCache(ins);
|
return visitCache(ins);
|
||||||
@ -223,6 +224,9 @@ class CodeGenerator : public CodeGeneratorSpecific
|
|||||||
bool visitGetNameCache(LGetNameCache *ins) {
|
bool visitGetNameCache(LGetNameCache *ins) {
|
||||||
return visitCache(ins);
|
return visitCache(ins);
|
||||||
}
|
}
|
||||||
|
bool visitCallsiteCloneCache(LCallsiteCloneCache *ins) {
|
||||||
|
return visitCache(ins);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool visitCache(LInstruction *load);
|
bool visitCache(LInstruction *load);
|
||||||
|
@ -3109,8 +3109,7 @@ IonBuilder::checkInlineableGetPropertyCache(uint32_t argc)
|
|||||||
}
|
}
|
||||||
|
|
||||||
MPolyInlineDispatch *
|
MPolyInlineDispatch *
|
||||||
IonBuilder::makePolyInlineDispatch(JSContext *cx, AutoObjectVector &targets, int argc,
|
IonBuilder::makePolyInlineDispatch(JSContext *cx, int argc, MGetPropertyCache *getPropCache,
|
||||||
MGetPropertyCache *getPropCache,
|
|
||||||
types::StackTypeSet *types, types::StackTypeSet *barrier,
|
types::StackTypeSet *types, types::StackTypeSet *barrier,
|
||||||
MBasicBlock *bottom,
|
MBasicBlock *bottom,
|
||||||
Vector<MDefinition *, 8, IonAllocPolicy> &retvalDefns)
|
Vector<MDefinition *, 8, IonAllocPolicy> &retvalDefns)
|
||||||
@ -3207,7 +3206,8 @@ IonBuilder::makePolyInlineDispatch(JSContext *cx, AutoObjectVector &targets, int
|
|||||||
fallbackBlock->end(MGoto::New(fallbackEndBlock));
|
fallbackBlock->end(MGoto::New(fallbackEndBlock));
|
||||||
|
|
||||||
// Create Call
|
// Create Call
|
||||||
MCall *call = MCall::New(NULL, argc + 1, argc, false);
|
MCall *call = MCall::New(NULL, argc + 1, argc, false,
|
||||||
|
oracle->getCallTarget(script(), argc, pc));
|
||||||
if (!call)
|
if (!call)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
@ -3249,7 +3249,8 @@ IonBuilder::makePolyInlineDispatch(JSContext *cx, AutoObjectVector &targets, int
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
IonBuilder::inlineScriptedCall(AutoObjectVector &targets, uint32_t argc, bool constructing,
|
IonBuilder::inlineScriptedCall(AutoObjectVector &targets, AutoObjectVector &originals,
|
||||||
|
uint32_t argc, bool constructing,
|
||||||
types::StackTypeSet *types, types::StackTypeSet *barrier)
|
types::StackTypeSet *types, types::StackTypeSet *barrier)
|
||||||
{
|
{
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
@ -3289,7 +3290,7 @@ IonBuilder::inlineScriptedCall(AutoObjectVector &targets, uint32_t argc, bool co
|
|||||||
int numCases = inlinePropTable->numEntries();
|
int numCases = inlinePropTable->numEntries();
|
||||||
IonSpew(IonSpew_Inlining, "Got inlineable property cache with %d cases", numCases);
|
IonSpew(IonSpew_Inlining, "Got inlineable property cache with %d cases", numCases);
|
||||||
|
|
||||||
inlinePropTable->trimToTargets(targets);
|
inlinePropTable->trimToAndMaybePatchTargets(targets, originals);
|
||||||
|
|
||||||
// Trim the cases based on those that match the targets at this call site.
|
// Trim the cases based on those that match the targets at this call site.
|
||||||
IonSpew(IonSpew_Inlining, "%d inlineable cases left after trimming to %d targets",
|
IonSpew(IonSpew_Inlining, "%d inlineable cases left after trimming to %d targets",
|
||||||
@ -3339,14 +3340,18 @@ IonBuilder::inlineScriptedCall(AutoObjectVector &targets, uint32_t argc, bool co
|
|||||||
// In the polymorphic case, we end the current block with a MPolyInlineDispatch instruction.
|
// In the polymorphic case, we end the current block with a MPolyInlineDispatch instruction.
|
||||||
|
|
||||||
// Create a PolyInlineDispatch instruction for this call site
|
// Create a PolyInlineDispatch instruction for this call site
|
||||||
MPolyInlineDispatch *disp = makePolyInlineDispatch(cx, targets, argc, getPropCache,
|
MPolyInlineDispatch *disp = makePolyInlineDispatch(cx, argc, getPropCache, types, barrier,
|
||||||
types, barrier, bottom, retvalDefns);
|
bottom, retvalDefns);
|
||||||
if (!disp)
|
if (!disp)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
// It's guaranteed that targets.length() == originals.length()
|
||||||
for (size_t i = 0; i < targets.length(); i++) {
|
for (size_t i = 0; i < targets.length(); i++) {
|
||||||
// Create an MConstant for the function
|
// Create an MConstant for the function. Note that we guard on the
|
||||||
JSFunction *func = targets[i]->toFunction();
|
// original function pointer, even if we have a clone, as we only
|
||||||
RootedFunction target(cx, func);
|
// clone at the callsite, so guarding on the clone would be
|
||||||
|
// guaranteed to fail.
|
||||||
|
JSFunction *func = originals[i]->toFunction();
|
||||||
MConstant *constFun = MConstant::New(ObjectValue(*func));
|
MConstant *constFun = MConstant::New(ObjectValue(*func));
|
||||||
|
|
||||||
// Create new entry block for the inlined callee graph.
|
// Create new entry block for the inlined callee graph.
|
||||||
@ -3693,6 +3698,21 @@ IonBuilder::createThis(HandleFunction target, MDefinition *callee)
|
|||||||
return createThisScripted(callee);
|
return createThisScripted(callee);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
IonBuilder::anyFunctionIsCloneAtCallsite(types::StackTypeSet *funTypes)
|
||||||
|
{
|
||||||
|
uint32_t count = funTypes->getObjectCount();
|
||||||
|
if (count < 1)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < count; i++) {
|
||||||
|
JSObject *obj = funTypes->getSingleObject(i);
|
||||||
|
if (obj->isFunction() && obj->toFunction()->isCloneAtCallsite())
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
IonBuilder::jsop_funcall(uint32_t argc)
|
IonBuilder::jsop_funcall(uint32_t argc)
|
||||||
{
|
{
|
||||||
@ -3706,7 +3726,7 @@ IonBuilder::jsop_funcall(uint32_t argc)
|
|||||||
// If |Function.prototype.call| may be overridden, don't optimize callsite.
|
// If |Function.prototype.call| may be overridden, don't optimize callsite.
|
||||||
RootedFunction native(cx, getSingleCallTarget(argc, pc));
|
RootedFunction native(cx, getSingleCallTarget(argc, pc));
|
||||||
if (!native || !native->isNative() || native->native() != &js_fun_call)
|
if (!native || !native->isNative() || native->native() != &js_fun_call)
|
||||||
return makeCall(native, argc, false);
|
return makeCall(native, argc, false, false);
|
||||||
|
|
||||||
// Extract call target.
|
// Extract call target.
|
||||||
types::StackTypeSet *funTypes = oracle->getCallArg(script(), argc, 0, pc);
|
types::StackTypeSet *funTypes = oracle->getCallArg(script(), argc, 0, pc);
|
||||||
@ -3739,7 +3759,7 @@ IonBuilder::jsop_funcall(uint32_t argc)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Call without inlining.
|
// Call without inlining.
|
||||||
return makeCall(target, argc, false);
|
return makeCall(target, argc, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
@ -3747,7 +3767,7 @@ IonBuilder::jsop_funapply(uint32_t argc)
|
|||||||
{
|
{
|
||||||
RootedFunction native(cx, getSingleCallTarget(argc, pc));
|
RootedFunction native(cx, getSingleCallTarget(argc, pc));
|
||||||
if (argc != 2)
|
if (argc != 2)
|
||||||
return makeCall(native, argc, false);
|
return makeCall(native, argc, false, false);
|
||||||
|
|
||||||
// Disable compilation if the second argument to |apply| cannot be guaranteed
|
// Disable compilation if the second argument to |apply| cannot be guaranteed
|
||||||
// to be either definitely |arguments| or definitely not |arguments|.
|
// to be either definitely |arguments| or definitely not |arguments|.
|
||||||
@ -3758,7 +3778,7 @@ IonBuilder::jsop_funapply(uint32_t argc)
|
|||||||
|
|
||||||
// Fallback to regular call if arg 2 is not definitely |arguments|.
|
// Fallback to regular call if arg 2 is not definitely |arguments|.
|
||||||
if (isArgObj != DefinitelyArguments)
|
if (isArgObj != DefinitelyArguments)
|
||||||
return makeCall(native, argc, false);
|
return makeCall(native, argc, false, false);
|
||||||
|
|
||||||
if (!native ||
|
if (!native ||
|
||||||
!native->isNative() ||
|
!native->isNative() ||
|
||||||
@ -3847,7 +3867,7 @@ IonBuilder::jsop_funapplyarguments(uint32_t argc)
|
|||||||
// Pop apply function.
|
// Pop apply function.
|
||||||
current->pop();
|
current->pop();
|
||||||
|
|
||||||
return makeCall(target, false, argFunc, thisArg, args);
|
return makeCall(target, false, false, argFunc, thisArg, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
@ -3856,11 +3876,29 @@ IonBuilder::jsop_call(uint32_t argc, bool constructing)
|
|||||||
AssertCanGC();
|
AssertCanGC();
|
||||||
|
|
||||||
// Acquire known call target if existent.
|
// Acquire known call target if existent.
|
||||||
AutoObjectVector targets(cx);
|
AutoObjectVector originals(cx);
|
||||||
uint32_t numTargets = getPolyCallTargets(argc, pc, targets, 4);
|
uint32_t numTargets = getPolyCallTargets(argc, pc, originals, 4);
|
||||||
types::StackTypeSet *barrier;
|
types::StackTypeSet *barrier;
|
||||||
types::StackTypeSet *types = oracle->returnTypeSet(script(), pc, &barrier);
|
types::StackTypeSet *types = oracle->returnTypeSet(script(), pc, &barrier);
|
||||||
|
|
||||||
|
// If any call targets need to be cloned, clone them. Keep track of the
|
||||||
|
// originals as we need to case on them for poly inline.
|
||||||
|
bool hasClones = false;
|
||||||
|
AutoObjectVector targets(cx);
|
||||||
|
RootedFunction fun(cx);
|
||||||
|
RootedScript scriptRoot(cx, script());
|
||||||
|
for (uint32_t i = 0; i < numTargets; i++) {
|
||||||
|
fun = originals[i]->toFunction();
|
||||||
|
if (fun->isCloneAtCallsite()) {
|
||||||
|
fun = CloneFunctionAtCallsite(cx, fun, scriptRoot, pc);
|
||||||
|
if (!fun)
|
||||||
|
return false;
|
||||||
|
hasClones = true;
|
||||||
|
}
|
||||||
|
if (!targets.append(fun))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Attempt to inline native and scripted functions.
|
// Attempt to inline native and scripted functions.
|
||||||
if (inliningEnabled()) {
|
if (inliningEnabled()) {
|
||||||
// Inline a single native call if possible.
|
// Inline a single native call if possible.
|
||||||
@ -3877,14 +3915,34 @@ IonBuilder::jsop_call(uint32_t argc, bool constructing)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (numTargets > 0 && makeInliningDecision(targets, argc))
|
if (numTargets > 0 && makeInliningDecision(targets, argc))
|
||||||
return inlineScriptedCall(targets, argc, constructing, types, barrier);
|
return inlineScriptedCall(targets, originals, argc, constructing, types, barrier);
|
||||||
}
|
}
|
||||||
|
|
||||||
RootedFunction target(cx, NULL);
|
RootedFunction target(cx, NULL);
|
||||||
if (numTargets == 1)
|
if (numTargets == 1)
|
||||||
target = targets[0]->toFunction();
|
target = targets[0]->toFunction();
|
||||||
|
|
||||||
return makeCallBarrier(target, argc, constructing, types, barrier);
|
return makeCallBarrier(target, argc, constructing, hasClones, types, barrier);
|
||||||
|
}
|
||||||
|
|
||||||
|
MDefinition *
|
||||||
|
IonBuilder::makeCallsiteClone(HandleFunction target, MDefinition *fun)
|
||||||
|
{
|
||||||
|
// Bake in the clone eagerly if we have a known target. We have arrived here
|
||||||
|
// because TI told us that the known target is a should-clone-at-callsite
|
||||||
|
// function, which means that target already is the clone.
|
||||||
|
if (target) {
|
||||||
|
MConstant *constant = MConstant::New(ObjectValue(*target));
|
||||||
|
current->add(constant);
|
||||||
|
return constant;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a callsite clone IC if we have multiple targets. Note that we
|
||||||
|
// should have checked already that at least some targets are marked as
|
||||||
|
// should-clone-at-callsite.
|
||||||
|
MCallsiteCloneCache *clone = MCallsiteCloneCache::New(fun, pc);
|
||||||
|
current->add(clone);
|
||||||
|
return clone;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
@ -4002,18 +4060,19 @@ IonBuilder::popFormals(uint32_t argc, MDefinition **fun, MPassArg **thisArg,
|
|||||||
}
|
}
|
||||||
|
|
||||||
MCall *
|
MCall *
|
||||||
IonBuilder::makeCallHelper(HandleFunction target, uint32_t argc, bool constructing)
|
IonBuilder::makeCallHelper(HandleFunction target, uint32_t argc, bool constructing,
|
||||||
|
bool cloneAtCallsite)
|
||||||
{
|
{
|
||||||
Vector<MPassArg *> args(cx);
|
Vector<MPassArg *> args(cx);
|
||||||
MPassArg *thisArg;
|
MPassArg *thisArg;
|
||||||
MDefinition *fun;
|
MDefinition *fun;
|
||||||
|
|
||||||
popFormals(argc, &fun, &thisArg, &args);
|
popFormals(argc, &fun, &thisArg, &args);
|
||||||
return makeCallHelper(target, constructing, fun, thisArg, args);
|
return makeCallHelper(target, constructing, cloneAtCallsite, fun, thisArg, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
MCall *
|
MCall *
|
||||||
IonBuilder::makeCallHelper(HandleFunction target, bool constructing,
|
IonBuilder::makeCallHelper(HandleFunction target, bool constructing, bool cloneAtCallsite,
|
||||||
MDefinition *fun, MPassArg *thisArg, Vector<MPassArg *> &args)
|
MDefinition *fun, MPassArg *thisArg, Vector<MPassArg *> &args)
|
||||||
{
|
{
|
||||||
// This function may be called with mutated stack.
|
// This function may be called with mutated stack.
|
||||||
@ -4027,7 +4086,8 @@ IonBuilder::makeCallHelper(HandleFunction target, bool constructing,
|
|||||||
if (target && !target->isNative())
|
if (target && !target->isNative())
|
||||||
targetArgs = Max<uint32_t>(target->nargs, argc);
|
targetArgs = Max<uint32_t>(target->nargs, argc);
|
||||||
|
|
||||||
MCall *call = MCall::New(target, targetArgs + 1, argc, constructing);
|
MCall *call = MCall::New(target, targetArgs + 1, argc, constructing,
|
||||||
|
target ? NULL : oracle->getCallTarget(script(), argc, pc));
|
||||||
if (!call)
|
if (!call)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
@ -4071,6 +4131,11 @@ IonBuilder::makeCallHelper(HandleFunction target, bool constructing,
|
|||||||
// Pass |this| and function.
|
// Pass |this| and function.
|
||||||
call->addArg(0, thisArg);
|
call->addArg(0, thisArg);
|
||||||
|
|
||||||
|
// Add a callsite clone IC for multiple targets which all should be
|
||||||
|
// callsite cloned, or bake in the clone for a single target.
|
||||||
|
if (cloneAtCallsite)
|
||||||
|
fun = makeCallsiteClone(target, fun);
|
||||||
|
|
||||||
if (target && JSOp(*pc) == JSOP_CALL) {
|
if (target && JSOp(*pc) == JSOP_CALL) {
|
||||||
// We know we have a single call target. Check whether the "this" types
|
// We know we have a single call target. Check whether the "this" types
|
||||||
// are DOM types and our function a DOM function, and if so flag the
|
// are DOM types and our function a DOM function, and if so flag the
|
||||||
@ -4084,6 +4149,7 @@ IonBuilder::makeCallHelper(HandleFunction target, bool constructing,
|
|||||||
call->setDOMFunction();
|
call->setDOMFunction();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
call->initFunction(fun);
|
call->initFunction(fun);
|
||||||
|
|
||||||
current->add(call);
|
current->add(call);
|
||||||
@ -4113,7 +4179,7 @@ AdjustTypeBarrierForDOMCall(const JSJitInfo* jitinfo, types::StackTypeSet *types
|
|||||||
|
|
||||||
bool
|
bool
|
||||||
IonBuilder::makeCallBarrier(HandleFunction target, uint32_t argc,
|
IonBuilder::makeCallBarrier(HandleFunction target, uint32_t argc,
|
||||||
bool constructing,
|
bool constructing, bool cloneAtCallsite,
|
||||||
types::StackTypeSet *types,
|
types::StackTypeSet *types,
|
||||||
types::StackTypeSet *barrier)
|
types::StackTypeSet *barrier)
|
||||||
{
|
{
|
||||||
@ -4122,17 +4188,18 @@ IonBuilder::makeCallBarrier(HandleFunction target, uint32_t argc,
|
|||||||
MDefinition *fun;
|
MDefinition *fun;
|
||||||
|
|
||||||
popFormals(argc, &fun, &thisArg, &args);
|
popFormals(argc, &fun, &thisArg, &args);
|
||||||
return makeCallBarrier(target, constructing, fun, thisArg, args, types, barrier);
|
return makeCallBarrier(target, constructing, cloneAtCallsite,
|
||||||
|
fun, thisArg, args, types, barrier);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
IonBuilder::makeCallBarrier(HandleFunction target, bool constructing,
|
IonBuilder::makeCallBarrier(HandleFunction target, bool constructing, bool cloneAtCallsite,
|
||||||
MDefinition *fun, MPassArg *thisArg,
|
MDefinition *fun, MPassArg *thisArg,
|
||||||
Vector<MPassArg *> &args,
|
Vector<MPassArg *> &args,
|
||||||
types::StackTypeSet *types,
|
types::StackTypeSet *types,
|
||||||
types::StackTypeSet *barrier)
|
types::StackTypeSet *barrier)
|
||||||
{
|
{
|
||||||
MCall *call = makeCallHelper(target, constructing, fun, thisArg, args);
|
MCall *call = makeCallHelper(target, constructing, cloneAtCallsite, fun, thisArg, args);
|
||||||
if (!call)
|
if (!call)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -4150,24 +4217,25 @@ IonBuilder::makeCallBarrier(HandleFunction target, bool constructing,
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
IonBuilder::makeCall(HandleFunction target, uint32_t argc, bool constructing)
|
IonBuilder::makeCall(HandleFunction target, uint32_t argc, bool constructing, bool cloneAtCallsite)
|
||||||
{
|
{
|
||||||
Vector<MPassArg *> args(cx);
|
Vector<MPassArg *> args(cx);
|
||||||
MPassArg *thisArg;
|
MPassArg *thisArg;
|
||||||
MDefinition *fun;
|
MDefinition *fun;
|
||||||
|
|
||||||
popFormals(argc, &fun, &thisArg, &args);
|
popFormals(argc, &fun, &thisArg, &args);
|
||||||
return makeCall(target, constructing, fun, thisArg, args);
|
return makeCall(target, constructing, cloneAtCallsite, fun, thisArg, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
IonBuilder::makeCall(HandleFunction target, bool constructing,
|
IonBuilder::makeCall(HandleFunction target, bool constructing, bool cloneAtCallsite,
|
||||||
MDefinition *fun, MPassArg *thisArg,
|
MDefinition *fun, MPassArg *thisArg,
|
||||||
Vector<MPassArg*> &args)
|
Vector<MPassArg*> &args)
|
||||||
{
|
{
|
||||||
types::StackTypeSet *barrier;
|
types::StackTypeSet *barrier;
|
||||||
types::StackTypeSet *types = oracle->returnTypeSet(script(), pc, &barrier);
|
types::StackTypeSet *types = oracle->returnTypeSet(script(), pc, &barrier);
|
||||||
return makeCallBarrier(target, constructing, fun, thisArg, args, types, barrier);
|
return makeCallBarrier(target, constructing, cloneAtCallsite,
|
||||||
|
fun, thisArg, args, types, barrier);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
@ -6284,7 +6352,7 @@ IonBuilder::getPropTryCommonGetter(bool *emitted, HandleId id, types::StackTypeS
|
|||||||
current->add(wrapper);
|
current->add(wrapper);
|
||||||
current->push(wrapper);
|
current->push(wrapper);
|
||||||
|
|
||||||
if (!makeCallBarrier(getter, 0, false, types, barrier))
|
if (!makeCallBarrier(getter, 0, false, false, types, barrier))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
*emitted = true;
|
*emitted = true;
|
||||||
@ -6450,7 +6518,7 @@ IonBuilder::jsop_setprop(HandlePropertyName name)
|
|||||||
|
|
||||||
// Call the setter. Note that we have to push the original value, not
|
// Call the setter. Note that we have to push the original value, not
|
||||||
// the setter's return value.
|
// the setter's return value.
|
||||||
MCall *call = makeCallHelper(setter, 1, false);
|
MCall *call = makeCallHelper(setter, 1, false, false);
|
||||||
if (!call)
|
if (!call)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -419,22 +419,27 @@ class IonBuilder : public MIRGenerator
|
|||||||
bool jsop_call_inline(HandleFunction callee, uint32_t argc, bool constructing,
|
bool jsop_call_inline(HandleFunction callee, uint32_t argc, bool constructing,
|
||||||
MConstant *constFun, MBasicBlock *bottom,
|
MConstant *constFun, MBasicBlock *bottom,
|
||||||
Vector<MDefinition *, 8, IonAllocPolicy> &retvalDefns);
|
Vector<MDefinition *, 8, IonAllocPolicy> &retvalDefns);
|
||||||
bool inlineScriptedCall(AutoObjectVector &targets, uint32_t argc, bool constructing,
|
bool inlineScriptedCall(AutoObjectVector &targets, AutoObjectVector &originals,
|
||||||
|
uint32_t argc, bool constructing,
|
||||||
types::StackTypeSet *types, types::StackTypeSet *barrier);
|
types::StackTypeSet *types, types::StackTypeSet *barrier);
|
||||||
bool makeInliningDecision(AutoObjectVector &targets, uint32_t argc);
|
bool makeInliningDecision(AutoObjectVector &targets, uint32_t argc);
|
||||||
|
|
||||||
|
bool anyFunctionIsCloneAtCallsite(types::StackTypeSet *funTypes);
|
||||||
|
MDefinition *makeCallsiteClone(HandleFunction target, MDefinition *fun);
|
||||||
void popFormals(uint32_t argc, MDefinition **fun, MPassArg **thisArg,
|
void popFormals(uint32_t argc, MDefinition **fun, MPassArg **thisArg,
|
||||||
Vector<MPassArg *> *args);
|
Vector<MPassArg *> *args);
|
||||||
MCall *makeCallHelper(HandleFunction target, bool constructing,
|
MCall *makeCallHelper(HandleFunction target, bool constructing, bool cloneAtCallsite,
|
||||||
MDefinition *fun, MPassArg *thisArg, Vector<MPassArg *> &args);
|
MDefinition *fun, MPassArg *thisArg, Vector<MPassArg *> &args);
|
||||||
MCall *makeCallHelper(HandleFunction target, uint32_t argc, bool constructing);
|
MCall *makeCallHelper(HandleFunction target, uint32_t argc, bool constructing,
|
||||||
|
bool cloneAtCallsite);
|
||||||
bool makeCallBarrier(HandleFunction target, uint32_t argc, bool constructing,
|
bool makeCallBarrier(HandleFunction target, uint32_t argc, bool constructing,
|
||||||
types::StackTypeSet *types, types::StackTypeSet *barrier);
|
bool cloneAtCallsite, types::StackTypeSet *types,
|
||||||
bool makeCallBarrier(HandleFunction target, bool constructing,
|
types::StackTypeSet *barrier);
|
||||||
|
bool makeCallBarrier(HandleFunction target, bool constructing, bool cloneAtCallsite,
|
||||||
MDefinition *fun, MPassArg *thisArg, Vector<MPassArg *> &args,
|
MDefinition *fun, MPassArg *thisArg, Vector<MPassArg *> &args,
|
||||||
types::StackTypeSet *types, types::StackTypeSet *barrier);
|
types::StackTypeSet *types, types::StackTypeSet *barrier);
|
||||||
bool makeCall(HandleFunction target, uint32_t argc, bool constructing);
|
bool makeCall(HandleFunction target, uint32_t argc, bool constructing, bool cloneAtCallsite);
|
||||||
bool makeCall(HandleFunction target, bool constructing,
|
bool makeCall(HandleFunction target, bool constructing, bool cloneAtCallsite,
|
||||||
MDefinition *fun, MPassArg *thisArg, Vector<MPassArg *> &args);
|
MDefinition *fun, MPassArg *thisArg, Vector<MPassArg *> &args);
|
||||||
|
|
||||||
inline bool TestCommonPropFunc(JSContext *cx, types::StackTypeSet *types,
|
inline bool TestCommonPropFunc(JSContext *cx, types::StackTypeSet *types,
|
||||||
@ -448,7 +453,7 @@ class IonBuilder : public MIRGenerator
|
|||||||
MGetPropertyCache *checkInlineableGetPropertyCache(uint32_t argc);
|
MGetPropertyCache *checkInlineableGetPropertyCache(uint32_t argc);
|
||||||
|
|
||||||
MPolyInlineDispatch *
|
MPolyInlineDispatch *
|
||||||
makePolyInlineDispatch(JSContext *cx, AutoObjectVector &targets, int argc,
|
makePolyInlineDispatch(JSContext *cx, int argc,
|
||||||
MGetPropertyCache *getPropCache,
|
MGetPropertyCache *getPropCache,
|
||||||
types::StackTypeSet *types, types::StackTypeSet *barrier,
|
types::StackTypeSet *types, types::StackTypeSet *barrier,
|
||||||
MBasicBlock *bottom,
|
MBasicBlock *bottom,
|
||||||
|
@ -2108,3 +2108,72 @@ js::ion::GetNameCache(JSContext *cx, size_t cacheIndex, HandleObject scopeChain,
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
IonCacheCallsiteClone::attach(JSContext *cx, IonScript *ion, HandleFunction original,
|
||||||
|
HandleFunction clone)
|
||||||
|
{
|
||||||
|
MacroAssembler masm;
|
||||||
|
|
||||||
|
// Guard against object identity on the original.
|
||||||
|
RepatchLabel exit;
|
||||||
|
CodeOffsetJump exitOffset = masm.branchPtrWithPatch(Assembler::NotEqual, calleeReg(),
|
||||||
|
ImmWord(uintptr_t(original.get())), &exit);
|
||||||
|
masm.bind(&exit);
|
||||||
|
|
||||||
|
// Load the clone.
|
||||||
|
masm.movePtr(ImmWord(uintptr_t(clone.get())), outputReg());
|
||||||
|
|
||||||
|
RepatchLabel rejoin;
|
||||||
|
CodeOffsetJump rejoinOffset = masm.jumpWithPatch(&rejoin);
|
||||||
|
masm.bind(&rejoin);
|
||||||
|
|
||||||
|
Linker linker(masm);
|
||||||
|
IonCode *code = linker.newCode(cx);
|
||||||
|
if (!code)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
rejoinOffset.fixup(&masm);
|
||||||
|
exitOffset.fixup(&masm);
|
||||||
|
|
||||||
|
if (ion->invalidated())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
CodeLocationJump rejoinJump(code, rejoinOffset);
|
||||||
|
CodeLocationJump exitJump(code, exitOffset);
|
||||||
|
CodeLocationJump lastJump_ = lastJump();
|
||||||
|
PatchJump(lastJump_, CodeLocationLabel(code));
|
||||||
|
PatchJump(rejoinJump, rejoinLabel());
|
||||||
|
PatchJump(exitJump, cacheLabel());
|
||||||
|
updateLastJump(exitJump);
|
||||||
|
|
||||||
|
IonSpew(IonSpew_InlineCaches, "Generated CALL callee clone stub at %p", code->raw());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSObject *
|
||||||
|
js::ion::CallsiteCloneCache(JSContext *cx, size_t cacheIndex, HandleObject callee)
|
||||||
|
{
|
||||||
|
AutoFlushCache afc ("CallsiteCloneCache");
|
||||||
|
|
||||||
|
// Act as the identity for functions that are not clone-at-callsite, as we
|
||||||
|
// generate this cache as long as some callees are clone-at-callsite.
|
||||||
|
RootedFunction fun(cx, callee->toFunction());
|
||||||
|
if (!fun->isCloneAtCallsite())
|
||||||
|
return fun;
|
||||||
|
|
||||||
|
IonScript *ion = GetTopIonJSScript(cx)->ionScript();
|
||||||
|
IonCacheCallsiteClone &cache = ion->getCache(cacheIndex).toCallsiteClone();
|
||||||
|
|
||||||
|
RootedFunction clone(cx, CloneFunctionAtCallsite(cx, fun, cache.callScript(), cache.callPc()));
|
||||||
|
if (!clone)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (cache.stubCount() < MAX_STUBS) {
|
||||||
|
if (!cache.attach(cx, ion, fun, clone))
|
||||||
|
return NULL;
|
||||||
|
cache.incrementStubCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
return clone;
|
||||||
|
}
|
||||||
|
@ -23,6 +23,7 @@ class IonCacheSetProperty;
|
|||||||
class IonCacheGetElement;
|
class IonCacheGetElement;
|
||||||
class IonCacheBindName;
|
class IonCacheBindName;
|
||||||
class IonCacheName;
|
class IonCacheName;
|
||||||
|
class IonCacheCallsiteClone;
|
||||||
|
|
||||||
// Common structure encoding the state of a polymorphic inline cache contained
|
// Common structure encoding the state of a polymorphic inline cache contained
|
||||||
// in the code for an IonScript. IonCaches are used for polymorphic operations
|
// in the code for an IonScript. IonCaches are used for polymorphic operations
|
||||||
@ -87,7 +88,8 @@ class IonCache
|
|||||||
GetElement,
|
GetElement,
|
||||||
BindName,
|
BindName,
|
||||||
Name,
|
Name,
|
||||||
NameTypeOf
|
NameTypeOf,
|
||||||
|
CallsiteClone
|
||||||
};
|
};
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@ -139,6 +141,12 @@ class IonCache
|
|||||||
PropertyName *name;
|
PropertyName *name;
|
||||||
TypedOrValueRegisterSpace output;
|
TypedOrValueRegisterSpace output;
|
||||||
} name;
|
} name;
|
||||||
|
struct {
|
||||||
|
Register callee;
|
||||||
|
Register output;
|
||||||
|
JSScript *callScript;
|
||||||
|
jsbytecode *callPc;
|
||||||
|
} callsiteclone;
|
||||||
} u;
|
} u;
|
||||||
|
|
||||||
// Registers live after the cache, excluding output registers. The initial
|
// Registers live after the cache, excluding output registers. The initial
|
||||||
@ -236,6 +244,10 @@ class IonCache
|
|||||||
JS_ASSERT(kind_ == Name || kind_ == NameTypeOf);
|
JS_ASSERT(kind_ == Name || kind_ == NameTypeOf);
|
||||||
return *(IonCacheName *)this;
|
return *(IonCacheName *)this;
|
||||||
}
|
}
|
||||||
|
IonCacheCallsiteClone &toCallsiteClone() {
|
||||||
|
JS_ASSERT(kind_ == CallsiteClone);
|
||||||
|
return *(IonCacheCallsiteClone *)this;
|
||||||
|
}
|
||||||
|
|
||||||
void setScriptedLocation(UnrootedScript script, jsbytecode *pc) {
|
void setScriptedLocation(UnrootedScript script, jsbytecode *pc) {
|
||||||
JS_ASSERT(!idempotent_);
|
JS_ASSERT(!idempotent_);
|
||||||
@ -430,6 +442,39 @@ class IonCacheName : public IonCache
|
|||||||
HandleShape shape);
|
HandleShape shape);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class IonCacheCallsiteClone : public IonCache
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
IonCacheCallsiteClone(CodeOffsetJump initialJump,
|
||||||
|
CodeOffsetLabel rejoinLabel,
|
||||||
|
CodeOffsetLabel cacheLabel,
|
||||||
|
RegisterSet liveRegs,
|
||||||
|
Register callee, JSScript *callScript, jsbytecode *callPc,
|
||||||
|
Register output)
|
||||||
|
{
|
||||||
|
init(CallsiteClone, liveRegs, initialJump, rejoinLabel, cacheLabel);
|
||||||
|
u.callsiteclone.callee = callee;
|
||||||
|
u.callsiteclone.callScript = callScript;
|
||||||
|
u.callsiteclone.callPc = callPc;
|
||||||
|
u.callsiteclone.output = output;
|
||||||
|
}
|
||||||
|
|
||||||
|
Register calleeReg() const {
|
||||||
|
return u.callsiteclone.callee;
|
||||||
|
}
|
||||||
|
HandleScript callScript() const {
|
||||||
|
return HandleScript::fromMarkedLocation(&u.callsiteclone.callScript);
|
||||||
|
}
|
||||||
|
jsbytecode *callPc() const {
|
||||||
|
return u.callsiteclone.callPc;
|
||||||
|
}
|
||||||
|
Register outputReg() const {
|
||||||
|
return u.callsiteclone.output;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool attach(JSContext *cx, IonScript *ion, HandleFunction original, HandleFunction clone);
|
||||||
|
};
|
||||||
|
|
||||||
bool
|
bool
|
||||||
GetPropertyCache(JSContext *cx, size_t cacheIndex, HandleObject obj, MutableHandleValue vp);
|
GetPropertyCache(JSContext *cx, size_t cacheIndex, HandleObject obj, MutableHandleValue vp);
|
||||||
|
|
||||||
@ -447,6 +492,9 @@ BindNameCache(JSContext *cx, size_t cacheIndex, HandleObject scopeChain);
|
|||||||
bool
|
bool
|
||||||
GetNameCache(JSContext *cx, size_t cacheIndex, HandleObject scopeChain, MutableHandleValue vp);
|
GetNameCache(JSContext *cx, size_t cacheIndex, HandleObject scopeChain, MutableHandleValue vp);
|
||||||
|
|
||||||
|
JSObject *
|
||||||
|
CallsiteCloneCache(JSContext *cx, size_t cacheIndex, HandleObject callee);
|
||||||
|
|
||||||
} // namespace ion
|
} // namespace ion
|
||||||
} // namespace js
|
} // namespace js
|
||||||
|
|
||||||
|
@ -2811,6 +2811,22 @@ class LCallGetIntrinsicValue : public LCallInstructionHelper<BOX_PIECES, 0, 0>
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class LCallsiteCloneCache : public LInstructionHelper<1, 1, 0>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LIR_HEADER(CallsiteCloneCache);
|
||||||
|
|
||||||
|
LCallsiteCloneCache(const LAllocation &callee) {
|
||||||
|
setOperand(0, callee);
|
||||||
|
}
|
||||||
|
const LAllocation *callee() {
|
||||||
|
return getOperand(0);
|
||||||
|
}
|
||||||
|
const MCallsiteCloneCache *mir() const {
|
||||||
|
return mir_->toCallsiteCloneCache();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Patchable jump to stubs generated for a GetProperty cache, which loads a
|
// Patchable jump to stubs generated for a GetProperty cache, which loads a
|
||||||
// boxed value.
|
// boxed value.
|
||||||
class LGetPropertyCacheV : public LInstructionHelper<BOX_PIECES, 1, 0>
|
class LGetPropertyCacheV : public LInstructionHelper<BOX_PIECES, 1, 0>
|
||||||
|
@ -154,6 +154,7 @@
|
|||||||
_(CallGetProperty) \
|
_(CallGetProperty) \
|
||||||
_(GetNameCache) \
|
_(GetNameCache) \
|
||||||
_(CallGetIntrinsicValue) \
|
_(CallGetIntrinsicValue) \
|
||||||
|
_(CallsiteCloneCache) \
|
||||||
_(CallGetElement) \
|
_(CallGetElement) \
|
||||||
_(CallSetElement) \
|
_(CallSetElement) \
|
||||||
_(CallSetProperty) \
|
_(CallSetProperty) \
|
||||||
|
@ -1884,6 +1884,17 @@ LIRGenerator::visitCallGetIntrinsicValue(MCallGetIntrinsicValue *ins)
|
|||||||
return assignSafepoint(lir, ins);
|
return assignSafepoint(lir, ins);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
LIRGenerator::visitCallsiteCloneCache(MCallsiteCloneCache *ins)
|
||||||
|
{
|
||||||
|
JS_ASSERT(ins->callee()->type() == MIRType_Object);
|
||||||
|
|
||||||
|
LCallsiteCloneCache *lir = new LCallsiteCloneCache(useRegister(ins->callee()));
|
||||||
|
if (!define(lir, ins))
|
||||||
|
return false;
|
||||||
|
return assignSafepoint(lir, ins);
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
LIRGenerator::visitGetPropertyCache(MGetPropertyCache *ins)
|
LIRGenerator::visitGetPropertyCache(MGetPropertyCache *ins)
|
||||||
{
|
{
|
||||||
|
@ -175,6 +175,7 @@ class LIRGenerator : public LIRGeneratorSpecific
|
|||||||
bool visitDeleteProperty(MDeleteProperty *ins);
|
bool visitDeleteProperty(MDeleteProperty *ins);
|
||||||
bool visitGetNameCache(MGetNameCache *ins);
|
bool visitGetNameCache(MGetNameCache *ins);
|
||||||
bool visitCallGetIntrinsicValue(MCallGetIntrinsicValue *ins);
|
bool visitCallGetIntrinsicValue(MCallGetIntrinsicValue *ins);
|
||||||
|
bool visitCallsiteCloneCache(MCallsiteCloneCache *ins);
|
||||||
bool visitCallGetElement(MCallGetElement *ins);
|
bool visitCallGetElement(MCallGetElement *ins);
|
||||||
bool visitCallSetElement(MCallSetElement *ins);
|
bool visitCallSetElement(MCallSetElement *ins);
|
||||||
bool visitSetPropertyCache(MSetPropertyCache *ins);
|
bool visitSetPropertyCache(MSetPropertyCache *ins);
|
||||||
|
@ -395,10 +395,11 @@ MParameter::congruentTo(MDefinition * const &ins) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
MCall *
|
MCall *
|
||||||
MCall::New(JSFunction *target, size_t maxArgc, size_t numActualArgs, bool construct)
|
MCall::New(JSFunction *target, size_t maxArgc, size_t numActualArgs, bool construct,
|
||||||
|
types::StackTypeSet *calleeTypes)
|
||||||
{
|
{
|
||||||
JS_ASSERT(maxArgc >= numActualArgs);
|
JS_ASSERT(maxArgc >= numActualArgs);
|
||||||
MCall *ins = new MCall(target, numActualArgs, construct);
|
MCall *ins = new MCall(target, numActualArgs, construct, calleeTypes);
|
||||||
if (!ins->init(maxArgc + NumNonArgumentOperands))
|
if (!ins->init(maxArgc + NumNonArgumentOperands))
|
||||||
return NULL;
|
return NULL;
|
||||||
return ins;
|
return ins;
|
||||||
|
@ -1171,18 +1171,23 @@ class MCall
|
|||||||
CompilerRootFunction target_;
|
CompilerRootFunction target_;
|
||||||
// Original value of argc from the bytecode.
|
// Original value of argc from the bytecode.
|
||||||
uint32_t numActualArgs_;
|
uint32_t numActualArgs_;
|
||||||
|
// The typeset of the callee, could be NULL.
|
||||||
|
types::StackTypeSet *calleeTypes_;
|
||||||
|
|
||||||
MCall(JSFunction *target, uint32_t numActualArgs, bool construct)
|
MCall(JSFunction *target, uint32_t numActualArgs, bool construct,
|
||||||
|
types::StackTypeSet *calleeTypes)
|
||||||
: construct_(construct),
|
: construct_(construct),
|
||||||
target_(target),
|
target_(target),
|
||||||
numActualArgs_(numActualArgs)
|
numActualArgs_(numActualArgs),
|
||||||
|
calleeTypes_(calleeTypes)
|
||||||
{
|
{
|
||||||
setResultType(MIRType_Value);
|
setResultType(MIRType_Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
INSTRUCTION_HEADER(Call)
|
INSTRUCTION_HEADER(Call)
|
||||||
static MCall *New(JSFunction *target, size_t maxArgc, size_t numActualArgs, bool construct);
|
static MCall *New(JSFunction *target, size_t maxArgc, size_t numActualArgs, bool construct,
|
||||||
|
types::StackTypeSet *calleeTypes);
|
||||||
|
|
||||||
void initPrepareCall(MDefinition *start) {
|
void initPrepareCall(MDefinition *start) {
|
||||||
JS_ASSERT(start->isPrepareCall());
|
JS_ASSERT(start->isPrepareCall());
|
||||||
@ -1214,6 +1219,9 @@ class MCall
|
|||||||
bool isConstructing() const {
|
bool isConstructing() const {
|
||||||
return construct_;
|
return construct_;
|
||||||
}
|
}
|
||||||
|
types::StackTypeSet *calleeTypes() const {
|
||||||
|
return calleeTypes_;
|
||||||
|
}
|
||||||
|
|
||||||
// The number of stack arguments is the max between the number of formal
|
// The number of stack arguments is the max between the number of formal
|
||||||
// arguments and the number of actual arguments. The number of stack
|
// arguments and the number of actual arguments. The number of stack
|
||||||
@ -4343,12 +4351,16 @@ class InlinePropertyTable : public TempObject
|
|||||||
return entries_[i]->func;
|
return entries_[i]->func;
|
||||||
}
|
}
|
||||||
|
|
||||||
void trimToTargets(AutoObjectVector &targets) {
|
void trimToAndMaybePatchTargets(AutoObjectVector &targets, AutoObjectVector &originals) {
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
while (i < numEntries()) {
|
while (i < numEntries()) {
|
||||||
bool foundFunc = false;
|
bool foundFunc = false;
|
||||||
for (size_t j = 0; j < targets.length(); j++) {
|
// Compare using originals, but if we find a matching function,
|
||||||
if (entries_[i]->func == targets[j]) {
|
// patch it to the target, which might be a clone.
|
||||||
|
for (size_t j = 0; j < originals.length(); j++) {
|
||||||
|
if (entries_[i]->func == originals[j]) {
|
||||||
|
if (entries_[i]->func != targets[j])
|
||||||
|
entries_[i] = new Entry(entries_[i]->typeObj, targets[j]->toFunction());
|
||||||
foundFunc = true;
|
foundFunc = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -4976,6 +4988,41 @@ class MCallGetIntrinsicValue : public MNullaryInstruction
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class MCallsiteCloneCache
|
||||||
|
: public MUnaryInstruction,
|
||||||
|
public SingleObjectPolicy
|
||||||
|
{
|
||||||
|
jsbytecode *callPc_;
|
||||||
|
|
||||||
|
MCallsiteCloneCache(MDefinition *callee, jsbytecode *callPc)
|
||||||
|
: MUnaryInstruction(callee),
|
||||||
|
callPc_(callPc)
|
||||||
|
{
|
||||||
|
setResultType(MIRType_Object);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
INSTRUCTION_HEADER(CallsiteCloneCache);
|
||||||
|
|
||||||
|
static MCallsiteCloneCache *New(MDefinition *callee, jsbytecode *callPc) {
|
||||||
|
return new MCallsiteCloneCache(callee, callPc);
|
||||||
|
}
|
||||||
|
TypePolicy *typePolicy() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
MDefinition *callee() const {
|
||||||
|
return getOperand(0);
|
||||||
|
}
|
||||||
|
jsbytecode *callPc() const {
|
||||||
|
return callPc_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Callsite cloning is idempotent.
|
||||||
|
AliasSet getAliasSet() const {
|
||||||
|
return AliasSet::None();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
class MSetPropertyInstruction : public MBinaryInstruction
|
class MSetPropertyInstruction : public MBinaryInstruction
|
||||||
{
|
{
|
||||||
CompilerRootPropertyName name_;
|
CompilerRootPropertyName name_;
|
||||||
|
@ -123,6 +123,7 @@ namespace ion {
|
|||||||
_(CallGetProperty) \
|
_(CallGetProperty) \
|
||||||
_(GetNameCache) \
|
_(GetNameCache) \
|
||||||
_(CallGetIntrinsicValue) \
|
_(CallGetIntrinsicValue) \
|
||||||
|
_(CallsiteCloneCache) \
|
||||||
_(CallGetElement) \
|
_(CallGetElement) \
|
||||||
_(CallSetElement) \
|
_(CallSetElement) \
|
||||||
_(CallSetProperty) \
|
_(CallSetProperty) \
|
||||||
|
@ -47,15 +47,27 @@ ShouldMonitorReturnType(JSFunction *fun)
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
InvokeFunction(JSContext *cx, HandleFunction fun, uint32_t argc, Value *argv, Value *rval)
|
InvokeFunction(JSContext *cx, HandleFunction fun0, uint32_t argc, Value *argv, Value *rval)
|
||||||
{
|
{
|
||||||
AssertCanGC();
|
AssertCanGC();
|
||||||
|
|
||||||
|
RootedFunction fun(cx, fun0);
|
||||||
|
|
||||||
// In order to prevent massive bouncing between Ion and JM, see if we keep
|
// In order to prevent massive bouncing between Ion and JM, see if we keep
|
||||||
// hitting functions that are uncompilable.
|
// hitting functions that are uncompilable.
|
||||||
if (fun->isInterpreted()) {
|
if (fun->isInterpreted()) {
|
||||||
if (fun->isInterpretedLazy() && !JSFunction::getOrCreateScript(cx, fun))
|
if (fun->isInterpretedLazy() && !JSFunction::getOrCreateScript(cx, fun))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
if (fun->isCloneAtCallsite()) {
|
||||||
|
RootedScript script(cx);
|
||||||
|
jsbytecode *pc;
|
||||||
|
types::TypeScript::GetPcScript(cx, &script, &pc);
|
||||||
|
fun = CloneFunctionAtCallsite(cx, fun0, script, pc);
|
||||||
|
if (!fun)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!fun->nonLazyScript()->canIonCompile()) {
|
if (!fun->nonLazyScript()->canIonCompile()) {
|
||||||
UnrootedScript script = GetTopIonJSScript(cx);
|
UnrootedScript script = GetTopIonJSScript(cx);
|
||||||
if (script->hasIonScript() &&
|
if (script->hasIonScript() &&
|
||||||
|
@ -410,7 +410,7 @@ class AutoDetectInvalidation
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
bool InvokeFunction(JSContext *cx, HandleFunction fun, uint32_t argc, Value *argv, Value *rval);
|
bool InvokeFunction(JSContext *cx, HandleFunction fun0, uint32_t argc, Value *argv, Value *rval);
|
||||||
JSObject *NewGCThing(JSContext *cx, gc::AllocKind allocKind, size_t thingSize);
|
JSObject *NewGCThing(JSContext *cx, gc::AllocKind allocKind, size_t thingSize);
|
||||||
|
|
||||||
bool CheckOverRecursed(JSContext *cx);
|
bool CheckOverRecursed(JSContext *cx);
|
||||||
|
Loading…
Reference in New Issue
Block a user