summaryrefslogtreecommitdiff
path: root/js/src/jit/IonBuilder.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/jit/IonBuilder.cpp')
-rw-r--r--js/src/jit/IonBuilder.cpp238
1 files changed, 214 insertions, 24 deletions
diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp
index ae3776c888..7eb69ec00f 100644
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -636,7 +636,7 @@ IonBuilder::analyzeNewLoopTypes(MBasicBlock* entry, jsbytecode* start, jsbytecod
MPhi* phi = entry->getSlot(slot)->toPhi();
- if (*last == JSOP_POS)
+ if (*last == JSOP_POS || *last == JSOP_TONUMERIC)
last = earlier;
if (CodeSpec[*last].format & JOF_TYPESET) {
@@ -719,6 +719,8 @@ IonBuilder::analyzeNewLoopTypes(MBasicBlock* entry, jsbytecode* start, jsbytecod
case JSOP_DIV:
case JSOP_MOD:
case JSOP_NEG:
+ case JSOP_INC:
+ case JSOP_DEC:
type = inspector->expectedResultType(last);
break;
case JSOP_BIGINT:
@@ -1584,6 +1586,7 @@ IonBuilder::traverseBytecode()
break;
case JSOP_POS:
+ case JSOP_TONUMERIC:
case JSOP_TOID:
case JSOP_TOSTRING:
// These ops may leave their input on the stack without setting
@@ -1737,9 +1740,16 @@ IonBuilder::inspectOpcode(JSOp op)
case JSOP_POS:
return jsop_pos();
+ case JSOP_TONUMERIC:
+ return jsop_tonumeric();
+
case JSOP_NEG:
return jsop_neg();
+ case JSOP_INC:
+ case JSOP_DEC:
+ return jsop_inc_or_dec(op);
+
case JSOP_TOSTRING:
return jsop_tostring();
@@ -4852,7 +4862,7 @@ IonBuilder::jsop_bitop(JSOp op)
}
MDefinition::Opcode
-JSOpToMDefinition(JSOp op)
+BinaryJSOpToMDefinition(JSOp op)
{
switch (op) {
case JSOP_ADD:
@@ -4953,6 +4963,40 @@ IonBuilder::powTrySpecialized(bool* emitted, MDefinition* base, MDefinition* pow
return true;
}
+MIRType
+IonBuilder::binaryArithNumberSpecialization(MDefinition* left, MDefinition* right)
+{
+ // Try to specialize as int32.
+ if (left->type() == MIRType::Int32 && right->type() == MIRType::Int32 &&
+ !inspector->hasSeenDoubleResult(pc)) {
+ return MIRType::Int32;
+ }
+ return MIRType::Double;
+}
+
+MBinaryArithInstruction*
+IonBuilder::binaryArithEmitSpecialized(MDefinition::Opcode op, MIRType specialization,
+ MDefinition* left, MDefinition* right)
+{
+ MBinaryArithInstruction* ins = MBinaryArithInstruction::New(alloc(), op, left, right);
+ ins->setSpecialization(specialization);
+
+ if (op == MDefinition::Op_Add || op == MDefinition::Op_Mul) {
+ ins->setCommutative();
+ }
+
+ current->add(ins);
+ current->push(ins);
+
+ MOZ_ASSERT(!ins->isEffectful());
+
+ if(!maybeInsertResume()) {
+ return nullptr;
+ }
+
+ return ins;
+}
+
static inline bool
SimpleArithOperand(MDefinition* op)
{
@@ -4986,19 +5030,20 @@ IonBuilder::binaryArithTrySpecialized(bool* emitted, JSOp op, MDefinition* left,
return true;
}
- MDefinition::Opcode defOp = JSOpToMDefinition(op);
- MBinaryArithInstruction* ins = MBinaryArithInstruction::New(alloc(), defOp, left, right);
- ins->setNumberSpecialization(alloc(), inspector, pc);
-
- if (op == JSOP_ADD || op == JSOP_MUL)
- ins->setCommutative();
-
- current->add(ins);
- current->push(ins);
+ MDefinition::Opcode defOp = BinaryJSOpToMDefinition(op);
+ MIRType specialization = binaryArithNumberSpecialization(left, right);
+ MBinaryArithInstruction* ins = binaryArithEmitSpecialized(defOp, specialization, left, right);
- MOZ_ASSERT(!ins->isEffectful());
- if (!maybeInsertResume())
+ if(!ins) {
return false;
+ }
+
+ // Relax int32 to double if, despite the fact that we have int32 operands and
+ // we've never seen a double result, we know the result may overflow or be a
+ // double.
+ if (specialization == MIRType::Int32 && ins->constantDoubleResult(alloc())) {
+ ins->setSpecialization(MIRType::Double);
+ }
trackOptimizationSuccess();
*emitted = true;
@@ -5022,16 +5067,10 @@ IonBuilder::binaryArithTrySpecializedOnBaselineInspector(bool* emitted, JSOp op,
return true;
}
- MDefinition::Opcode def_op = JSOpToMDefinition(op);
- MBinaryArithInstruction* ins = MBinaryArithInstruction::New(alloc(), def_op, left, right);
- ins->setSpecialization(specialization);
-
- current->add(ins);
- current->push(ins);
-
- MOZ_ASSERT(!ins->isEffectful());
- if (!maybeInsertResume())
+ MDefinition::Opcode defOp = BinaryJSOpToMDefinition(op);
+ if(!binaryArithEmitSpecialized(defOp, specialization, left, right)) {
return false;
+ }
trackOptimizationSuccess();
*emitted = true;
@@ -5124,8 +5163,8 @@ IonBuilder::jsop_binary_arith(JSOp op, MDefinition* left, MDefinition* right)
trackOptimizationAttempt(TrackedStrategy::BinaryArith_Call);
trackOptimizationSuccess();
- MDefinition::Opcode def_op = JSOpToMDefinition(op);
- MBinaryArithInstruction* ins = MBinaryArithInstruction::New(alloc(), def_op, left, right);
+ MDefinition::Opcode defOp = BinaryJSOpToMDefinition(op);
+ MBinaryArithInstruction* ins = MBinaryArithInstruction::New(alloc(), defOp, left, right);
// Decrease type from 'any type' to 'empty type' when one of the operands
// is 'empty typed'.
@@ -5202,6 +5241,157 @@ IonBuilder::jsop_neg()
return jsop_binary_arith(JSOP_MUL, negator, right);
}
+MDefinition*
+IonBuilder::unaryArithConvertToBinary(JSOp op, MDefinition::Opcode* defOp)
+{
+ switch (op) {
+ case JSOP_INC: {
+ *defOp = MDefinition::Op_Add;
+ MConstant* right = MConstant::New(alloc(), Int32Value(1));
+ current->add(right);
+ return right;
+ }
+ case JSOP_DEC: {
+ *defOp = MDefinition::Op_Sub;
+ MConstant* right = MConstant::New(alloc(), Int32Value(1));
+ current->add(right);
+ return right;
+ }
+ default:
+ MOZ_CRASH("unexpected unary opcode");
+ }
+}
+
+bool
+IonBuilder::unaryArithTrySpecialized(bool* emitted, JSOp op, MDefinition* value)
+{
+ MOZ_ASSERT(*emitted == false);
+
+ // Try to convert Inc(x) or Dec(x) to Add(x,1) or Sub(x,1) if the operand is a
+ // number.
+
+ trackOptimizationAttempt(TrackedStrategy::UnaryArith_SpecializedTypes);
+
+ if (!IsNumberType(value->type())) {
+ trackOptimizationOutcome(TrackedOutcome::OperandNotNumber);
+ return true;
+ }
+
+ MDefinition::Opcode defOp;
+ MDefinition* rhs = unaryArithConvertToBinary(op, &defOp);
+ MIRType specialization = binaryArithNumberSpecialization(value, rhs);
+ if (!binaryArithEmitSpecialized(defOp, specialization, value, rhs)) {
+ return false;
+ }
+
+ trackOptimizationSuccess();
+ *emitted = true;
+ return true;
+}
+
+bool
+IonBuilder::unaryArithTrySpecializedOnBaselineInspector(bool* emitted, JSOp op, MDefinition* value)
+{
+ MOZ_ASSERT(*emitted == false);
+
+ // Try to emit a specialized binary instruction speculating the
+ // type using the baseline caches.
+
+ trackOptimizationAttempt(TrackedStrategy::UnaryArith_SpecializedOnBaselineTypes);
+
+ MIRType specialization = inspector->expectedBinaryArithSpecialization(pc);
+ if (specialization == MIRType::None) {
+ trackOptimizationOutcome(TrackedOutcome::SpeculationOnInputTypesFailed);
+ return true;
+ }
+
+ MDefinition::Opcode defOp;
+ MDefinition* rhs = unaryArithConvertToBinary(op, &defOp);
+ if (!binaryArithEmitSpecialized(defOp, specialization, value, rhs)) {
+ return false;
+ }
+
+ trackOptimizationSuccess();
+ *emitted = true;
+ return true;
+}
+
+bool
+IonBuilder::jsop_tonumeric()
+{
+ MDefinition* peeked = current->peek(-1);
+
+ if (IsNumericType(peeked->type())) {
+ // Elide the ToNumeric as we already unboxed the value.
+ peeked->setImplicitlyUsedUnchecked();
+ return true;
+ }
+
+ LifoAlloc* lifoAlloc = alloc().lifoAlloc();
+ TemporaryTypeSet* types = lifoAlloc->new_<TemporaryTypeSet>();
+ if (!types) {
+ return false;
+ }
+
+ types->addType(TypeSet::Int32Type(), lifoAlloc);
+ types->addType(TypeSet::DoubleType(), lifoAlloc);
+ types->addType(TypeSet::BigIntType(), lifoAlloc);
+
+ if (peeked->type() == MIRType::Value && peeked->resultTypeSet() &&
+ peeked->resultTypeSet()->isSubset(types)) {
+ // Elide the ToNumeric because the arg is already a boxed numeric.
+ peeked->setImplicitlyUsedUnchecked();
+ return true;
+ }
+
+ // Otherwise, pop the value and add an MToNumeric.
+ MDefinition* popped = current->pop();
+ MToNumeric* ins = MToNumeric::New(alloc(), popped, types);
+ current->add(ins);
+ current->push(ins);
+
+ // toValue() is effectful, so add a resume point.
+ return resumeAfter(ins);
+}
+
+bool
+IonBuilder::jsop_inc_or_dec(JSOp op)
+{
+ bool emitted = false;
+ MDefinition* value = current->pop();
+
+ startTrackingOptimizations();
+
+ trackTypeInfo(TrackedTypeSite::Operand, value->type(), value->resultTypeSet());
+
+ if (!unaryArithTrySpecialized(&emitted, op, value)) {
+ return false;
+ }
+ if (emitted) {
+ return true;
+ }
+
+ if (!unaryArithTrySpecializedOnBaselineInspector(&emitted, op, value)) {
+ return false;
+ }
+ if (emitted) {
+ return true;
+ }
+
+ trackOptimizationAttempt(TrackedStrategy::UnaryArith_InlineCache);
+ trackOptimizationSuccess();
+
+ MInstruction* stub = MUnarySharedStub::New(alloc(), value);
+ current->add(stub);
+ current->push(stub);
+
+ // Decrease type from 'any type' to 'empty type' when one of the operands
+ // is 'empty typed'.
+ maybeMarkEmpty(stub);
+
+ return resumeAfter(stub);
+}
+
bool
IonBuilder::jsop_tostring()
{