| 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());
|
| + 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,196 @@
|
|
|
|
|
| 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: {
|
| + if (deopt == NULL) {
|
| + __ AddImmediate(result, left, -imm);
|
| + } else {
|
| + __ SubImmediateDetectOverflow(result, left, imm, CMPRES);
|
| + __ bltz(CMPRES, deopt);
|
| + }
|
| + break;
|
| + }
|
| + 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(TMP1, value);
|
| + __ mult(left, TMP1);
|
| + __ 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 +1411,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 +1475,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 +1612,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 +1639,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())) {
|
|
|