Chromium Code Reviews| Index: runtime/vm/intermediate_language_mips.cc |
| =================================================================== |
| --- runtime/vm/intermediate_language_mips.cc (revision 21386) |
| +++ runtime/vm/intermediate_language_mips.cc (working copy) |
| @@ -405,6 +405,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) { |
| + __ LoadImmediate(value_cid_reg, kSmiCid); |
| + } |
| + __ andi(TMP1, value_reg, Immediate(kSmiTagMask)); |
| + if (value_is_smi == NULL) { |
| + __ beq(TMP1, ZR, &done); |
| + } else { |
| + __ beq(TMP1, ZR, value_is_smi); |
| + } |
| + __ 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, |
| @@ -430,11 +449,62 @@ |
| } |
| +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; |
| + } |
| +} |
| + |
| + |
| static void EmitSmiComparisonOp(FlowGraphCompiler* compiler, |
| 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(CMPRES, right.reg(), left.constant()); |
|
regis
2013/04/12 21:18:04
Do you still have a reason to dedicate a register
zra
2013/04/12 22:19:59
Yah, I think it's probably less error-prone to kee
|
| + true_condition = FlowGraphCompiler::FlipCondition(true_condition); |
| + } else if (right.IsConstant()) { |
| + __ CompareObject(CMPRES, left.reg(), right.constant()); |
| + } else { |
| + __ subu(CMPRES, left.reg(), right.reg()); |
| + } |
| + |
| + if (branch != NULL) { |
| + branch->EmitBranchOnCondition(compiler, true_condition); |
| + } else { |
| + Register result = locs.out().reg(); |
| + Label done, is_true; |
| + switch (true_condition) { |
| + case EQ: __ beq(CMPRES, ZR, &is_true); break; |
| + case NE: __ bne(CMPRES, ZR, &is_true); break; |
| + case GT: __ bgtz(CMPRES, &is_true); break; |
| + case GE: __ bgez(CMPRES, &is_true); break; |
| + case LT: __ bltz(CMPRES, &is_true); break; |
| + case LE: __ blez(CMPRES, &is_true); break; |
| + default: |
| + UNREACHABLE(); |
| + break; |
| + } |
| + __ LoadObject(result, Bool::False()); |
| + __ b(&done); |
| + __ Bind(&is_true); |
| + __ LoadObject(result, Bool::True()); |
| + __ Bind(&done); |
| + } |
| } |
| @@ -446,6 +516,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, |
| @@ -543,19 +621,135 @@ |
| 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(A0)); |
| + locs->set_in(1, Location::RegisterLocation(A1)); |
| + locs->set_out(Location::RegisterLocation(V0)); |
| + 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 A2. |
| + const intptr_t kNumArguments = 2; |
| + LoadValueCid(compiler, A2, left); |
| + compiler->EmitTestAndCall(ICData::Handle(ic_data()->AsUnaryClassChecks()), |
| + A2, // 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::kDeopt, |
| + 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(CMPRES, V0, Bool::True()); |
| + branch->EmitBranchOnCondition(compiler, EQ); |
| } |
| @@ -880,13 +1074,191 @@ |
| 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. |
|
regis
2013/04/12 21:18:04
I merged the kSUB case with kADD case on ARM. Sinc
zra
2013/04/12 22:19:59
Done.
|
| + } |
| + case Token::kADD: { |
| + if (deopt == NULL) { |
| + __ AddImmediate(result, left, imm); |
| + } else { |
| + __ AddImmediateDetectOverflow(result, left, imm, CMPRES); |
| + __ bltz(CMPRES, deopt); |
| + } |
| + break; |
| + } |
| + case Token::kMUL: { |
| + // Keep left value tagged and untag right value. |
| + const intptr_t value = Smi::Cast(constant).Value(); |
| + if (value == 2) { |
| + __ sll(result, left, 1); |
| + } else { |
| + __ LoadImmediate(TMP, value); |
| + __ mult(left, TMP); |
| + __ mflo(result); |
| + } |
| + if (deopt != NULL) { |
| + UNIMPLEMENTED(); |
| + } |
| + break; |
| + } |
| + case Token::kTRUNCDIV: { |
| + UNIMPLEMENTED(); |
| + break; |
| + } |
| + case Token::kBIT_AND: { |
| + // No overflow check. |
| + if (Utils::IsUint(kImmBits, imm)) { |
| + __ andi(result, left, Immediate(imm)); |
| + } else { |
| + __ LoadImmediate(TMP1, imm); |
| + __ and_(result, left, TMP1); |
| + } |
| + break; |
| + } |
| + case Token::kBIT_OR: { |
| + // No overflow check. |
| + if (Utils::IsUint(kImmBits, imm)) { |
| + __ ori(result, left, Immediate(imm)); |
| + } else { |
| + __ LoadImmediate(TMP1, imm); |
| + __ or_(result, left, TMP1); |
| + } |
| + break; |
| + } |
| + case Token::kBIT_XOR: { |
| + // No overflow check. |
| + if (Utils::IsUint(kImmBits, imm)) { |
| + __ xori(result, left, Immediate(imm)); |
| + } else { |
| + __ LoadImmediate(TMP1, imm); |
| + __ xor_(result, left, TMP1); |
| + } |
| + 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) { |
| + __ addu(result, left, right); |
| + } else { |
| + __ AdduDetectOverflow(result, left, right, CMPRES); |
| + __ bltz(CMPRES, deopt); |
| + } |
| + break; |
| + } |
| + case Token::kSUB: { |
| + if (deopt == NULL) { |
| + __ subu(result, left, right); |
| + } else { |
| + __ SubuDetectOverflow(result, left, right, CMPRES); |
| + __ bltz(CMPRES, deopt); |
| + } |
| + break; |
| + } |
| + case Token::kMUL: { |
| + __ SmiUntag(left); |
| + __ mult(left, right); |
| + __ mflo(result); |
| + if (deopt != NULL) { |
| + UNIMPLEMENTED(); |
| + } |
| + break; |
| + } |
| + case Token::kBIT_AND: { |
| + // No overflow check. |
| + __ and_(result, left, right); |
| + break; |
| + } |
| + case Token::kBIT_OR: { |
| + // No overflow check. |
| + __ or_(result, left, right); |
| + break; |
| + } |
| + case Token::kBIT_XOR: { |
| + // No overflow check. |
| + __ xor_(result, left, 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; |
| + } |
| } |
| @@ -1034,13 +1406,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. |
| + __ lw(T0, Address(SP, (instance_call()->ArgumentCount() - 1) * kWordSize)); |
| + |
| + LoadValueCid(compiler, T2, T0, |
| + (ic_data().GetReceiverClassIdAt(0) == kSmiCid) ? NULL : deopt); |
| + |
| + compiler->EmitTestAndCall(ic_data(), |
| + T2, // Class id register. |
| + instance_call()->ArgumentCount(), |
| + instance_call()->argument_names(), |
| + deopt, |
| + instance_call()->deopt_id(), |
| + instance_call()->token_pos(), |
| + locs()); |
| } |
| @@ -1067,13 +1470,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); |
| + __ andi(TMP1, value, Immediate(kSmiTagMask)); |
| + __ bne(TMP1, ZR, deopt); |
| } |
| @@ -1196,6 +1607,10 @@ |
| 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; |
| default: |
| OS::Print("Error: Condition not recognized: %d\n", condition); |
| UNIMPLEMENTED(); |
| @@ -1219,21 +1634,33 @@ |
| Condition true_condition) { |
| if (compiler->CanFallThroughTo(false_successor())) { |
| // If the next block is the false successor we will fall through to it. |
| - if (true_condition == EQ) { |
| - __ beq(CMPRES, ZR, compiler->GetJumpLabel(true_successor())); |
| - } else { |
| - ASSERT(true_condition == NE); |
| - __ bne(CMPRES, ZR, compiler->GetJumpLabel(true_successor())); |
| + Label* label = compiler->GetJumpLabel(true_successor()); |
| + switch (true_condition) { |
| + case EQ: __ beq(CMPRES, ZR, label); break; |
| + case NE: __ bne(CMPRES, ZR, label); break; |
| + case GT: __ bgtz(CMPRES, label); break; |
| + case GE: __ bgez(CMPRES, label); break; |
| + case LT: __ bltz(CMPRES, label); break; |
| + case LE: __ blez(CMPRES, label); break; |
| + default: |
| + UNREACHABLE(); |
| + break; |
| } |
| } else { |
| // If the next block is the true successor we negate comparison and fall |
| // through to it. |
| Condition false_condition = NegateCondition(true_condition); |
| - if (false_condition == EQ) { |
| - __ beq(CMPRES, ZR, compiler->GetJumpLabel(false_successor())); |
| - } else { |
| - ASSERT(false_condition == NE); |
| - __ bne(CMPRES, ZR, compiler->GetJumpLabel(false_successor())); |
| + Label* label = compiler->GetJumpLabel(false_successor()); |
| + switch (false_condition) { |
| + case EQ: __ beq(CMPRES, ZR, label); break; |
| + case NE: __ bne(CMPRES, ZR, label); break; |
| + case GT: __ bgtz(CMPRES, label); break; |
| + case GE: __ bgez(CMPRES, label); break; |
| + case LT: __ bltz(CMPRES, label); break; |
| + case LE: __ blez(CMPRES, label); break; |
| + default: |
| + UNREACHABLE(); |
| + break; |
| } |
| // Fall through or jump to the true successor. |
| if (!compiler->CanFallThroughTo(true_successor())) { |