mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 796114 - Inline with type-checked arguments. r=h4writer
This commit is contained in:
parent
a7a4608f44
commit
8a3fa24cd0
@ -932,10 +932,12 @@ CodeGenerator::visitTypeBarrier(LTypeBarrier *lir)
|
||||
ValueOperand operand = ToValue(lir, LTypeBarrier::Input);
|
||||
Register scratch = ToRegister(lir->temp());
|
||||
|
||||
Label mismatched;
|
||||
masm.guardTypeSet(operand, lir->mir()->typeSet(), scratch, &mismatched);
|
||||
if (!bailoutFrom(&mismatched, lir->snapshot()))
|
||||
Label matched, miss;
|
||||
masm.guardTypeSet(operand, lir->mir()->typeSet(), scratch, &matched, &miss);
|
||||
masm.jump(&miss);
|
||||
if (!bailoutFrom(&miss, lir->snapshot()))
|
||||
return false;
|
||||
masm.bind(&matched);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -945,10 +947,26 @@ CodeGenerator::visitMonitorTypes(LMonitorTypes *lir)
|
||||
ValueOperand operand = ToValue(lir, LMonitorTypes::Input);
|
||||
Register scratch = ToRegister(lir->temp());
|
||||
|
||||
Label mismatched;
|
||||
masm.guardTypeSet(operand, lir->mir()->typeSet(), scratch, &mismatched);
|
||||
if (!bailoutFrom(&mismatched, lir->snapshot()))
|
||||
Label matched, miss;
|
||||
masm.guardTypeSet(operand, lir->mir()->typeSet(), scratch, &matched, &miss);
|
||||
masm.jump(&miss);
|
||||
if (!bailoutFrom(&miss, lir->snapshot()))
|
||||
return false;
|
||||
masm.bind(&matched);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGenerator::visitExcludeType(LExcludeType *lir)
|
||||
{
|
||||
ValueOperand operand = ToValue(lir, LExcludeType::Input);
|
||||
Register scratch = ToRegister(lir->temp());
|
||||
|
||||
Label matched, miss;
|
||||
masm.guardType(operand, lir->mir()->type(), scratch, &matched, &miss);
|
||||
if (matched.used() && !bailoutFrom(&matched, lir->snapshot()))
|
||||
return false;
|
||||
masm.bind(&miss);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1711,7 +1729,7 @@ CodeGenerator::generateArgumentsChecks()
|
||||
JS_ASSERT(info.scopeChainSlot() == 0);
|
||||
static const uint32_t START_SLOT = 1;
|
||||
|
||||
Label mismatched;
|
||||
Label miss;
|
||||
for (uint32_t i = START_SLOT; i < CountArgSlots(info.fun()); i++) {
|
||||
// All initial parameters are guaranteed to be MParameters.
|
||||
MParameter *param = rp->getOperand(i)->toParameter();
|
||||
@ -1722,10 +1740,13 @@ CodeGenerator::generateArgumentsChecks()
|
||||
// Use ReturnReg as a scratch register here, since not all platforms
|
||||
// have an actual ScratchReg.
|
||||
int32_t offset = ArgToStackOffset((i - START_SLOT) * sizeof(Value));
|
||||
masm.guardTypeSet(Address(StackPointer, offset), types, temp, &mismatched);
|
||||
Label matched;
|
||||
masm.guardTypeSet(Address(StackPointer, offset), types, temp, &matched, &miss);
|
||||
masm.jump(&miss);
|
||||
masm.bind(&matched);
|
||||
}
|
||||
|
||||
if (mismatched.used() && !bailoutFrom(&mismatched, graph.entrySnapshot()))
|
||||
if (miss.used() && !bailoutFrom(&miss, graph.entrySnapshot()))
|
||||
return false;
|
||||
|
||||
masm.freeStack(frameSize());
|
||||
|
@ -84,6 +84,7 @@ class CodeGenerator : public CodeGeneratorSpecific
|
||||
bool visitConvertElementsToDoubles(LConvertElementsToDoubles *lir);
|
||||
bool visitTypeBarrier(LTypeBarrier *lir);
|
||||
bool visitMonitorTypes(LMonitorTypes *lir);
|
||||
bool visitExcludeType(LExcludeType *lir);
|
||||
bool visitCallNative(LCallNative *call);
|
||||
bool emitCallInvokeFunction(LInstruction *call, Register callereg,
|
||||
uint32_t argc, uint32_t unusedStack);
|
||||
|
@ -2908,7 +2908,7 @@ IonBuilder::inlineScriptedCall(HandleFunction target, CallInfo &callInfo)
|
||||
{
|
||||
AssertCanGC();
|
||||
JS_ASSERT(target->isInterpreted());
|
||||
JS_ASSERT(callInfo.hasTypeInfo());
|
||||
JS_ASSERT(callInfo.hasCallType());
|
||||
|
||||
// Remove the MPassArg if still present.
|
||||
if (callInfo.isWrapped())
|
||||
@ -2944,9 +2944,21 @@ IonBuilder::inlineScriptedCall(HandleFunction target, CallInfo &callInfo)
|
||||
callInfo.popFormals(current);
|
||||
current->push(callInfo.fun());
|
||||
|
||||
RootedScript calleeScript(cx, target->nonLazyScript());
|
||||
TypeInferenceOracle oracle;
|
||||
if (!oracle.init(cx, calleeScript))
|
||||
return false;
|
||||
|
||||
// Add exclude type barriers.
|
||||
if (callInfo.argsBarrier()) {
|
||||
addTypeBarrier(0, callInfo, oracle.thisTypeSet(calleeScript));
|
||||
int32_t max = (callInfo.argc() < target->nargs) ? callInfo.argc() : target->nargs;
|
||||
for (int32_t i = 1; i <= max; i++)
|
||||
addTypeBarrier(i, callInfo, oracle.parameterTypeSet(calleeScript, i - 1));
|
||||
}
|
||||
|
||||
// Start inlining
|
||||
LifoAlloc *alloc = GetIonContext()->temp->lifoAlloc();
|
||||
RootedScript calleeScript(cx, target->nonLazyScript());
|
||||
CompileInfo *info = alloc->new_<CompileInfo>(calleeScript.get(), target,
|
||||
(jsbytecode *)NULL, callInfo.constructing(),
|
||||
SequentialExecution);
|
||||
@ -2956,10 +2968,6 @@ IonBuilder::inlineScriptedCall(HandleFunction target, CallInfo &callInfo)
|
||||
MIRGraphExits saveExits;
|
||||
AutoAccumulateExits aae(graph(), saveExits);
|
||||
|
||||
TypeInferenceOracle oracle;
|
||||
if (!oracle.init(cx, calleeScript))
|
||||
return false;
|
||||
|
||||
IonBuilder inlineBuilder(cx, &temp(), &graph(), &oracle,
|
||||
info, inliningDepth + 1, loopDepth_);
|
||||
|
||||
@ -3007,6 +3015,68 @@ IonBuilder::inlineScriptedCall(HandleFunction target, CallInfo &callInfo)
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
IonBuilder::addTypeBarrier(uint32_t i, CallInfo &callinfo, types::StackTypeSet *calleeObs)
|
||||
{
|
||||
MDefinition *ins = NULL;
|
||||
types::StackTypeSet *callerObs = NULL;
|
||||
types::TypeBarrier *excluded = callinfo.argsBarrier();
|
||||
|
||||
if (i == 0) {
|
||||
ins = callinfo.thisArg();
|
||||
callerObs = callinfo.thisType();
|
||||
} else {
|
||||
ins = callinfo.getArg(i - 1);
|
||||
callerObs = callinfo.getArgType(i - 1);
|
||||
}
|
||||
|
||||
while (excluded) {
|
||||
if (excluded->target == calleeObs) {
|
||||
JS_ASSERT(callerObs->hasType(excluded->type));
|
||||
|
||||
if (excluded->type == types::Type::DoubleType() &&
|
||||
calleeObs->hasType(types::Type::Int32Type())) {
|
||||
// The double type also implies int32, so this implies that
|
||||
// double should be coerced into int if possible, and other
|
||||
// types should remain.
|
||||
|
||||
JSValueType callerType = callerObs->getKnownTypeTag();
|
||||
if (callerType == JSVAL_TYPE_DOUBLE) {
|
||||
MInstruction *bailType = MToInt32::New(ins);
|
||||
current->add(bailType);
|
||||
ins = bailType;
|
||||
break;
|
||||
} else {
|
||||
// We expect either an Int or a Value, this variant is not
|
||||
// optimized and favor the int variant by filtering out all
|
||||
// other inputs.
|
||||
JS_ASSERT(callerType == JSVAL_TYPE_UNKNOWN);
|
||||
// Bail if the input is not a number.
|
||||
MInstruction *toDouble = MUnbox::New(ins, MIRType_Double, MUnbox::Fallible);
|
||||
// Bail if the double does not fit in an int.
|
||||
MInstruction *toInt = MToInt32::New(ins);
|
||||
current->add(toDouble);
|
||||
current->add(toInt);
|
||||
ins = toInt;
|
||||
}
|
||||
} else {
|
||||
JS_ASSERT(!calleeObs->hasType(excluded->type));
|
||||
|
||||
// Filter out unexpected type which are not yet added to the set
|
||||
// observed type but which are infered by type inference.
|
||||
MInstruction *bailType = MExcludeType::New(ins, excluded->type);
|
||||
current->add(bailType);
|
||||
}
|
||||
}
|
||||
excluded = excluded->next;
|
||||
}
|
||||
|
||||
if (i == 0)
|
||||
callinfo.setThis(ins);
|
||||
else
|
||||
callinfo.setArg(i - 1, ins);
|
||||
}
|
||||
|
||||
MDefinition *
|
||||
IonBuilder::patchInlinedReturn(CallInfo &callInfo, MBasicBlock *exit, MBasicBlock *bottom)
|
||||
{
|
||||
@ -3065,6 +3135,7 @@ IonBuilder::jsop_call_inline(HandleFunction callee, CallInfo &callInfo, MBasicBl
|
||||
Vector<MDefinition *, 8, IonAllocPolicy> &retvalDefns)
|
||||
{
|
||||
AssertCanGC();
|
||||
JS_ASSERT(callInfo.hasCallType());
|
||||
|
||||
// Push formals to capture them in the inline resume point.
|
||||
int calleePos = -((int) callInfo.argc() + 2);
|
||||
@ -3086,8 +3157,20 @@ IonBuilder::jsop_call_inline(HandleFunction callee, CallInfo &callInfo, MBasicBl
|
||||
// Inlining JSOP_FUNCALL uses inlineScriptedCall
|
||||
JS_ASSERT(callInfo.argc() == GET_ARGC(inlineResumePoint->pc()));
|
||||
|
||||
LifoAlloc *alloc = GetIonContext()->temp->lifoAlloc();
|
||||
RootedScript calleeScript(cx, callee->nonLazyScript());
|
||||
TypeInferenceOracle oracle;
|
||||
if (!oracle.init(cx, calleeScript))
|
||||
return false;
|
||||
|
||||
// Add exclude type barriers.
|
||||
if (callInfo.argsBarrier()) {
|
||||
addTypeBarrier(0, callInfo, oracle.thisTypeSet(calleeScript));
|
||||
int32_t max = (callInfo.argc() < callee->nargs) ? callInfo.argc() : callee->nargs;
|
||||
for (int32_t i = 1; i <= max; i++)
|
||||
addTypeBarrier(i, callInfo, oracle.parameterTypeSet(calleeScript, i - 1));
|
||||
}
|
||||
|
||||
LifoAlloc *alloc = GetIonContext()->temp->lifoAlloc();
|
||||
CompileInfo *info = alloc->new_<CompileInfo>(calleeScript.get(), callee,
|
||||
(jsbytecode *)NULL, callInfo.constructing(),
|
||||
SequentialExecution);
|
||||
@ -3097,10 +3180,6 @@ IonBuilder::jsop_call_inline(HandleFunction callee, CallInfo &callInfo, MBasicBl
|
||||
MIRGraphExits saveExits;
|
||||
AutoAccumulateExits aae(graph(), saveExits);
|
||||
|
||||
TypeInferenceOracle oracle;
|
||||
if (!oracle.init(cx, calleeScript))
|
||||
return false;
|
||||
|
||||
IonBuilder inlineBuilder(cx, &temp(), &graph(), &oracle,
|
||||
info, inliningDepth + 1, loopDepth_);
|
||||
|
||||
@ -3508,14 +3587,19 @@ IonBuilder::inlineScriptedCalls(AutoObjectVector &targets, AutoObjectVector &ori
|
||||
// guaranteed to fail.
|
||||
JSFunction *func = originals[i]->toFunction();
|
||||
MConstant *constFun = MConstant::New(ObjectValue(*func));
|
||||
top->add(constFun);
|
||||
|
||||
// Create new entry block for the inlined callee graph.
|
||||
MBasicBlock *entryBlock = newBlock(current, pc);
|
||||
if (!entryBlock)
|
||||
return false;
|
||||
|
||||
// Modify the resume point to account for the callee selection.
|
||||
int funIndex = entryBlock->entryResumePoint()->numOperands();
|
||||
funIndex -= ((int) callInfo.argc() + 2);
|
||||
entryBlock->entryResumePoint()->replaceOperand(funIndex, constFun);
|
||||
|
||||
// Add case to PolyInlineDispatch
|
||||
entryBlock->add(constFun);
|
||||
disp->addCallee(constFun, entryBlock);
|
||||
}
|
||||
top->end(disp);
|
||||
@ -4035,6 +4119,9 @@ IonBuilder::jsop_funapplyarguments(uint32_t argc)
|
||||
if (!args.append(inlinedArguments_.begin(), inlinedArguments_.end()))
|
||||
return false;
|
||||
callInfo.setArgs(&args);
|
||||
RootedScript scriptRoot(cx, script());
|
||||
if (!callInfo.initFunApplyArguments(oracle, scriptRoot, pc, info().nargs()))
|
||||
return false;
|
||||
|
||||
// This
|
||||
MPassArg *passThis = current->pop()->toPassArg();
|
||||
@ -4119,6 +4206,8 @@ IonBuilder::jsop_call(uint32_t argc, bool constructing)
|
||||
}
|
||||
|
||||
// Inline scriped call(s).
|
||||
if (!callInfo.initCallType(oracle, scriptRoot, pc))
|
||||
return false;
|
||||
if (inliningEnabled() && targets.length() > 0 && makeInliningDecision(targets))
|
||||
return inlineScriptedCalls(targets, originals, callInfo);
|
||||
|
||||
|
@ -277,7 +277,17 @@ class IonBuilder : public MIRGenerator
|
||||
void rewriteParameters();
|
||||
bool initScopeChain();
|
||||
bool pushConstant(const Value &v);
|
||||
|
||||
// Add a guard which ensure that the set of type which goes through this
|
||||
// generated code correspond to the observed or infered (actual) type.
|
||||
bool pushTypeBarrier(MInstruction *ins, types::StackTypeSet *actual, types::StackTypeSet *observed);
|
||||
|
||||
// Add a guard which ensure that the set of type does not go through. Some
|
||||
// instructions, such as function calls, can have an excluded set of types
|
||||
// which would be added after invalidation if they are observed in the
|
||||
// callee.
|
||||
void addTypeBarrier(uint32_t i, CallInfo &callinfo, types::StackTypeSet *calleeObs);
|
||||
|
||||
void monitorResult(MInstruction *ins, types::TypeSet *barrier, types::StackTypeSet *types);
|
||||
|
||||
JSObject *getSingletonPrototype(JSFunction *target);
|
||||
@ -455,7 +465,7 @@ class IonBuilder : public MIRGenerator
|
||||
types::StackTypeSet *calleeTypes, bool cloneAtCallsite);
|
||||
bool makeCallBarrier(HandleFunction target, CallInfo &callInfo,
|
||||
types::StackTypeSet *calleeTypes, bool cloneAtCallsite);
|
||||
bool makeCall(HandleFunction target, CallInfo &callInfo,
|
||||
bool makeCall(HandleFunction target, CallInfo &callInfo,
|
||||
types::StackTypeSet *calleeTypes, bool cloneAtCallsite);
|
||||
|
||||
MDefinition *patchInlinedReturn(CallInfo &callInfo, MBasicBlock *exit, MBasicBlock *bottom);
|
||||
@ -542,6 +552,10 @@ class CallInfo
|
||||
types::StackTypeSet *barrier_;
|
||||
types::StackTypeSet *types_;
|
||||
|
||||
types::TypeBarrier *argsBarriers_;
|
||||
types::StackTypeSet *thisType_;
|
||||
Vector<types::StackTypeSet *> argsType_;
|
||||
|
||||
MDefinition *fun_;
|
||||
MDefinition *thisArg_;
|
||||
Vector<MDefinition *> args_;
|
||||
@ -552,6 +566,9 @@ class CallInfo
|
||||
CallInfo(JSContext *cx, bool constructing)
|
||||
: barrier_(NULL),
|
||||
types_(NULL),
|
||||
argsBarriers_(NULL),
|
||||
thisType_(NULL),
|
||||
argsType_(cx),
|
||||
fun_(NULL),
|
||||
thisArg_(NULL),
|
||||
args_(cx),
|
||||
@ -562,6 +579,9 @@ class CallInfo
|
||||
types::StackTypeSet *types, types::StackTypeSet *barrier)
|
||||
: barrier_(barrier),
|
||||
types_(types),
|
||||
argsBarriers_(NULL),
|
||||
thisType_(NULL),
|
||||
argsType_(cx),
|
||||
fun_(NULL),
|
||||
thisArg_(NULL),
|
||||
args_(cx),
|
||||
@ -571,6 +591,10 @@ class CallInfo
|
||||
bool init(CallInfo &callInfo) {
|
||||
JS_ASSERT(constructing_ == callInfo.constructing());
|
||||
|
||||
thisType_ = callInfo.thisType_;
|
||||
if (thisType_ && !argsType_.append(callInfo.argsType_.begin(), callInfo.argsType_.end()))
|
||||
return false;
|
||||
|
||||
fun_ = callInfo.fun();
|
||||
thisArg_ = callInfo.thisArg();
|
||||
|
||||
@ -589,10 +613,8 @@ class CallInfo
|
||||
// Get the arguments in the right order
|
||||
if (!args_.reserve(argc))
|
||||
return false;
|
||||
for (int32_t i = argc; i > 0; i--) {
|
||||
if (!args_.append(current->peek(-i)))
|
||||
return false;
|
||||
}
|
||||
for (int32_t i = argc; i > 0; i--)
|
||||
args_.infallibleAppend(current->peek(-i));
|
||||
current->popn(argc);
|
||||
|
||||
// Get |this| and |fun|
|
||||
@ -602,6 +624,33 @@ class CallInfo
|
||||
return true;
|
||||
}
|
||||
|
||||
bool initCallType(TypeOracle *oracle, HandleScript script, jsbytecode *pc) {
|
||||
argsBarriers_ = oracle->callArgsBarrier(script, pc);
|
||||
thisType_ = oracle->getCallTarget(script, argc(), pc);
|
||||
if (!argsType_.reserve(argc()))
|
||||
return false;
|
||||
for (uint32_t i = 1; i <= argc(); i++)
|
||||
argsType_.infallibleAppend(oracle->getCallArg(script, argc(), i, pc));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool initFunApplyArguments(TypeOracle *oracle, HandleScript script, jsbytecode *pc, uint32_t nargs) {
|
||||
argsBarriers_ = oracle->callArgsBarrier(script, pc);
|
||||
thisType_ = oracle->getCallArg(script, 2, 0, pc);
|
||||
if (!argsType_.reserve(nargs))
|
||||
return false;
|
||||
for (uint32_t i = 0; i < nargs; i++)
|
||||
argsType_.infallibleAppend(oracle->parameterTypeSet(script, i));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool hasCallType() {
|
||||
// argsBarriers can be NULL is the caller does not need to guard its
|
||||
// arguments again some value type expected by callees. On the other
|
||||
// hand we would always have a thisType if the type info is initialized.
|
||||
return hasTypeInfo() && thisType_;
|
||||
}
|
||||
|
||||
void popFormals(MBasicBlock *current) {
|
||||
current->popn(argc() + 2);
|
||||
}
|
||||
@ -637,20 +686,43 @@ class CallInfo
|
||||
return &args_;
|
||||
}
|
||||
|
||||
Vector<types::StackTypeSet *> &argvType() {
|
||||
return argsType_;
|
||||
}
|
||||
|
||||
MDefinition *getArg(uint32_t i) {
|
||||
JS_ASSERT(i < argc());
|
||||
return args_[i];
|
||||
}
|
||||
|
||||
types::StackTypeSet *getArgType(uint32_t i) {
|
||||
JS_ASSERT(i < argc() && argc() == argsType_.length());
|
||||
return argsType_[i];
|
||||
}
|
||||
|
||||
void setArg(uint32_t i, MDefinition *def) {
|
||||
JS_ASSERT(i < argc());
|
||||
args_[i] = def;
|
||||
}
|
||||
|
||||
MDefinition *thisArg() {
|
||||
JS_ASSERT(thisArg_);
|
||||
return thisArg_;
|
||||
}
|
||||
|
||||
types::StackTypeSet *thisType() {
|
||||
JS_ASSERT(thisType_);
|
||||
return thisType_;
|
||||
}
|
||||
|
||||
void setThis(MDefinition *thisArg) {
|
||||
thisArg_ = thisArg;
|
||||
}
|
||||
|
||||
void setThisType(types::StackTypeSet *thisType) {
|
||||
thisType_ = thisType;
|
||||
}
|
||||
|
||||
bool constructing() {
|
||||
return constructing_;
|
||||
}
|
||||
@ -663,6 +735,10 @@ class CallInfo
|
||||
return barrier_;
|
||||
}
|
||||
|
||||
types::TypeBarrier *argsBarrier() {
|
||||
return argsBarriers_;
|
||||
}
|
||||
|
||||
void wrapArgs(MBasicBlock *current) {
|
||||
thisArg_ = wrap(current, thisArg_);
|
||||
for (uint32_t i = 0; i < argc(); i++)
|
||||
|
@ -331,7 +331,7 @@ ion::HandleException(ResumeFromException *rfe)
|
||||
++frames;
|
||||
}
|
||||
|
||||
IonScript *ionScript;
|
||||
IonScript *ionScript = NULL;
|
||||
if (iter.checkInvalidation(&ionScript))
|
||||
ionScript->decref(cx->runtime->defaultFreeOp());
|
||||
}
|
||||
@ -456,7 +456,7 @@ MarkIonJSFrame(JSTracer *trc, const IonFrameIterator &frame)
|
||||
|
||||
MarkCalleeToken(trc, layout->calleeToken());
|
||||
|
||||
IonScript *ionScript;
|
||||
IonScript *ionScript = NULL;
|
||||
if (frame.checkInvalidation(&ionScript)) {
|
||||
// This frame has been invalidated, meaning that its IonScript is no
|
||||
// longer reachable through the callee token (JSFunction/JSScript->ion
|
||||
@ -876,7 +876,7 @@ IonFrameIterator::ionScript() const
|
||||
{
|
||||
JS_ASSERT(type() == IonFrame_OptimizedJS);
|
||||
|
||||
IonScript *ionScript;
|
||||
IonScript *ionScript = NULL;
|
||||
if (checkInvalidation(&ionScript))
|
||||
return ionScript;
|
||||
return script()->ionScript();
|
||||
|
@ -15,60 +15,112 @@
|
||||
using namespace js;
|
||||
using namespace js::ion;
|
||||
|
||||
template <typename T> void
|
||||
MacroAssembler::guardTypeSet(const T &address, const types::TypeSet *types,
|
||||
Register scratch, Label *mismatched)
|
||||
// Emulate a TypeSet logic from a Type object to avoid duplicating the guard
|
||||
// logic.
|
||||
class TypeWrapper {
|
||||
types::Type t_;
|
||||
|
||||
public:
|
||||
TypeWrapper(types::Type t) : t_(t) {}
|
||||
|
||||
inline bool unknown() const {
|
||||
return t_.isUnknown();
|
||||
}
|
||||
inline bool hasType(types::Type t) const {
|
||||
if (t == types::Type::Int32Type())
|
||||
return t == t_ || t_ == types::Type::DoubleType();
|
||||
return t == t_;
|
||||
}
|
||||
inline unsigned getObjectCount() const {
|
||||
if (t_.isAnyObject() || t_.isUnknown() || !t_.isObject())
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
inline JSObject *getSingleObject(unsigned) const {
|
||||
if (t_.isSingleObject())
|
||||
return t_.singleObject();
|
||||
return NULL;
|
||||
}
|
||||
inline types::TypeObject *getTypeObject(unsigned) const {
|
||||
if (t_.isTypeObject())
|
||||
return t_.typeObject();
|
||||
return NULL;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Source, typename TypeSet> void
|
||||
MacroAssembler::guardTypeSet(const Source &address, const TypeSet *types,
|
||||
Register scratch, Label *matched, Label *miss)
|
||||
{
|
||||
JS_ASSERT(!types->unknown());
|
||||
|
||||
Label matched;
|
||||
Register tag = extractTag(address, scratch);
|
||||
|
||||
if (types->hasType(types::Type::DoubleType())) {
|
||||
// The double type also implies Int32.
|
||||
JS_ASSERT(types->hasType(types::Type::Int32Type()));
|
||||
branchTestNumber(Equal, tag, &matched);
|
||||
branchTestNumber(Equal, tag, matched);
|
||||
} else if (types->hasType(types::Type::Int32Type())) {
|
||||
branchTestInt32(Equal, tag, &matched);
|
||||
branchTestInt32(Equal, tag, matched);
|
||||
}
|
||||
|
||||
if (types->hasType(types::Type::UndefinedType()))
|
||||
branchTestUndefined(Equal, tag, &matched);
|
||||
branchTestUndefined(Equal, tag, matched);
|
||||
if (types->hasType(types::Type::BooleanType()))
|
||||
branchTestBoolean(Equal, tag, &matched);
|
||||
branchTestBoolean(Equal, tag, matched);
|
||||
if (types->hasType(types::Type::StringType()))
|
||||
branchTestString(Equal, tag, &matched);
|
||||
branchTestString(Equal, tag, matched);
|
||||
if (types->hasType(types::Type::NullType()))
|
||||
branchTestNull(Equal, tag, &matched);
|
||||
branchTestNull(Equal, tag, matched);
|
||||
|
||||
if (types->hasType(types::Type::AnyObjectType())) {
|
||||
branchTestObject(Equal, tag, &matched);
|
||||
branchTestObject(Equal, tag, matched);
|
||||
} else if (types->getObjectCount()) {
|
||||
branchTestObject(NotEqual, tag, mismatched);
|
||||
branchTestObject(NotEqual, tag, miss);
|
||||
Register obj = extractObject(address, scratch);
|
||||
|
||||
unsigned count = types->getObjectCount();
|
||||
for (unsigned i = 0; i < count; i++) {
|
||||
if (JSObject *object = types->getSingleObject(i))
|
||||
branchPtr(Equal, obj, ImmGCPtr(object), &matched);
|
||||
branchPtr(Equal, obj, ImmGCPtr(object), matched);
|
||||
}
|
||||
|
||||
loadPtr(Address(obj, JSObject::offsetOfType()), scratch);
|
||||
|
||||
for (unsigned i = 0; i < count; i++) {
|
||||
if (types::TypeObject *object = types->getTypeObject(i))
|
||||
branchPtr(Equal, scratch, ImmGCPtr(object), &matched);
|
||||
branchPtr(Equal, scratch, ImmGCPtr(object), matched);
|
||||
}
|
||||
}
|
||||
|
||||
jump(mismatched);
|
||||
bind(&matched);
|
||||
}
|
||||
|
||||
template <typename Source> void
|
||||
MacroAssembler::guardType(const Source &address, types::Type type,
|
||||
Register scratch, Label *matched, Label *miss)
|
||||
{
|
||||
TypeWrapper wrapper(type);
|
||||
guardTypeSet(address, &wrapper, scratch, matched, miss);
|
||||
}
|
||||
|
||||
template void MacroAssembler::guardTypeSet(const Address &address, const types::StackTypeSet *types,
|
||||
Register scratch, Label *matched, Label *miss);
|
||||
template void MacroAssembler::guardTypeSet(const ValueOperand &value, const types::StackTypeSet *types,
|
||||
Register scratch, Label *matched, Label *miss);
|
||||
|
||||
template void MacroAssembler::guardTypeSet(const Address &address, const types::TypeSet *types,
|
||||
Register scratch, Label *mismatched);
|
||||
Register scratch, Label *matched, Label *miss);
|
||||
template void MacroAssembler::guardTypeSet(const ValueOperand &value, const types::TypeSet *types,
|
||||
Register scratch, Label *mismatched);
|
||||
Register scratch, Label *matched, Label *miss);
|
||||
|
||||
template void MacroAssembler::guardTypeSet(const Address &address, const TypeWrapper *types,
|
||||
Register scratch, Label *matched, Label *miss);
|
||||
template void MacroAssembler::guardTypeSet(const ValueOperand &value, const TypeWrapper *types,
|
||||
Register scratch, Label *matched, Label *miss);
|
||||
|
||||
template void MacroAssembler::guardType(const Address &address, types::Type type,
|
||||
Register scratch, Label *matched, Label *miss);
|
||||
template void MacroAssembler::guardType(const ValueOperand &value, types::Type type,
|
||||
Register scratch, Label *matched, Label *miss);
|
||||
|
||||
void
|
||||
MacroAssembler::PushRegsInMask(RegisterSet set)
|
||||
|
@ -122,9 +122,12 @@ class MacroAssembler : public MacroAssemblerSpecific
|
||||
|
||||
// Emits a test of a value against all types in a TypeSet. A scratch
|
||||
// register is required.
|
||||
template <typename T>
|
||||
void guardTypeSet(const T &address, const types::TypeSet *types, Register scratch,
|
||||
Label *mismatched);
|
||||
template <typename Source, typename TypeSet>
|
||||
void guardTypeSet(const Source &address, const TypeSet *types, Register scratch,
|
||||
Label *matched, Label *miss);
|
||||
template <typename Source>
|
||||
void guardType(const Source &address, types::Type type, Register scratch,
|
||||
Label *matched, Label *miss);
|
||||
|
||||
void loadObjShape(Register objReg, Register dest) {
|
||||
loadPtr(Address(objReg, JSObject::offsetOfShape()), dest);
|
||||
|
@ -3711,6 +3711,27 @@ class LMonitorTypes : public LInstructionHelper<0, BOX_PIECES, 1>
|
||||
}
|
||||
};
|
||||
|
||||
// Guard that a value is in a TypeSet.
|
||||
class LExcludeType : public LInstructionHelper<0, BOX_PIECES, 1>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(ExcludeType);
|
||||
BOX_OUTPUT_ACCESSORS();
|
||||
|
||||
LExcludeType(const LDefinition &temp) {
|
||||
setTemp(0, temp);
|
||||
}
|
||||
|
||||
static const size_t Input = 0;
|
||||
|
||||
const MExcludeType *mir() const {
|
||||
return mir_->toExcludeType();
|
||||
}
|
||||
const LDefinition *temp() {
|
||||
return getTemp(0);
|
||||
}
|
||||
};
|
||||
|
||||
// Guard against an object's class.
|
||||
class LGuardClass : public LInstructionHelper<0, 1, 1>
|
||||
{
|
||||
|
@ -132,6 +132,7 @@
|
||||
_(ParDump) \
|
||||
_(TypeBarrier) \
|
||||
_(MonitorTypes) \
|
||||
_(ExcludeType) \
|
||||
_(InitializedLength) \
|
||||
_(SetInitializedLength) \
|
||||
_(BoundsCheck) \
|
||||
|
@ -1633,6 +1633,18 @@ LIRGenerator::visitMonitorTypes(MMonitorTypes *ins)
|
||||
return assignSnapshot(lir, Bailout_Monitor) && add(lir, ins);
|
||||
}
|
||||
|
||||
bool
|
||||
LIRGenerator::visitExcludeType(MExcludeType *ins)
|
||||
{
|
||||
LExcludeType *filter = new LExcludeType(temp());
|
||||
if (!useBox(filter, LExcludeType::Input, ins->input()))
|
||||
return false;
|
||||
if (!assignSnapshot(filter, ins->bailoutKind()))
|
||||
return false;
|
||||
filter->setMir(ins);
|
||||
return add(filter);
|
||||
}
|
||||
|
||||
bool
|
||||
LIRGenerator::visitArrayLength(MArrayLength *ins)
|
||||
{
|
||||
|
@ -158,6 +158,7 @@ class LIRGenerator : public LIRGeneratorSpecific
|
||||
bool visitStoreSlot(MStoreSlot *ins);
|
||||
bool visitTypeBarrier(MTypeBarrier *ins);
|
||||
bool visitMonitorTypes(MMonitorTypes *ins);
|
||||
bool visitExcludeType(MExcludeType *ins);
|
||||
bool visitArrayLength(MArrayLength *ins);
|
||||
bool visitTypedArrayLength(MTypedArrayLength *ins);
|
||||
bool visitTypedArrayElements(MTypedArrayElements *ins);
|
||||
|
@ -1599,7 +1599,7 @@ MTruncateToInt32::foldsTo(bool useValueNumbers)
|
||||
|
||||
if (input->type() == MIRType_Double && input->isConstant()) {
|
||||
const Value &v = input->toConstant()->value();
|
||||
uint32_t ret = ToInt32(v.toDouble());
|
||||
int32_t ret = ToInt32(v.toDouble());
|
||||
return MConstant::New(Int32Value(ret));
|
||||
}
|
||||
|
||||
|
@ -1722,6 +1722,7 @@ class MBox : public MUnaryInstruction
|
||||
|
||||
return new MBox(ins);
|
||||
}
|
||||
|
||||
bool congruentTo(MDefinition *const &ins) const {
|
||||
return congruentIfOperandsEqual(ins);
|
||||
}
|
||||
@ -6188,6 +6189,7 @@ class MTypeBarrier : public MUnaryInstruction
|
||||
static MTypeBarrier *New(MDefinition *def, const types::StackTypeSet *types) {
|
||||
return new MTypeBarrier(def, types);
|
||||
}
|
||||
|
||||
bool congruentTo(MDefinition * const &def) const {
|
||||
return false;
|
||||
}
|
||||
@ -6242,6 +6244,46 @@ class MMonitorTypes : public MUnaryInstruction
|
||||
}
|
||||
};
|
||||
|
||||
// Guards that the incoming value does not have the specified Type.
|
||||
class MExcludeType
|
||||
: public MUnaryInstruction,
|
||||
public BoxInputsPolicy
|
||||
{
|
||||
types::Type type_;
|
||||
|
||||
MExcludeType(MDefinition *def, types::Type type)
|
||||
: MUnaryInstruction(def),
|
||||
type_(type)
|
||||
{
|
||||
setGuard();
|
||||
setMovable();
|
||||
}
|
||||
|
||||
public:
|
||||
INSTRUCTION_HEADER(ExcludeType);
|
||||
|
||||
static MExcludeType *New(MDefinition *def, types::Type type) {
|
||||
return new MExcludeType(def, type);
|
||||
}
|
||||
|
||||
MDefinition *input() const {
|
||||
return getOperand(0);
|
||||
}
|
||||
BailoutKind bailoutKind() const {
|
||||
return Bailout_Normal;
|
||||
}
|
||||
types::Type type() const {
|
||||
return type_;
|
||||
}
|
||||
|
||||
TypePolicy *typePolicy() {
|
||||
return this;
|
||||
}
|
||||
AliasSet getAliasSet() const {
|
||||
return AliasSet::None();
|
||||
}
|
||||
};
|
||||
|
||||
class MNewSlots : public MNullaryInstruction
|
||||
{
|
||||
unsigned nslots_;
|
||||
|
@ -96,6 +96,7 @@ namespace ion {
|
||||
_(FunctionEnvironment) \
|
||||
_(TypeBarrier) \
|
||||
_(MonitorTypes) \
|
||||
_(ExcludeType) \
|
||||
_(GetPropertyCache) \
|
||||
_(GetElementCache) \
|
||||
_(BindNameCache) \
|
||||
|
@ -192,6 +192,7 @@ class ParallelArrayVisitor : public MInstructionVisitor
|
||||
SAFE_OP(FunctionEnvironment) // just a load of func env ptr
|
||||
SAFE_OP(TypeBarrier) // causes a bailout if the type is not found: a-ok with us
|
||||
SAFE_OP(MonitorTypes) // causes a bailout if the type is not found: a-ok with us
|
||||
SAFE_OP(ExcludeType) // causes a bailout if the type is not found: a-ok with us
|
||||
UNSAFE_OP(GetPropertyCache)
|
||||
UNSAFE_OP(GetElementCache)
|
||||
UNSAFE_OP(BindNameCache)
|
||||
|
@ -587,14 +587,16 @@ TypeInferenceOracle::canInlineCall(HandleScript caller, jsbytecode *pc)
|
||||
// Ignore code->monitoredTypes, as we know the caller is foo
|
||||
if (op != JSOP_FUNAPPLY && code->monitoredTypes)
|
||||
return false;
|
||||
|
||||
// Gets removed in Bug 796114
|
||||
if (caller->analysis()->typeBarriers(cx, pc))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
types::TypeBarrier*
|
||||
TypeInferenceOracle::callArgsBarrier(HandleScript caller, jsbytecode *pc)
|
||||
{
|
||||
JS_ASSERT(types::IsInlinableCall(pc));
|
||||
return caller->analysis()->typeBarriers(cx, pc);
|
||||
}
|
||||
|
||||
bool
|
||||
TypeInferenceOracle::canEnterInlinedFunction(RawScript caller, jsbytecode *pc, RawFunction target)
|
||||
{
|
||||
|
@ -163,6 +163,9 @@ class TypeOracle
|
||||
virtual bool canInlineCall(HandleScript caller, jsbytecode *pc) {
|
||||
return false;
|
||||
}
|
||||
virtual types::TypeBarrier *callArgsBarrier(HandleScript caller, jsbytecode *pc) {
|
||||
return NULL;
|
||||
}
|
||||
virtual bool canEnterInlinedFunction(RawScript caller, jsbytecode *pc, RawFunction callee) {
|
||||
return false;
|
||||
}
|
||||
@ -273,6 +276,7 @@ class TypeInferenceOracle : public TypeOracle
|
||||
MIRType elementWrite(UnrootedScript script, jsbytecode *pc);
|
||||
bool canInlineCalls();
|
||||
bool canInlineCall(HandleScript caller, jsbytecode *pc);
|
||||
types::TypeBarrier *callArgsBarrier(HandleScript caller, jsbytecode *pc);
|
||||
bool canEnterInlinedFunction(RawScript caller, jsbytecode *pc, RawFunction callee);
|
||||
types::StackTypeSet *aliasedVarBarrier(UnrootedScript script, jsbytecode *pc, types::StackTypeSet **barrier);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user