Index: runtime/vm/intermediate_language_arm.cc |
=================================================================== |
--- runtime/vm/intermediate_language_arm.cc (revision 20980) |
+++ runtime/vm/intermediate_language_arm.cc (working copy) |
@@ -222,6 +222,21 @@ |
} |
+static Condition TokenKindToSmiCondition(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; |
+ } |
+} |
+ |
+ |
LocationSummary* EqualityCompareInstr::MakeLocationSummary() const { |
const intptr_t kNumInputs = 2; |
const bool is_checked_strict_equal = |
@@ -380,6 +395,25 @@ |
} |
+static void LoadValueCid(FlowGraphCompiler* compiler, |
+ Register value_cid_reg, |
+ Register value_reg, |
+ Label* value_is_smi = NULL) { |
+ Label done; |
+ if (value_is_smi == NULL) { |
+ __ mov(value_cid_reg, ShifterOperand(kSmiCid)); |
+ } |
+ __ tst(value_reg, ShifterOperand(kSmiTagMask)); |
+ if (value_is_smi == NULL) { |
+ __ b(&done, EQ); |
+ } else { |
+ __ b(value_is_smi, EQ); |
+ } |
+ __ LoadClassId(value_cid_reg, value_reg); |
+ __ Bind(&done); |
+} |
+ |
+ |
// Emit code when ICData's targets are all Object == (which is ===). |
static void EmitCheckedStrictEqual(FlowGraphCompiler* compiler, |
const ICData& ic_data, |
@@ -409,7 +443,33 @@ |
const LocationSummary& locs, |
Token::Kind kind, |
BranchInstr* branch) { |
- UNIMPLEMENTED(); |
+ Location left = locs.in(0); |
+ Location right = locs.in(1); |
+ ASSERT(!left.IsConstant() || !right.IsConstant()); |
+ |
+ Condition true_condition = TokenKindToSmiCondition(kind); |
+ |
+ if (left.IsConstant()) { |
+ __ CompareObject(right.reg(), left.constant()); |
+ true_condition = FlowGraphCompiler::FlipCondition(true_condition); |
+ } else if (right.IsConstant()) { |
+ __ CompareObject(left.reg(), right.constant()); |
+ } else { |
+ __ cmp(left.reg(), ShifterOperand(right.reg())); |
+ } |
+ |
+ if (branch != NULL) { |
+ branch->EmitBranchOnCondition(compiler, true_condition); |
+ } else { |
+ Register result = locs.out().reg(); |
+ Label done, is_true; |
+ __ b(&is_true, true_condition); |
+ __ LoadObject(result, Bool::False()); |
+ __ b(&done); |
+ __ Bind(&is_true); |
+ __ LoadObject(result, Bool::True()); |
+ __ Bind(&done); |
+ } |
} |
@@ -421,6 +481,14 @@ |
} |
+static void EmitUnboxedMintComparisonOp(FlowGraphCompiler* compiler, |
+ const LocationSummary& locs, |
+ Token::Kind kind, |
+ BranchInstr* branch) { |
+ UNIMPLEMENTED(); |
+} |
+ |
+ |
static void EmitDoubleComparisonOp(FlowGraphCompiler* compiler, |
const LocationSummary& locs, |
Token::Kind kind, |
@@ -518,19 +586,137 @@ |
LocationSummary* RelationalOpInstr::MakeLocationSummary() const { |
- UNIMPLEMENTED(); |
- return NULL; |
+ const intptr_t kNumInputs = 2; |
+ const intptr_t kNumTemps = 0; |
+ if (operands_class_id() == kMintCid) { |
+ const intptr_t kNumTemps = 2; |
+ LocationSummary* locs = |
+ new LocationSummary(kNumInputs, kNumTemps, LocationSummary::kNoCall); |
+ locs->set_in(0, Location::RequiresFpuRegister()); |
+ locs->set_in(1, Location::RequiresFpuRegister()); |
+ locs->set_temp(0, Location::RequiresRegister()); |
+ locs->set_temp(1, Location::RequiresRegister()); |
+ locs->set_out(Location::RequiresRegister()); |
+ return locs; |
+ } |
+ if (operands_class_id() == kDoubleCid) { |
+ LocationSummary* summary = |
+ new LocationSummary(kNumInputs, kNumTemps, LocationSummary::kNoCall); |
+ summary->set_in(0, Location::RequiresFpuRegister()); |
+ summary->set_in(1, Location::RequiresFpuRegister()); |
+ summary->set_out(Location::RequiresRegister()); |
+ return summary; |
+ } else if (operands_class_id() == kSmiCid) { |
+ LocationSummary* summary = |
+ new LocationSummary(kNumInputs, kNumTemps, LocationSummary::kNoCall); |
+ summary->set_in(0, Location::RegisterOrConstant(left())); |
+ // Only one input can be a constant operand. The case of two constant |
+ // operands should be handled by constant propagation. |
+ summary->set_in(1, summary->in(0).IsConstant() |
+ ? Location::RequiresRegister() |
+ : Location::RegisterOrConstant(right())); |
+ summary->set_out(Location::RequiresRegister()); |
+ return summary; |
+ } |
+ LocationSummary* locs = |
+ new LocationSummary(kNumInputs, kNumTemps, LocationSummary::kCall); |
+ // Pick arbitrary fixed input registers because this is a call. |
+ locs->set_in(0, Location::RegisterLocation(R0)); |
+ locs->set_in(1, Location::RegisterLocation(R1)); |
+ locs->set_out(Location::RegisterLocation(R0)); |
+ return locs; |
} |
void RelationalOpInstr::EmitNativeCode(FlowGraphCompiler* compiler) { |
- UNIMPLEMENTED(); |
+ if (operands_class_id() == kSmiCid) { |
+ EmitSmiComparisonOp(compiler, *locs(), kind(), NULL); |
+ return; |
+ } |
+ if (operands_class_id() == kMintCid) { |
+ EmitUnboxedMintComparisonOp(compiler, *locs(), kind(), NULL); |
+ return; |
+ } |
+ if (operands_class_id() == kDoubleCid) { |
+ EmitDoubleComparisonOp(compiler, *locs(), kind(), NULL); |
+ return; |
+ } |
+ |
+ // Push arguments for the call. |
+ // TODO(fschneider): Split this instruction into different types to avoid |
+ // explicitly pushing arguments to the call here. |
+ Register left = locs()->in(0).reg(); |
+ Register right = locs()->in(1).reg(); |
+ __ Push(left); |
+ __ Push(right); |
+ if (HasICData() && (ic_data()->NumberOfChecks() > 0)) { |
+ Label* deopt = compiler->AddDeoptStub(deopt_id(), kDeoptRelationalOp); |
+ // Load class into R2. Since this is a call, any register except |
+ // the fixed input registers would be ok. |
+ ASSERT((left != R2) && (right != R2)); |
+ const intptr_t kNumArguments = 2; |
+ LoadValueCid(compiler, R2, left); |
+ compiler->EmitTestAndCall(ICData::Handle(ic_data()->AsUnaryClassChecks()), |
+ R2, // Class id register. |
+ kNumArguments, |
+ Array::Handle(), // No named arguments. |
+ deopt, // Deoptimize target. |
+ deopt_id(), |
+ token_pos(), |
+ locs()); |
+ return; |
+ } |
+ const String& function_name = |
+ String::ZoneHandle(Symbols::New(Token::Str(kind()))); |
+ if (!compiler->is_optimizing()) { |
+ compiler->AddCurrentDescriptor(PcDescriptors::kDeoptBefore, |
+ deopt_id(), |
+ token_pos()); |
+ } |
+ const intptr_t kNumArguments = 2; |
+ const intptr_t kNumArgsChecked = 2; // Type-feedback. |
+ ICData& relational_ic_data = ICData::ZoneHandle(ic_data()->raw()); |
+ if (compiler->is_optimizing() && FLAG_propagate_ic_data) { |
+ ASSERT(!ic_data()->IsNull()); |
+ if (ic_data()->NumberOfChecks() == 0) { |
+ // IC call for reoptimization populates original ICData. |
+ relational_ic_data = ic_data()->raw(); |
+ } else { |
+ // Megamorphic call. |
+ relational_ic_data = ic_data()->AsUnaryClassChecks(); |
+ } |
+ } else { |
+ relational_ic_data = ICData::New(compiler->parsed_function().function(), |
+ function_name, |
+ deopt_id(), |
+ kNumArgsChecked); |
+ } |
+ compiler->GenerateInstanceCall(deopt_id(), |
+ token_pos(), |
+ kNumArguments, |
+ Array::ZoneHandle(), // No optional arguments. |
+ locs(), |
+ relational_ic_data); |
} |
void RelationalOpInstr::EmitBranchCode(FlowGraphCompiler* compiler, |
BranchInstr* branch) { |
- UNIMPLEMENTED(); |
+ if (operands_class_id() == kSmiCid) { |
+ EmitSmiComparisonOp(compiler, *locs(), kind(), branch); |
+ return; |
+ } |
+ if (operands_class_id() == kMintCid) { |
+ EmitUnboxedMintComparisonOp(compiler, *locs(), kind(), branch); |
+ return; |
+ } |
+ if (operands_class_id() == kDoubleCid) { |
+ EmitDoubleComparisonOp(compiler, *locs(), kind(), branch); |
+ return; |
+ } |
+ EmitNativeCode(compiler); |
+ __ CompareObject(R0, Bool::True()); |
+ branch->EmitBranchOnCondition(compiler, EQ); |
} |
@@ -854,13 +1040,194 @@ |
LocationSummary* BinarySmiOpInstr::MakeLocationSummary() const { |
- UNIMPLEMENTED(); |
- return NULL; |
+ const intptr_t kNumInputs = 2; |
+ if (op_kind() == Token::kTRUNCDIV) { |
+ UNIMPLEMENTED(); |
+ return NULL; |
+ } else { |
+ const intptr_t kNumTemps = 0; |
+ LocationSummary* summary = |
+ new LocationSummary(kNumInputs, kNumTemps, LocationSummary::kNoCall); |
+ summary->set_in(0, Location::RequiresRegister()); |
+ summary->set_in(1, Location::RegisterOrSmiConstant(right())); |
+ // We make use of 3-operand instructions by not requiring result register |
+ // to be identical to first input register as on Intel. |
+ summary->set_out(Location::RequiresRegister()); |
+ return summary; |
+ } |
} |
void BinarySmiOpInstr::EmitNativeCode(FlowGraphCompiler* compiler) { |
- UNIMPLEMENTED(); |
+ if (op_kind() == Token::kSHL) { |
+ UNIMPLEMENTED(); |
+ return; |
+ } |
+ |
+ ASSERT(!is_truncating()); |
+ Register left = locs()->in(0).reg(); |
+ Register result = locs()->out().reg(); |
+ Label* deopt = NULL; |
+ if (CanDeoptimize()) { |
+ deopt = compiler->AddDeoptStub(deopt_id(), kDeoptBinarySmiOp); |
+ } |
+ |
+ if (locs()->in(1).IsConstant()) { |
+ const Object& constant = locs()->in(1).constant(); |
+ ASSERT(constant.IsSmi()); |
+ int32_t imm = reinterpret_cast<int32_t>(constant.raw()); |
+ switch (op_kind()) { |
+ case Token::kSUB: { |
+ imm = -imm; // TODO(regis): What if deopt != NULL && imm == 0x80000000? |
+ // Fall through. |
+ } |
+ case Token::kADD: { |
+ if (deopt == NULL) { |
+ __ AddImmediate(result, left, imm); |
+ } else { |
+ __ AddImmediateSetFlags(result, left, imm); |
+ __ b(deopt, VS); |
+ } |
+ break; |
+ } |
+ case Token::kMUL: { |
+ // Keep left value tagged and untag right value. |
+ const intptr_t value = Smi::Cast(constant).Value(); |
+ if (value == 2) { |
+ __ mov(result, ShifterOperand(left, LSL, 1)); |
+ } else { |
+ __ LoadImmediate(IP, value); |
+ __ mul(result, left, IP); |
+ } |
+ if (deopt != NULL) { |
+ UNIMPLEMENTED(); |
+ } |
+ break; |
+ } |
+ case Token::kTRUNCDIV: { |
+ UNIMPLEMENTED(); |
+ break; |
+ } |
+ case Token::kBIT_AND: { |
+ // No overflow check. |
+ ShifterOperand shifter_op; |
+ if (ShifterOperand::CanHold(imm, &shifter_op)) { |
+ __ and_(result, left, shifter_op); |
+ } else { |
+ // TODO(regis): Try to use bic. |
+ __ LoadImmediate(IP, imm); |
+ __ and_(result, left, ShifterOperand(IP)); |
+ } |
+ break; |
+ } |
+ case Token::kBIT_OR: { |
+ // No overflow check. |
+ ShifterOperand shifter_op; |
+ if (ShifterOperand::CanHold(imm, &shifter_op)) { |
+ __ orr(result, left, shifter_op); |
+ } else { |
+ // TODO(regis): Try to use orn. |
+ __ LoadImmediate(IP, imm); |
+ __ orr(result, left, ShifterOperand(IP)); |
+ } |
+ break; |
+ } |
+ case Token::kBIT_XOR: { |
+ // No overflow check. |
+ ShifterOperand shifter_op; |
+ if (ShifterOperand::CanHold(imm, &shifter_op)) { |
+ __ eor(result, left, shifter_op); |
+ } else { |
+ __ LoadImmediate(IP, imm); |
+ __ eor(result, left, ShifterOperand(IP)); |
+ } |
+ break; |
+ } |
+ case Token::kSHR: { |
+ UNIMPLEMENTED(); |
+ break; |
+ } |
+ |
+ default: |
+ UNREACHABLE(); |
+ break; |
+ } |
+ return; |
+ } |
+ |
+ Register right = locs()->in(1).reg(); |
+ switch (op_kind()) { |
+ case Token::kADD: { |
+ if (deopt == NULL) { |
+ __ add(result, left, ShifterOperand(right)); |
+ } else { |
+ __ adds(result, left, ShifterOperand(right)); |
+ __ b(deopt, VS); |
+ } |
+ break; |
+ } |
+ case Token::kSUB: { |
+ if (deopt == NULL) { |
+ __ sub(result, left, ShifterOperand(right)); |
+ } else { |
+ __ subs(result, left, ShifterOperand(right)); |
+ __ b(deopt, VS); |
+ } |
+ break; |
+ } |
+ case Token::kMUL: { |
+ __ SmiUntag(left); |
+ __ mul(result, left, right); |
+ if (deopt != NULL) { |
+ UNIMPLEMENTED(); |
+ } |
+ break; |
+ } |
+ case Token::kBIT_AND: { |
+ // No overflow check. |
+ __ and_(result, left, ShifterOperand(right)); |
+ break; |
+ } |
+ case Token::kBIT_OR: { |
+ // No overflow check. |
+ __ orr(result, left, ShifterOperand(right)); |
+ break; |
+ } |
+ case Token::kBIT_XOR: { |
+ // No overflow check. |
+ __ eor(result, left, ShifterOperand(right)); |
+ break; |
+ } |
+ case Token::kTRUNCDIV: { |
+ UNIMPLEMENTED(); |
+ break; |
+ } |
+ case Token::kSHR: { |
+ UNIMPLEMENTED(); |
+ break; |
+ } |
+ case Token::kDIV: { |
+ // Dispatches to 'Double./'. |
+ // TODO(srdjan): Implement as conversion to double and double division. |
+ UNREACHABLE(); |
+ break; |
+ } |
+ case Token::kMOD: { |
+ // TODO(srdjan): Implement. |
+ UNREACHABLE(); |
+ break; |
+ } |
+ case Token::kOR: |
+ case Token::kAND: { |
+ // Flow graph builder has dissected this operation to guarantee correct |
+ // behavior (short-circuit evaluation). |
+ UNREACHABLE(); |
+ break; |
+ } |
+ default: |
+ UNREACHABLE(); |
+ break; |
+ } |
} |
@@ -986,13 +1353,44 @@ |
LocationSummary* PolymorphicInstanceCallInstr::MakeLocationSummary() const { |
- UNIMPLEMENTED(); |
- return NULL; |
+ return MakeCallSummary(); |
} |
void PolymorphicInstanceCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) { |
- UNIMPLEMENTED(); |
+ Label* deopt = compiler->AddDeoptStub(instance_call()->deopt_id(), |
+ kDeoptPolymorphicInstanceCallTestFail); |
+ if (ic_data().NumberOfChecks() == 0) { |
+ __ b(deopt); |
+ return; |
+ } |
+ ASSERT(ic_data().num_args_tested() == 1); |
+ if (!with_checks()) { |
+ ASSERT(ic_data().HasOneTarget()); |
+ const Function& target = Function::ZoneHandle(ic_data().GetTargetAt(0)); |
+ compiler->GenerateStaticCall(instance_call()->deopt_id(), |
+ instance_call()->token_pos(), |
+ target, |
+ instance_call()->ArgumentCount(), |
+ instance_call()->argument_names(), |
+ locs()); |
+ return; |
+ } |
+ |
+ // Load receiver into R0. |
+ __ ldr(R0, Address(SP, (instance_call()->ArgumentCount() - 1) * kWordSize)); |
+ |
+ LoadValueCid(compiler, R2, R0, |
+ (ic_data().GetReceiverClassIdAt(0) == kSmiCid) ? NULL : deopt); |
+ |
+ compiler->EmitTestAndCall(ic_data(), |
+ R2, // Class id register. |
+ instance_call()->ArgumentCount(), |
+ instance_call()->argument_names(), |
+ deopt, |
+ instance_call()->deopt_id(), |
+ instance_call()->token_pos(), |
+ locs()); |
} |
@@ -1019,13 +1417,21 @@ |
LocationSummary* CheckSmiInstr::MakeLocationSummary() const { |
- UNIMPLEMENTED(); |
- return NULL; |
+ const intptr_t kNumInputs = 1; |
+ const intptr_t kNumTemps = 0; |
+ LocationSummary* summary = |
+ new LocationSummary(kNumInputs, kNumTemps, LocationSummary::kNoCall); |
+ summary->set_in(0, Location::RequiresRegister()); |
+ return summary; |
} |
void CheckSmiInstr::EmitNativeCode(FlowGraphCompiler* compiler) { |
- UNIMPLEMENTED(); |
+ Register value = locs()->in(0).reg(); |
+ Label* deopt = compiler->AddDeoptStub(deopt_id(), |
+ kDeoptCheckSmi); |
+ __ tst(value, ShifterOperand(kSmiTagMask)); |
+ __ b(deopt, NE); |
} |