Index: runtime/vm/intermediate_language_mips.cc |
=================================================================== |
--- runtime/vm/intermediate_language_mips.cc (revision 42554) |
+++ runtime/vm/intermediate_language_mips.cc (working copy) |
@@ -115,27 +115,26 @@ |
static Condition NegateCondition(Condition condition) { |
- switch (condition) { |
- case EQ: return NE; |
- case NE: return EQ; |
- case LT: return GE; |
- case LE: return GT; |
- case GT: return LE; |
- case GE: return LT; |
+ switch (condition.rel_op()) { |
+ case AL: condition.set_rel_op(NV); break; |
+ case NV: condition.set_rel_op(AL); break; |
+ case EQ: condition.set_rel_op(NE); break; |
+ case NE: condition.set_rel_op(EQ); break; |
+ case LT: condition.set_rel_op(GE); break; |
+ case LE: condition.set_rel_op(GT); break; |
+ case GT: condition.set_rel_op(LE); break; |
+ case GE: condition.set_rel_op(LT); break; |
+ case ULT: condition.set_rel_op(UGE); break; |
+ case ULE: condition.set_rel_op(UGT); break; |
+ case UGT: condition.set_rel_op(ULE); break; |
+ case UGE: condition.set_rel_op(ULT); break; |
default: |
UNREACHABLE(); |
- return EQ; |
} |
+ return condition; |
} |
-// Detect pattern when one value is zero and another is a power of 2. |
-static bool IsPowerOfTwoKind(intptr_t v1, intptr_t v2) { |
- return (Utils::IsPowerOfTwo(v1) && (v2 == 0)) || |
- (Utils::IsPowerOfTwo(v2) && (v1 == 0)); |
-} |
- |
- |
LocationSummary* IfThenElseInstr::MakeLocationSummary(Isolate* isolate, |
bool opt) const { |
comparison()->InitializeLocationSummary(isolate, opt); |
@@ -146,76 +145,109 @@ |
void IfThenElseInstr::EmitNativeCode(FlowGraphCompiler* compiler) { |
const Register result = locs()->out(0).reg(); |
- Location left = locs()->in(0); |
- Location right = locs()->in(1); |
- ASSERT(!left.IsConstant() || !right.IsConstant()); |
+ intptr_t true_value = if_true_; |
+ intptr_t false_value = if_false_; |
+ bool swapped = false; |
+ if (true_value == 0) { |
+ // Swap values so that false_value is zero. |
+ intptr_t temp = true_value; |
+ true_value = false_value; |
+ false_value = temp; |
+ swapped = true; |
+ } |
- // Clear out register. |
- __ mov(result, ZR); |
+ // Initialize result with the true value. |
+ __ LoadImmediate(result, Smi::RawValue(true_value)); |
// Emit comparison code. This must not overwrite the result register. |
- BranchLabels labels = { NULL, NULL, NULL }; |
+ BranchLabels labels = { NULL, NULL, NULL }; // Emit branch-free code. |
Condition true_condition = comparison()->EmitComparisonCode(compiler, labels); |
- |
- const bool is_power_of_two_kind = IsPowerOfTwoKind(if_true_, if_false_); |
- |
- intptr_t true_value = if_true_; |
- intptr_t false_value = if_false_; |
- |
- if (is_power_of_two_kind) { |
- if (true_value == 0) { |
- // We need to have zero in result on true_condition. |
- true_condition = NegateCondition(true_condition); |
- } |
- } else { |
- if (true_value == 0) { |
- // Swap values so that false_value is zero. |
- intptr_t temp = true_value; |
- true_value = false_value; |
- false_value = temp; |
- } else { |
- true_condition = NegateCondition(true_condition); |
- } |
+ if (swapped) { |
+ true_condition = NegateCondition(true_condition); |
} |
- switch (true_condition) { |
+ // Evaluate condition and provide result in CMPRES1. |
+ Register left = true_condition.left(); |
+ Register right = true_condition.right(); |
+ bool zero_is_false = true; // Zero in CMPRES1 indicates a false condition. |
+ switch (true_condition.rel_op()) { |
+ case AL: return; // Result holds true_value. |
+ case NV: __ LoadImmediate(result, false_value); return; |
case EQ: |
- __ xor_(result, CMPRES1, CMPRES2); |
- __ xori(result, result, Immediate(1)); |
+ zero_is_false = false; |
+ // fall through. |
+ case NE: { |
+ if (left == IMM) { |
+ __ XorImmediate(CMPRES1, right, true_condition.imm()); |
+ } else if (right == IMM) { |
+ __ XorImmediate(CMPRES1, left, true_condition.imm()); |
+ } else { |
+ __ xor_(CMPRES1, left, right); |
+ } |
break; |
- case NE: |
- __ xor_(result, CMPRES1, CMPRES2); |
+ } |
+ case GE: |
+ zero_is_false = false; |
+ // fall through. |
+ case LT: { |
+ if (left == IMM) { |
+ __ slti(CMPRES1, right, Immediate(true_condition.imm() + 1)); |
+ zero_is_false = !zero_is_false; |
+ } else if (right == IMM) { |
+ __ slti(CMPRES1, left, Immediate(true_condition.imm())); |
+ } else { |
+ __ slt(CMPRES1, left, right); |
+ } |
break; |
- case GT: |
- __ mov(result, CMPRES2); |
+ } |
+ case LE: |
+ zero_is_false = false; |
+ // fall through. |
+ case GT: { |
+ if (left == IMM) { |
+ __ slti(CMPRES1, right, Immediate(true_condition.imm())); |
+ } else if (right == IMM) { |
+ __ slti(CMPRES1, left, Immediate(true_condition.imm() + 1)); |
+ zero_is_false = !zero_is_false; |
+ } else { |
+ __ slt(CMPRES1, right, left); |
+ } |
break; |
- case GE: |
- __ xori(result, CMPRES1, Immediate(1)); |
+ } |
+ case UGE: |
+ zero_is_false = false; |
+ // fall through. |
+ case ULT: { |
+ ASSERT((left != IMM) && (right != IMM)); // No unsigned constants used. |
+ __ sltu(CMPRES1, left, right); |
break; |
- case LT: |
- __ mov(result, CMPRES1); |
+ } |
+ case ULE: |
+ zero_is_false = false; |
+ // fall through. |
+ case UGT: { |
+ ASSERT((left != IMM) && (right != IMM)); // No unsigned constants used. |
+ __ sltu(CMPRES1, right, left); |
break; |
- case LE: |
- __ xori(result, CMPRES2, Immediate(1)); |
- break; |
+ } |
default: |
UNREACHABLE(); |
- break; |
} |
- if (is_power_of_two_kind) { |
- const intptr_t shift = |
- Utils::ShiftForPowerOfTwo(Utils::Maximum(true_value, false_value)); |
- __ sll(result, result, shift + kSmiTagSize); |
+ // CMPRES1 is the evaluated condition, zero or non-zero, as specified by the |
+ // flag zero_is_false. |
+ Register false_value_reg; |
+ if (false_value == 0) { |
+ false_value_reg = ZR; |
} else { |
- __ AddImmediate(result, result, -1); |
- const int32_t val = |
- Smi::RawValue(true_value) - Smi::RawValue(false_value); |
- __ AndImmediate(result, result, val); |
- if (false_value != 0) { |
- __ AddImmediate(result, result, Smi::RawValue(false_value)); |
- } |
+ __ LoadImmediate(CMPRES2, Smi::RawValue(false_value)); |
+ false_value_reg = CMPRES2; |
} |
+ if (zero_is_false) { |
+ __ movz(result, false_value_reg, CMPRES1); |
+ } else { |
+ __ movn(result, false_value_reg, CMPRES1); |
+ } |
} |
@@ -492,7 +524,7 @@ |
} |
-static Condition TokenKindToSmiCondition(Token::Kind kind) { |
+static RelationOperator TokenKindToIntRelOp(Token::Kind kind) { |
switch (kind) { |
case Token::kEQ: return EQ; |
case Token::kNE: return NE; |
@@ -502,44 +534,27 @@ |
case Token::kGTE: return GE; |
default: |
UNREACHABLE(); |
- return VS; |
+ return NV; |
} |
} |
-// Branches on condition c assuming comparison results in CMPRES1 and CMPRES2. |
-static void EmitBranchAfterCompare( |
- FlowGraphCompiler* compiler, Condition condition, Label* is_true) { |
- switch (condition) { |
- case EQ: __ beq(CMPRES1, CMPRES2, is_true); break; |
- case NE: __ bne(CMPRES1, CMPRES2, is_true); break; |
- case GT: __ bne(CMPRES2, ZR, is_true); break; |
- case GE: __ beq(CMPRES1, ZR, is_true); break; |
- case LT: __ bne(CMPRES1, ZR, is_true); break; |
- case LE: __ beq(CMPRES2, ZR, is_true); break; |
+static RelationOperator TokenKindToUintRelOp(Token::Kind kind) { |
+ switch (kind) { |
+ case Token::kEQ: return EQ; |
+ case Token::kNE: return NE; |
+ case Token::kLT: return ULT; |
+ case Token::kGT: return UGT; |
+ case Token::kLTE: return ULE; |
+ case Token::kGTE: return UGE; |
default: |
UNREACHABLE(); |
- break; |
+ return NV; |
} |
} |
-static Condition FlipCondition(Condition condition) { |
- switch (condition) { |
- case EQ: return EQ; |
- case NE: return NE; |
- case LT: return GT; |
- case LE: return GE; |
- case GT: return LT; |
- case GE: return LE; |
- default: |
- UNREACHABLE(); |
- return EQ; |
- } |
-} |
- |
- |
-// The comparison result is in CMPRES1/CMPRES2. |
+// The comparison code to emit is specified by true_condition. |
static void EmitBranchOnCondition(FlowGraphCompiler* compiler, |
Condition true_condition, |
BranchLabels labels) { |
@@ -546,11 +561,11 @@ |
__ TraceSimMsg("ControlInstruction::EmitBranchOnCondition"); |
if (labels.fall_through == labels.false_label) { |
// If the next block is the false successor, fall through to it. |
- EmitBranchAfterCompare(compiler, true_condition, labels.true_label); |
+ __ BranchOnCondition(true_condition, labels.true_label); |
} else { |
// If the next block is not the false successor, branch to it. |
Condition false_condition = NegateCondition(true_condition); |
- EmitBranchAfterCompare(compiler, false_condition, labels.false_label); |
+ __ BranchOnCondition(false_condition, labels.false_label); |
// Fall through or jump to the true successor. |
if (labels.fall_through != labels.true_label) { |
__ b(labels.true_label); |
@@ -564,43 +579,25 @@ |
Token::Kind kind) { |
__ TraceSimMsg("EmitSmiComparisonOp"); |
__ Comment("EmitSmiComparisonOp"); |
- Location left = locs.in(0); |
- Location right = locs.in(1); |
+ const Location left = locs.in(0); |
+ const Location right = locs.in(1); |
ASSERT(!left.IsConstant() || !right.IsConstant()); |
+ ASSERT(left.IsRegister() || left.IsConstant()); |
+ ASSERT(right.IsRegister() || right.IsConstant()); |
- Condition true_condition = TokenKindToSmiCondition(kind); |
- |
- if (left.IsConstant()) { |
- __ CompareObject(CMPRES1, CMPRES2, right.reg(), left.constant()); |
- true_condition = FlipCondition(true_condition); |
- } else if (right.IsConstant()) { |
- __ CompareObject(CMPRES1, CMPRES2, left.reg(), right.constant()); |
- } else { |
- __ slt(CMPRES1, left.reg(), right.reg()); |
- __ slt(CMPRES2, right.reg(), left.reg()); |
- } |
- return true_condition; |
+ int16_t imm = 0; |
+ const Register left_reg = left.IsRegister() ? |
+ left.reg() : __ LoadConditionOperand(CMPRES1, left.constant(), &imm); |
+ const Register right_reg = right.IsRegister() ? |
+ right.reg() : __ LoadConditionOperand(CMPRES2, right.constant(), &imm); |
+ return Condition(left_reg, right_reg, TokenKindToIntRelOp(kind), imm); |
} |
-static Condition TokenKindToMintCondition(Token::Kind kind) { |
- switch (kind) { |
- case Token::kEQ: return EQ; |
- case Token::kNE: return NE; |
- case Token::kLT: return LT; |
- case Token::kGT: return GT; |
- case Token::kLTE: return LE; |
- case Token::kGTE: return GE; |
- default: |
- UNREACHABLE(); |
- return VS; |
- } |
-} |
- |
- |
static Condition EmitUnboxedMintEqualityOp(FlowGraphCompiler* compiler, |
const LocationSummary& locs, |
- Token::Kind kind) { |
+ Token::Kind kind, |
+ BranchLabels labels) { |
__ TraceSimMsg("EmitUnboxedMintEqualityOp"); |
__ Comment("EmitUnboxedMintEqualityOp"); |
ASSERT(Token::IsEqualityOperator(kind)); |
@@ -611,17 +608,28 @@ |
Register right_lo = right_pair->At(0).reg(); |
Register right_hi = right_pair->At(1).reg(); |
- __ xor_(CMPRES1, left_lo, right_lo); |
- __ xor_(CMPRES2, left_hi, right_hi); |
- __ or_(CMPRES1, CMPRES1, CMPRES2); |
- __ mov(CMPRES2, ZR); |
- return TokenKindToMintCondition(kind); |
+ if (labels.false_label == NULL) { |
+ // Generate branch-free code. |
+ __ xor_(CMPRES1, left_lo, right_lo); |
+ __ xor_(AT, left_hi, right_hi); |
+ __ or_(CMPRES1, CMPRES1, AT); |
+ return Condition(CMPRES1, ZR, TokenKindToUintRelOp(kind)); |
+ } else { |
+ if (kind == Token::kEQ) { |
+ __ bne(left_hi, right_hi, labels.false_label); |
+ } else { |
+ ASSERT(kind == Token::kNE); |
+ __ bne(left_hi, right_hi, labels.true_label); |
+ } |
+ return Condition(left_lo, right_lo, TokenKindToUintRelOp(kind)); |
+ } |
} |
static Condition EmitUnboxedMintComparisonOp(FlowGraphCompiler* compiler, |
const LocationSummary& locs, |
- Token::Kind kind) { |
+ Token::Kind kind, |
+ BranchLabels labels) { |
__ TraceSimMsg("EmitUnboxedMintComparisonOp"); |
__ Comment("EmitUnboxedMintComparisonOp"); |
PairLocation* left_pair = locs.in(0).AsPairLocation(); |
@@ -631,32 +639,42 @@ |
Register right_lo = right_pair->At(0).reg(); |
Register right_hi = right_pair->At(1).reg(); |
- Label done; |
- // Compare upper halves first. |
- __ slt(CMPRES1, left_hi, right_hi); |
- __ slt(CMPRES2, right_hi, left_hi); |
- // If higher words aren't equal, skip comparing lower words. |
- __ bne(CMPRES1, CMPRES2, &done); |
+ if (labels.false_label == NULL) { |
+ // Generate branch-free code (except for skipping the lower words compare). |
+ // Result in CMPRES1, CMPRES2, so that CMPRES1 op CMPRES2 === left op right. |
+ Label done; |
+ // Compare upper halves first. |
+ __ slt(CMPRES1, right_hi, left_hi); |
+ __ slt(CMPRES2, left_hi, right_hi); |
+ // If higher words aren't equal, skip comparing lower words. |
+ __ bne(CMPRES1, CMPRES2, &done); |
- __ sltu(CMPRES1, left_lo, right_lo); |
- __ sltu(CMPRES2, right_lo, left_lo); |
- __ Bind(&done); |
- |
- return TokenKindToMintCondition(kind); |
-} |
- |
- |
-static Condition TokenKindToDoubleCondition(Token::Kind kind) { |
- switch (kind) { |
- case Token::kEQ: return EQ; |
- case Token::kNE: return NE; |
- case Token::kLT: return LT; |
- case Token::kGT: return GT; |
- case Token::kLTE: return LE; |
- case Token::kGTE: return GE; |
- default: |
- UNREACHABLE(); |
- return VS; |
+ __ sltu(CMPRES1, right_lo, left_lo); |
+ __ sltu(CMPRES2, left_lo, right_lo); |
+ __ Bind(&done); |
+ return Condition(CMPRES1, CMPRES2, TokenKindToUintRelOp(kind)); |
+ } else { |
+ switch (kind) { |
+ case Token::kLT: |
+ case Token::kLTE: { |
+ __ slt(AT, left_hi, right_hi); |
+ __ bne(AT, ZR, labels.true_label); |
+ __ delay_slot()->slt(AT, right_hi, left_hi); |
+ __ bne(AT, ZR, labels.false_label); |
+ break; |
+ } |
+ case Token::kGT: |
+ case Token::kGTE: { |
+ __ slt(AT, left_hi, right_hi); |
+ __ bne(AT, ZR, labels.false_label); |
+ __ delay_slot()->slt(AT, right_hi, left_hi); |
+ __ bne(AT, ZR, labels.true_label); |
+ break; |
+ } |
+ default: |
+ UNREACHABLE(); |
+ } |
+ return Condition(left_lo, right_lo, TokenKindToUintRelOp(kind)); |
} |
} |
@@ -670,19 +688,18 @@ |
__ Comment("DoubleComparisonOp(left=%d, right=%d)", left, right); |
- Condition true_condition = TokenKindToDoubleCondition(kind); |
__ cund(left, right); |
- Label* nan_label = (true_condition == NE) |
+ Label* nan_label = (kind == Token::kNE) |
? labels.true_label : labels.false_label; |
__ bc1t(nan_label); |
- switch (true_condition) { |
- case EQ: __ ceqd(left, right); break; |
- case NE: __ ceqd(left, right); break; |
- case LT: __ coltd(left, right); break; |
- case LE: __ coled(left, right); break; |
- case GT: __ coltd(right, left); break; |
- case GE: __ coled(right, left); break; |
+ switch (kind) { |
+ case Token::kEQ: __ ceqd(left, right); break; |
+ case Token::kNE: __ ceqd(left, right); break; |
+ case Token::kLT: __ coltd(left, right); break; |
+ case Token::kLTE: __ coled(left, right); break; |
+ case Token::kGT: __ coltd(right, left); break; |
+ case Token::kGTE: __ coled(right, left); break; |
default: { |
// We should only be passing the above conditions to this function. |
UNREACHABLE(); |
@@ -690,17 +707,34 @@ |
} |
} |
- // Ordering is expected to be described by CMPRES1, CMPRES2. |
- __ LoadImmediate(TMP, 1); |
- if (true_condition == NE) { |
- __ movf(CMPRES1, ZR); |
- __ movt(CMPRES1, TMP); |
+ if (labels.false_label == NULL) { |
+ // Generate branch-free code and return result in condition. |
+ __ LoadImmediate(CMPRES1, 1); |
+ if (kind == Token::kNE) { |
+ __ movf(CMPRES1, ZR); |
+ } else { |
+ __ movt(CMPRES1, ZR); |
+ } |
+ return Condition(CMPRES1, ZR, EQ); |
} else { |
- __ movf(CMPRES1, TMP); |
- __ movt(CMPRES1, ZR); |
+ if (labels.fall_through == labels.false_label) { |
+ if (kind == Token::kNE) { |
+ __ bc1f(labels.true_label); |
+ } else { |
+ __ bc1t(labels.true_label); |
+ } |
+ // Since we already branched on true, return the never true condition. |
+ return Condition(CMPRES1, CMPRES2, NV); |
+ } else { |
+ if (kind == Token::kNE) { |
+ __ bc1t(labels.false_label); |
+ } else { |
+ __ bc1f(labels.false_label); |
+ } |
+ // Since we already branched on false, return the always true condition. |
+ return Condition(CMPRES1, CMPRES2, AL); |
+ } |
} |
- __ mov(CMPRES2, ZR); |
- return EQ; |
} |
@@ -709,7 +743,7 @@ |
if (operation_cid() == kSmiCid) { |
return EmitSmiComparisonOp(compiler, *locs(), kind()); |
} else if (operation_cid() == kMintCid) { |
- return EmitUnboxedMintEqualityOp(compiler, *locs(), kind()); |
+ return EmitUnboxedMintEqualityOp(compiler, *locs(), kind(), labels); |
} else { |
ASSERT(operation_cid() == kDoubleCid); |
return EmitDoubleComparisonOp(compiler, *locs(), kind(), labels); |
@@ -775,9 +809,7 @@ |
} else { |
__ and_(CMPRES1, left, right.reg()); |
} |
- __ mov(CMPRES2, ZR); |
- Condition true_condition = (kind() == Token::kNE) ? NE : EQ; |
- return true_condition; |
+ return Condition(CMPRES1, ZR, (kind() == Token::kNE) ? NE : EQ); |
} |
@@ -841,9 +873,8 @@ |
} else { |
__ b(deopt); |
} |
- // Dummy result as the last instruction is a jump, any conditional |
- // branch using the result will therefore be skipped. |
- return EQ; |
+ // Dummy result as the last instruction is a jump or fall through. |
+ return Condition(CMPRES1, ZR, AL); |
} |
@@ -910,7 +941,7 @@ |
if (operation_cid() == kSmiCid) { |
return EmitSmiComparisonOp(compiler, *locs(), kind()); |
} else if (operation_cid() == kMintCid) { |
- return EmitUnboxedMintComparisonOp(compiler, *locs(), kind()); |
+ return EmitUnboxedMintComparisonOp(compiler, *locs(), kind(), labels); |
} else { |
ASSERT(operation_cid() == kDoubleCid); |
return EmitDoubleComparisonOp(compiler, *locs(), kind(), labels); |
@@ -1045,12 +1076,11 @@ |
ASSERT(cid_ == kOneByteStringCid); |
Register str = locs()->in(0).reg(); |
Register result = locs()->out(0).reg(); |
- Label done, is_one; |
+ ASSERT(str != result); |
+ Label done; |
__ lw(result, FieldAddress(str, String::length_offset())); |
- __ BranchEqual(result, Immediate(Smi::RawValue(1)), &is_one); |
- __ LoadImmediate(result, Smi::RawValue(-1)); |
- __ b(&done); |
- __ Bind(&is_one); |
+ __ BranchNotEqual(result, Immediate(Smi::RawValue(1)), &done); |
+ __ delay_slot()->addiu(result, ZR, Immediate(Smi::RawValue(-1))); |
__ lbu(result, FieldAddress(str, OneByteString::data_offset())); |
__ SmiTag(result); |
__ Bind(&done); |
@@ -2975,32 +3005,17 @@ |
} |
case Token::kBIT_AND: { |
// No overflow check. |
- if (Utils::IsUint(kImmBits, imm)) { |
- __ andi(result, left, Immediate(imm)); |
- } else { |
- __ LoadImmediate(TMP, imm); |
- __ and_(result, left, TMP); |
- } |
+ __ AndImmediate(result, left, imm); |
break; |
} |
case Token::kBIT_OR: { |
// No overflow check. |
- if (Utils::IsUint(kImmBits, imm)) { |
- __ ori(result, left, Immediate(imm)); |
- } else { |
- __ LoadImmediate(TMP, imm); |
- __ or_(result, left, TMP); |
- } |
+ __ OrImmediate(result, left, imm); |
break; |
} |
case Token::kBIT_XOR: { |
// No overflow check. |
- if (Utils::IsUint(kImmBits, imm)) { |
- __ xori(result, left, Immediate(imm)); |
- } else { |
- __ LoadImmediate(TMP, imm); |
- __ xor_(result, left, TMP); |
- } |
+ __ XorImmediate(result, left, imm); |
break; |
} |
case Token::kSHR: { |