Bug 796114 - Inline with type-checked arguments. r=h4writer

This commit is contained in:
Nicolas B. Pierron 2013-01-30 07:41:01 -08:00
parent a7a4608f44
commit 8a3fa24cd0
17 changed files with 384 additions and 57 deletions

View File

@ -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());

View File

@ -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);

View File

@ -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);

View File

@ -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++)

View File

@ -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();

View File

@ -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)

View File

@ -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);

View File

@ -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>
{

View File

@ -132,6 +132,7 @@
_(ParDump) \
_(TypeBarrier) \
_(MonitorTypes) \
_(ExcludeType) \
_(InitializedLength) \
_(SetInitializedLength) \
_(BoundsCheck) \

View File

@ -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)
{

View File

@ -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);

View File

@ -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));
}

View File

@ -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_;

View File

@ -96,6 +96,7 @@ namespace ion {
_(FunctionEnvironment) \
_(TypeBarrier) \
_(MonitorTypes) \
_(ExcludeType) \
_(GetPropertyCache) \
_(GetElementCache) \
_(BindNameCache) \

View File

@ -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)

View File

@ -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)
{

View File

@ -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);