Bug 1139152 - IonMonkey: Add dynamic output type checks for LIRs that use redefine, r=jandem

This commit is contained in:
Hannes Verschore 2015-03-18 10:08:39 +01:00
Родитель 9bde438ed5
Коммит 1e5553841b
24 изменённых файлов: 187 добавлений и 104 удалений

Просмотреть файл

@ -0,0 +1,25 @@
function toLiteralSource(value) {
if (value === null) {
return 'null';
}
if (typeof value === 'string') {
return escapeString(value);
}
if (typeof value === 'number') {
return generateNumber(value);
}
if (typeof value === 'boolean') {
return value ? 'true' : 'false';
}
value.test();
}
function test(x) {
var b = x ? true : {};
return toLiteralSource(b);
}
var output = true
for (var i=0; i<1000; i++) {
output = test(output) == 'true';
}

Просмотреть файл

@ -3740,7 +3740,6 @@ struct ScriptCountBlockState
}
};
#ifdef DEBUG
void
CodeGenerator::branchIfInvalidated(Register temp, Label *invalidated)
{
@ -3755,16 +3754,13 @@ CodeGenerator::branchIfInvalidated(Register temp, Label *invalidated)
}
void
CodeGenerator::emitObjectOrStringResultChecks(LInstruction *lir, MDefinition *mir)
CodeGenerator::emitAssertObjectOrStringResult(Register input, MIRType type, TemporaryTypeSet *typeset)
{
if (lir->numDefs() == 0)
return;
MOZ_ASSERT(lir->numDefs() == 1);
Register output = ToRegister(lir->getDef(0));
MOZ_ASSERT(type == MIRType_Object || type == MIRType_ObjectOrNull ||
type == MIRType_String || type == MIRType_Symbol);
GeneralRegisterSet regs(GeneralRegisterSet::All());
regs.take(output);
regs.take(input);
Register temp = regs.takeAny();
masm.push(temp);
@ -3774,22 +3770,21 @@ CodeGenerator::emitObjectOrStringResultChecks(LInstruction *lir, MDefinition *mi
Label done;
branchIfInvalidated(temp, &done);
if ((mir->type() == MIRType_Object || mir->type() == MIRType_ObjectOrNull) &&
mir->resultTypeSet() &&
!mir->resultTypeSet()->unknownObject())
if ((type == MIRType_Object || type == MIRType_ObjectOrNull) &&
typeset && !typeset->unknownObject())
{
// We have a result TypeSet, assert this object is in it.
Label miss, ok;
if (mir->type() == MIRType_ObjectOrNull)
masm.branchPtr(Assembler::Equal, output, ImmWord(0), &ok);
if (mir->resultTypeSet()->getObjectCount() > 0)
masm.guardObjectType(output, mir->resultTypeSet(), temp, &miss);
if (type == MIRType_ObjectOrNull)
masm.branchPtr(Assembler::Equal, input, ImmWord(0), &ok);
if (typeset->getObjectCount() > 0)
masm.guardObjectType(input, typeset, temp, &miss);
else
masm.jump(&miss);
masm.jump(&ok);
masm.bind(&miss);
masm.guardTypeSetMightBeIncomplete(output, temp, &ok);
masm.guardTypeSetMightBeIncomplete(input, temp, &ok);
masm.assumeUnreachable("MIR instruction returned object with unexpected type");
@ -3801,10 +3796,10 @@ CodeGenerator::emitObjectOrStringResultChecks(LInstruction *lir, MDefinition *mi
masm.setupUnalignedABICall(2, temp);
masm.loadJSContext(temp);
masm.passABIArg(temp);
masm.passABIArg(output);
masm.passABIArg(input);
void *callee;
switch (mir->type()) {
switch (type) {
case MIRType_Object:
callee = JS_FUNC_TO_DATA_PTR(void *, AssertValidObjectPtr);
break;
@ -3829,19 +3824,10 @@ CodeGenerator::emitObjectOrStringResultChecks(LInstruction *lir, MDefinition *mi
}
void
CodeGenerator::emitValueResultChecks(LInstruction *lir, MDefinition *mir)
CodeGenerator::emitAssertResultV(const ValueOperand input, TemporaryTypeSet *typeset)
{
if (lir->numDefs() == 0)
return;
MOZ_ASSERT(lir->numDefs() == BOX_PIECES);
if (!lir->getDef(0)->output()->isRegister())
return;
ValueOperand output = ToOutValue(lir);
GeneralRegisterSet regs(GeneralRegisterSet::All());
regs.take(output);
regs.take(input);
Register temp1 = regs.takeAny();
Register temp2 = regs.takeAny();
@ -3853,10 +3839,10 @@ CodeGenerator::emitValueResultChecks(LInstruction *lir, MDefinition *mir)
Label done;
branchIfInvalidated(temp1, &done);
if (mir->resultTypeSet() && !mir->resultTypeSet()->unknown()) {
if (typeset && !typeset->unknown()) {
// We have a result TypeSet, assert this value is in it.
Label miss, ok;
masm.guardTypeSet(output, mir->resultTypeSet(), BarrierKind::TypeSet, temp1, &miss);
masm.guardTypeSet(input, typeset, BarrierKind::TypeSet, temp1, &miss);
masm.jump(&ok);
masm.bind(&miss);
@ -3864,8 +3850,8 @@ CodeGenerator::emitValueResultChecks(LInstruction *lir, MDefinition *mir)
// Check for cases where the type set guard might have missed due to
// changing object groups.
Label realMiss;
masm.branchTestObject(Assembler::NotEqual, output, &realMiss);
Register payload = masm.extractObject(output, temp1);
masm.branchTestObject(Assembler::NotEqual, input, &realMiss);
Register payload = masm.extractObject(input, temp1);
masm.guardTypeSetMightBeIncomplete(payload, temp1, &ok);
masm.bind(&realMiss);
@ -3877,7 +3863,7 @@ CodeGenerator::emitValueResultChecks(LInstruction *lir, MDefinition *mir)
// Check that we have a valid GC pointer.
saveVolatile();
masm.pushValue(output);
masm.pushValue(input);
masm.movePtr(StackPointer, temp1);
masm.setupUnalignedABICall(2, temp2);
@ -3885,7 +3871,7 @@ CodeGenerator::emitValueResultChecks(LInstruction *lir, MDefinition *mir)
masm.passABIArg(temp2);
masm.passABIArg(temp1);
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, AssertValidValue));
masm.popValue(output);
masm.popValue(input);
restoreVolatile();
masm.bind(&done);
@ -3893,6 +3879,34 @@ CodeGenerator::emitValueResultChecks(LInstruction *lir, MDefinition *mir)
masm.pop(temp1);
}
#ifdef DEBUG
void
CodeGenerator::emitObjectOrStringResultChecks(LInstruction *lir, MDefinition *mir)
{
if (lir->numDefs() == 0)
return;
MOZ_ASSERT(lir->numDefs() == 1);
Register output = ToRegister(lir->getDef(0));
emitAssertObjectOrStringResult(output, mir->type(), mir->resultTypeSet());
}
void
CodeGenerator::emitValueResultChecks(LInstruction *lir, MDefinition *mir)
{
if (lir->numDefs() == 0)
return;
MOZ_ASSERT(lir->numDefs() == BOX_PIECES);
if (!lir->getDef(0)->output()->isRegister())
return;
ValueOperand output = ToOutValue(lir);
emitAssertResultV(output, mir->resultTypeSet());
}
void
CodeGenerator::emitDebugResultChecks(LInstruction *ins)
{
@ -9549,6 +9563,22 @@ CodeGenerator::emitAssertRangeD(const Range *r, FloatRegister input, FloatRegist
}
}
void
CodeGenerator::visitAssertResultV(LAssertResultV *ins)
{
const ValueOperand value = ToValue(ins, LAssertResultV::Input);
emitAssertResultV(value, ins->mirRaw()->resultTypeSet());
}
void
CodeGenerator::visitAssertResultT(LAssertResultT *ins)
{
Register input = ToRegister(ins->input());
MDefinition *mir = ins->mirRaw();
emitAssertObjectOrStringResult(input, mir->type(), mir->resultTypeSet());
}
void
CodeGenerator::visitAssertRangeI(LAssertRangeI *ins)
{

Просмотреть файл

@ -356,6 +356,11 @@ class CodeGenerator : public CodeGeneratorSpecific
void visitAssertRangeF(LAssertRangeF *ins);
void visitAssertRangeV(LAssertRangeV *ins);
void visitAssertResultV(LAssertResultV *ins);
void visitAssertResultT(LAssertResultT *ins);
void emitAssertResultV(const ValueOperand output, TemporaryTypeSet *typeset);
void emitAssertObjectOrStringResult(Register input, MIRType type, TemporaryTypeSet *typeset);
void visitInterruptCheck(LInterruptCheck *lir);
void visitAsmJSInterruptCheck(LAsmJSInterruptCheck *lir);
void visitRecompileCheck(LRecompileCheck *ins);
@ -459,9 +464,10 @@ class CodeGenerator : public CodeGeneratorSpecific
void emitAssertRangeD(const Range *r, FloatRegister input, FloatRegister temp);
Vector<CodeOffsetLabel, 0, JitAllocPolicy> ionScriptLabels_;
#ifdef DEBUG
void branchIfInvalidated(Register temp, Label *invalidated);
#ifdef DEBUG
void emitDebugResultChecks(LInstruction *ins);
void emitObjectOrStringResultChecks(LInstruction *lir, MDefinition *mir);
void emitValueResultChecks(LInstruction *lir, MDefinition *mir);

Просмотреть файл

@ -74,6 +74,9 @@ JitOptions::JitOptions()
// RangeAnalysis results.
SET_DEFAULT(checkRangeAnalysis, false);
// Whether to enable extra code to perform dynamic validations.
SET_DEFAULT(runExtraChecks, false);
// Toggle whether eager scalar replacement is globally disabled.
SET_DEFAULT(disableScalarReplacement, false);

Просмотреть файл

@ -46,6 +46,7 @@ struct JitOptions
bool checkOsiPointRegisters;
#endif
bool checkRangeAnalysis;
bool runExtraChecks;
bool disableScalarReplacement;
bool disableEagerSimdUnbox;
bool disableGvn;

Просмотреть файл

@ -6799,6 +6799,28 @@ class LAssertRangeV : public LInstructionHelper<0, BOX_PIECES, 3>
}
};
class LAssertResultT : public LInstructionHelper<0, 1, 0>
{
public:
LIR_HEADER(AssertResultT)
explicit LAssertResultT(const LAllocation &input) {
setOperand(0, input);
}
const LAllocation *input() {
return getOperand(0);
}
};
class LAssertResultV : public LInstructionHelper<0, BOX_PIECES, 0>
{
public:
LIR_HEADER(AssertResultV)
static const size_t Input = 0;
};
class LRecompileCheck : public LInstructionHelper<0, 0, 1>
{
public:

Просмотреть файл

@ -339,6 +339,8 @@
_(AssertRangeD) \
_(AssertRangeF) \
_(AssertRangeV) \
_(AssertResultV) \
_(AssertResultT) \
_(LexicalCheck) \
_(ThrowUninitializedLexical) \
_(NurseryObject) \

Просмотреть файл

@ -24,6 +24,12 @@ using namespace jit;
using mozilla::DebugOnly;
using JS::GenericNaN;
void
LIRGenerator::useBoxAtStart(LInstruction *lir, size_t n, MDefinition *mir, LUse::Policy policy)
{
return useBox(lir, n, mir, policy, true);
}
void
LIRGenerator::visitCloneLiteral(MCloneLiteral *ins)
{
@ -2317,8 +2323,8 @@ LIRGenerator::visitTypeBarrier(MTypeBarrier *ins)
if (ins->alwaysBails()) {
LBail *bail = new(alloc()) LBail();
assignSnapshot(bail, Bailout_Inevitable);
redefine(ins, ins->input());
add(bail, ins);
redefine(ins, ins->input());
return;
}
@ -2328,8 +2334,8 @@ LIRGenerator::visitTypeBarrier(MTypeBarrier *ins)
LTypeBarrierV *barrier = new(alloc()) LTypeBarrierV(tmp);
useBox(barrier, LTypeBarrierV::Input, ins->input());
assignSnapshot(barrier, Bailout_TypeBarrierV);
redefine(ins, ins->input());
add(barrier, ins);
redefine(ins, ins->input());
return;
}
@ -2348,8 +2354,8 @@ LIRGenerator::visitTypeBarrier(MTypeBarrier *ins)
LDefinition tmp = needTemp ? temp() : LDefinition::BogusTemp();
LTypeBarrierO *barrier = new(alloc()) LTypeBarrierO(useRegister(ins->getOperand(0)), tmp);
assignSnapshot(barrier, Bailout_TypeBarrierO);
redefine(ins, ins->getOperand(0));
add(barrier, ins);
redefine(ins, ins->getOperand(0));
return;
}
@ -4072,10 +4078,10 @@ LIRGenerator::visitLexicalCheck(MLexicalCheck *ins)
MDefinition *input = ins->input();
MOZ_ASSERT(input->type() == MIRType_Value);
LLexicalCheck *lir = new(alloc()) LLexicalCheck();
redefine(ins, input);
useBox(lir, LLexicalCheck::Input, input);
add(lir, ins);
assignSafepoint(lir, ins);
redefine(ins, input);
}
void

Просмотреть файл

@ -47,9 +47,7 @@ class LIRGenerator : public LIRGeneratorSpecific
private:
void useBoxAtStart(LInstruction *lir, size_t n, MDefinition *mir,
LUse::Policy policy = LUse::REGISTER) {
return useBox(lir, n, mir, policy, true);
}
LUse::Policy policy = LUse::REGISTER);
void lowerBitOp(JSOp op, MInstruction *ins);
void lowerShiftOp(JSOp op, MShiftInstruction *ins);

Просмотреть файл

@ -1135,7 +1135,6 @@ AutoDetectInvalidation::setReturnOverride()
cx_->runtime()->jitRuntime()->setIonReturnOverride(rval_.get());
}
#ifdef DEBUG
void
AssertValidObjectPtr(JSContext *cx, JSObject *obj)
{
@ -1149,7 +1148,7 @@ AssertValidObjectPtr(JSContext *cx, JSObject *obj)
if (obj->isTenured()) {
MOZ_ASSERT(obj->isAligned());
gc::AllocKind kind = obj->asTenured().getAllocKind();
mozilla::DebugOnly<gc::AllocKind> kind = obj->asTenured().getAllocKind();
MOZ_ASSERT(kind <= js::gc::AllocKind::OBJECT_LAST);
MOZ_ASSERT(obj->asTenured().zone() == cx->zone());
}
@ -1180,7 +1179,7 @@ AssertValidStringPtr(JSContext *cx, JSString *str)
MOZ_ASSERT(str->isAligned());
MOZ_ASSERT(str->length() <= JSString::MAX_LENGTH);
gc::AllocKind kind = str->getAllocKind();
mozilla::DebugOnly<gc::AllocKind> kind = str->getAllocKind();
if (str->isFatInline())
MOZ_ASSERT(kind == gc::AllocKind::FAT_INLINE_STRING);
else if (str->isExternal())
@ -1220,7 +1219,6 @@ AssertValidValue(JSContext *cx, Value *v)
else if (v->isSymbol())
AssertValidSymbolPtr(cx, v->toSymbol());
}
#endif
bool
ObjectIsCallable(JSObject *obj)

Просмотреть файл

@ -748,13 +748,11 @@ JSString *StringReplace(JSContext *cx, HandleString string, HandleString pattern
bool SetDenseElement(JSContext *cx, HandleNativeObject obj, int32_t index, HandleValue value,
bool strict);
#ifdef DEBUG
void AssertValidObjectPtr(JSContext *cx, JSObject *obj);
void AssertValidObjectOrNullPtr(JSContext *cx, JSObject *obj);
void AssertValidStringPtr(JSContext *cx, JSString *str);
void AssertValidSymbolPtr(JSContext *cx, JS::Symbol *sym);
void AssertValidValue(JSContext *cx, Value *v);
#endif
void MarkValueFromIon(JSRuntime *rt, Value *vp);
void MarkStringFromIon(JSRuntime *rt, JSString **stringp);

Просмотреть файл

@ -17,16 +17,6 @@ using namespace js::jit;
using mozilla::FloorLog2;
void
LIRGeneratorARM::useBox(LInstruction *lir, size_t n, MDefinition *mir,
LUse::Policy policy, bool useAtStart)
{
MOZ_ASSERT(mir->type() == MIRType_Value);
ensureDefined(mir);
lir->setOperand(n, LUse(mir->virtualRegister(), policy, useAtStart));
lir->setOperand(n + 1, LUse(VirtualRegisterOfPayload(mir), policy, useAtStart));
}
void
LIRGeneratorARM::useBoxFixed(LInstruction *lir, size_t n, MDefinition *mir, Register reg1,
Register reg2)

Просмотреть файл

@ -22,8 +22,6 @@ class LIRGeneratorARM : public LIRGeneratorShared
protected:
// Adds a box input to an instruction, setting operand |n| to the type and
// |n+1| to the payload.
void useBox(LInstruction *lir, size_t n, MDefinition *mir,
LUse::Policy policy = LUse::REGISTER, bool useAtStart = false);
void useBoxFixed(LInstruction *lir, size_t n, MDefinition *mir, Register reg1, Register reg2);
// x86 has constraints on what registers can be formatted for 1-byte

Просмотреть файл

@ -18,17 +18,6 @@ using namespace js::jit;
using mozilla::FloorLog2;
void
LIRGeneratorMIPS::useBox(LInstruction *lir, size_t n, MDefinition *mir,
LUse::Policy policy, bool useAtStart)
{
MOZ_ASSERT(mir->type() == MIRType_Value);
ensureDefined(mir);
lir->setOperand(n, LUse(mir->virtualRegister(), policy, useAtStart));
lir->setOperand(n + 1, LUse(VirtualRegisterOfPayload(mir), policy, useAtStart));
}
void
LIRGeneratorMIPS::useBoxFixed(LInstruction *lir, size_t n, MDefinition *mir, Register reg1,
Register reg2)

Просмотреть файл

@ -22,8 +22,6 @@ class LIRGeneratorMIPS : public LIRGeneratorShared
protected:
// Adds a box input to an instruction, setting operand |n| to the type and
// |n+1| to the payload.
void useBox(LInstruction *lir, size_t n, MDefinition *mir,
LUse::Policy policy = LUse::REGISTER, bool useAtStart = false);
void useBoxFixed(LInstruction *lir, size_t n, MDefinition *mir, Register reg1, Register reg2);
// x86 has constraints on what registers can be formatted for 1-byte

Просмотреть файл

@ -21,10 +21,6 @@ class LIRGeneratorNone : public LIRGeneratorShared
MOZ_CRASH();
}
void useBox(LInstruction *, size_t, MDefinition *,
LUse::Policy a = LUse::REGISTER, bool b = false) {
MOZ_CRASH();
}
void useBoxFixed(LInstruction *, size_t, MDefinition *, Register, Register) { MOZ_CRASH(); }
LAllocation useByteOpRegister(MDefinition *) { MOZ_CRASH(); }

Просмотреть файл

@ -207,6 +207,32 @@ LIRGeneratorShared::redefine(MDefinition *def, MDefinition *as)
} else {
ensureDefined(as);
def->setVirtualRegister(as->virtualRegister());
#ifdef DEBUG
if (js_JitOptions.runExtraChecks &&
def->resultTypeSet() && as->resultTypeSet() &&
!def->resultTypeSet()->equals(as->resultTypeSet()))
{
switch (def->type()) {
case MIRType_Object:
case MIRType_ObjectOrNull:
case MIRType_String:
case MIRType_Symbol: {
LAssertResultT *check = new(alloc()) LAssertResultT(useRegister(def));
add(check, def->toInstruction());
break;
}
case MIRType_Value: {
LAssertResultV *check = new(alloc()) LAssertResultV();
useBox(check, LAssertRangeV::Input, def);
add(check, def->toInstruction());
break;
}
default:
break;
}
}
#endif
}
}
@ -499,6 +525,19 @@ LIRGeneratorShared::useRegisterForTypedLoad(MDefinition *mir, MIRType type)
return useRegisterAtStart(mir);
}
void
LIRGeneratorShared::useBox(LInstruction *lir, size_t n, MDefinition *mir,
LUse::Policy policy, bool useAtStart)
{
MOZ_ASSERT(mir->type() == MIRType_Value);
ensureDefined(mir);
lir->setOperand(n, LUse(mir->virtualRegister(), policy, useAtStart));
#if defined(JS_NUNBOX32)
lir->setOperand(n + 1, LUse(VirtualRegisterOfPayload(mir), policy, useAtStart));
#endif
}
} // namespace jit
} // namespace js

Просмотреть файл

@ -156,6 +156,10 @@ class LIRGeneratorShared : public MDefinitionVisitor
template <size_t Ops, size_t Temps>
inline void defineReuseInput(LInstructionHelper<1, Ops, Temps> *lir, MDefinition *mir, uint32_t operand);
// Adds a use at operand |n| of a value-typed insturction.
inline void useBox(LInstruction *lir, size_t n, MDefinition *mir,
LUse::Policy policy = LUse::REGISTER, bool useAtStart = false);
// Rather than defining a new virtual register, sets |ins| to have the same
// virtual register as |as|.
inline void redefine(MDefinition *ins, MDefinition *as);

Просмотреть файл

@ -14,16 +14,6 @@
using namespace js;
using namespace js::jit;
void
LIRGeneratorX64::useBox(LInstruction *lir, size_t n, MDefinition *mir,
LUse::Policy policy, bool useAtStart)
{
MOZ_ASSERT(mir->type() == MIRType_Value);
ensureDefined(mir);
lir->setOperand(n, LUse(mir->virtualRegister(), policy, useAtStart));
}
void
LIRGeneratorX64::useBoxFixed(LInstruction *lir, size_t n, MDefinition *mir, Register reg1, Register)
{

Просмотреть файл

@ -24,8 +24,6 @@ class LIRGeneratorX64 : public LIRGeneratorX86Shared
void defineUntypedPhi(MPhi *phi, size_t lirIndex);
// Adds a use at operand |n| of a value-typed insturction.
void useBox(LInstruction *lir, size_t n, MDefinition *mir,
LUse::Policy policy = LUse::REGISTER, bool useAtStart = false);
void useBoxFixed(LInstruction *lir, size_t n, MDefinition *mir, Register reg1, Register);
// x86 has constraints on what registers can be formatted for 1-byte

Просмотреть файл

@ -14,17 +14,6 @@
using namespace js;
using namespace js::jit;
void
LIRGeneratorX86::useBox(LInstruction *lir, size_t n, MDefinition *mir,
LUse::Policy policy, bool useAtStart)
{
MOZ_ASSERT(mir->type() == MIRType_Value);
ensureDefined(mir);
lir->setOperand(n, LUse(mir->virtualRegister(), policy, useAtStart));
lir->setOperand(n + 1, LUse(VirtualRegisterOfPayload(mir), policy, useAtStart));
}
void
LIRGeneratorX86::useBoxFixed(LInstruction *lir, size_t n, MDefinition *mir, Register reg1,
Register reg2)

Просмотреть файл

@ -22,8 +22,6 @@ class LIRGeneratorX86 : public LIRGeneratorX86Shared
protected:
// Adds a box input to an instruction, setting operand |n| to the type and
// |n+1| to the payload.
void useBox(LInstruction *lir, size_t n, MDefinition *mir,
LUse::Policy policy = LUse::REGISTER, bool useAtStart = false);
void useBoxFixed(LInstruction *lir, size_t n, MDefinition *mir, Register reg1, Register reg2);
// It's a trap! On x86, the 1-byte store can only use one of

Просмотреть файл

@ -5836,6 +5836,9 @@ SetRuntimeOptions(JSRuntime *rt, const OptionParser &op)
if (op.getBoolOption("ion-check-range-analysis"))
jit::js_JitOptions.checkRangeAnalysis = true;
if (op.getBoolOption("ion-extra-checks"))
jit::js_JitOptions.runExtraChecks = true;
if (const char *str = op.getStringOption("ion-inlining")) {
if (strcmp(str, "on") == 0)
jit::js_JitOptions.disableInlining = false;
@ -6147,6 +6150,8 @@ main(int argc, char **argv, char **envp)
"Loop unrolling (default: off, on to enable)")
|| !op.addBoolOption('\0', "ion-check-range-analysis",
"Range analysis checking")
|| !op.addBoolOption('\0', "ion-extra-checks",
"Perform extra dynamic validation checks")
|| !op.addStringOption('\0', "ion-inlining", "on/off",
"Inline methods where possible (default: on, off to disable)")
|| !op.addStringOption('\0', "ion-osr", "on/off",

Просмотреть файл

@ -15,7 +15,7 @@ TBPL_FLAGS = [
[], # no flags, normal baseline and ion
['--ion-eager', '--ion-offthread-compile=off'], # implies --baseline-eager
['--ion-eager', '--ion-offthread-compile=off',
'--ion-check-range-analysis', '--no-sse3', '--no-threads'],
'--ion-check-range-analysis', '--ion-extra-checks', '--no-sse3', '--no-threads'],
['--baseline-eager'],
['--baseline-eager', '--no-fpu'],
['--no-baseline', '--no-ion'],