Adds branch instructions and labels to the MIPS simulator, assembler, disassembler.
Review URL: https://codereview.chromium.org//12634030
git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@20419 260f80e4-7a28-3924-810f-c04153c831b5
diff --git a/runtime/vm/assembler_mips.cc b/runtime/vm/assembler_mips.cc
index f74e06e..88bd937 100644
--- a/runtime/vm/assembler_mips.cc
+++ b/runtime/vm/assembler_mips.cc
@@ -22,6 +22,40 @@
}
}
+
+void Assembler::Bind(Label* label) {
+ ASSERT(!label->IsBound());
+ int bound_pc = buffer_.Size();
+ while (label->IsLinked()) {
+ int32_t position = label->Position();
+ int32_t next = buffer_.Load<int32_t>(position);
+ // Reletive destination from an instruction after the branch.
+ int32_t dest = bound_pc - (position + Instr::kInstrSize);
+ int32_t encoded = Assembler::EncodeBranchOffset(dest, next);
+ buffer_.Store<int32_t>(position, encoded);
+ label->position_ = Assembler::DecodeBranchOffset(next);
+ }
+ label->BindTo(bound_pc);
+ delay_slot_available_ = false;
+}
+
+
+int32_t Assembler::EncodeBranchOffset(int32_t offset, int32_t instr) {
+ ASSERT(Utils::IsAligned(offset, 4));
+ ASSERT(Utils::IsInt(18, offset));
+
+ // Properly preserve only the bits supported in the instruction.
+ offset >>= 2;
+ offset &= kBranchOffsetMask;
+ return (instr & ~kBranchOffsetMask) | offset;
+}
+
+
+int Assembler::DecodeBranchOffset(int32_t instr) {
+ // Sign-extend, left-shift by 2.
+ return (((instr & kBranchOffsetMask) << 16) >> 14);
+}
+
} // namespace dart
#endif // defined TARGET_ARCH_MIPS
diff --git a/runtime/vm/assembler_mips.h b/runtime/vm/assembler_mips.h
index 17c419c..ed85493 100644
--- a/runtime/vm/assembler_mips.h
+++ b/runtime/vm/assembler_mips.h
@@ -147,9 +147,7 @@
UNIMPLEMENTED();
}
- void Bind(Label* label) {
- UNIMPLEMENTED();
- }
+ void Bind(Label* label);
// Misc. functionality
int CodeSize() const { return buffer_.Size(); }
@@ -245,6 +243,101 @@
EmitIType(ANDI, rs, rt, imm_value);
}
+ // Unconditional branch.
+ void b(Label* l) {
+ beq(R0, R0, l);
+ }
+
+ // Branch if equal.
+ void beq(Register rs, Register rt, Label* l) {
+ ASSERT(!in_delay_slot_);
+ EmitBranch(BEQ, rs, rt, l);
+ EmitBranchDelayNop();
+ }
+
+ // Branch if equal, likely taken.
+ // Delay slot executed only when branch taken.
+ void beql(Register rs, Register rt, Label* l) {
+ ASSERT(!in_delay_slot_);
+ EmitBranch(BEQL, rs, rt, l);
+ EmitBranchDelayNop();
+ }
+
+ // Branch if rs >= 0.
+ void bgez(Register rs, Label* l) {
+ ASSERT(!in_delay_slot_);
+ EmitRegImmBranch(BGEZ, rs, l);
+ EmitBranchDelayNop();
+ }
+
+ // Branch if rs >= 0, likely taken.
+ // Delay slot executed only when branch taken.
+ void bgezl(Register rs, Label* l) {
+ ASSERT(!in_delay_slot_);
+ EmitRegImmBranch(BGEZL, rs, l);
+ EmitBranchDelayNop();
+ }
+
+ // Branch if rs > 0.
+ void bgtz(Register rs, Label* l) {
+ ASSERT(!in_delay_slot_);
+ EmitBranch(BGTZ, rs, R0, l);
+ EmitBranchDelayNop();
+ }
+
+ // Branch if rs > 0, likely taken.
+ // Delay slot executed only when branch taken.
+ void bgtzl(Register rs, Label* l) {
+ ASSERT(!in_delay_slot_);
+ EmitBranch(BGTZL, rs, R0, l);
+ EmitBranchDelayNop();
+ }
+
+ // Branch if rs <= 0.
+ void blez(Register rs, Label* l) {
+ ASSERT(!in_delay_slot_);
+ EmitBranch(BLEZ, rs, R0, l);
+ EmitBranchDelayNop();
+ }
+
+ // Branch if rs <= 0, likely taken.
+ // Delay slot executed only when branch taken.
+ void blezl(Register rs, Label* l) {
+ ASSERT(!in_delay_slot_);
+ EmitBranch(BLEZL, rs, R0, l);
+ EmitBranchDelayNop();
+ }
+
+ // Branch if rs < 0.
+ void bltz(Register rs, Label* l) {
+ ASSERT(!in_delay_slot_);
+ EmitRegImmBranch(BLTZ, rs, l);
+ EmitBranchDelayNop();
+ }
+
+ // Branch if rs < 0, likely taken.
+ // Delay slot executed only when branch taken.
+ void bltzl(Register rs, Label* l) {
+ ASSERT(!in_delay_slot_);
+ EmitRegImmBranch(BLTZL, rs, l);
+ EmitBranchDelayNop();
+ }
+
+ // Branch if not equal.
+ void bne(Register rs, Register rt, Label* l) {
+ ASSERT(!in_delay_slot_); // Jump within a delay slot is not supported.
+ EmitBranch(BNE, rs, rt, l);
+ EmitBranchDelayNop();
+ }
+
+ // Branch if not equal, likely taken.
+ // Delay slot executed only when branch taken.
+ void bnel(Register rs, Register rt, Label* l) {
+ ASSERT(!in_delay_slot_); // Jump within a delay slot is not supported.
+ EmitBranch(BNEL, rs, rt, l);
+ EmitBranchDelayNop();
+ }
+
void break_(int32_t code) {
ASSERT(Utils::IsUint(20, code));
Emit(SPECIAL << kOpcodeShift |
@@ -315,6 +408,10 @@
EmitRType(SPECIAL, R0, R0, rd, 0, MFLO);
}
+ void mov(Register rd, Register rs) {
+ or_(rd, rs, ZR);
+ }
+
void movn(Register rd, Register rs, Register rt) {
EmitRType(SPECIAL, rs, rt, rd, 0, MOVN);
}
@@ -331,6 +428,10 @@
EmitRType(SPECIAL, rs, rt, R0, 0, MULTU);
}
+ void nop() {
+ Emit(Instr::kNopInstruction);
+ }
+
void nor(Register rd, Register rs, Register rt) {
EmitRType(SPECIAL, rs, rt, rd, 0, NOR);
}
@@ -411,10 +512,6 @@
}
}
- void Move(Register rd, Register rs) {
- or_(rd, rs, ZR);
- }
-
private:
AssemblerBuffer buffer_;
GrowableObjectArray& object_pool_; // Objects and patchable jump targets.
@@ -496,6 +593,35 @@
func << kFunctionShift);
}
+ void EmitBranch(Opcode b, Register rs, Register rt, Label* label) {
+ if (label->IsBound()) {
+ // Reletive destination from an instruction after the branch.
+ int32_t dest = label->Position() - (buffer_.Size() + Instr::kInstrSize);
+ uint16_t dest_off = EncodeBranchOffset(dest, 0);
+ EmitIType(b, rs, rt, dest_off);
+ } else {
+ int position = buffer_.Size();
+ EmitIType(b, rs, rt, label->position_);
+ label->LinkTo(position);
+ }
+ }
+
+ void EmitRegImmBranch(RtRegImm b, Register rs, Label* label) {
+ if (label->IsBound()) {
+ // Reletive destination from an instruction after the branch.
+ int32_t dest = label->Position() - (buffer_.Size() + Instr::kInstrSize);
+ uint16_t dest_off = EncodeBranchOffset(dest, 0);
+ EmitRegImmType(REGIMM, rs, b, dest_off);
+ } else {
+ int position = buffer_.Size();
+ EmitRegImmType(REGIMM, rs, b, label->position_);
+ label->LinkTo(position);
+ }
+ }
+
+ static int32_t EncodeBranchOffset(int32_t offset, int32_t instr);
+ static int DecodeBranchOffset(int32_t instr);
+
void EmitBranchDelayNop() {
Emit(Instr::kNopInstruction); // Branch delay NOP.
delay_slot_available_ = true;
diff --git a/runtime/vm/assembler_mips_test.cc b/runtime/vm/assembler_mips_test.cc
index a0ee283..cf122b4 100644
--- a/runtime/vm/assembler_mips_test.cc
+++ b/runtime/vm/assembler_mips_test.cc
@@ -646,8 +646,351 @@
}
+ASSEMBLER_TEST_GENERATE(Beq_backward, assembler) {
+ Label l;
+
+ __ LoadImmediate(R1, 0);
+ __ LoadImmediate(R2, 1);
+ __ Bind(&l);
+ __ addiu(R1, R1, Immediate(1));
+ __ beq(R1, R2, &l);
+ __ ori(V0, R1, Immediate(0));
+ __ jr(RA);
+}
+
+
+ASSEMBLER_TEST_RUN(Beq_backward, test) {
+ typedef int (*SimpleCode)();
+ EXPECT_EQ(2, EXECUTE_TEST_CODE_INT32(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(Beq_backward_delay, assembler) {
+ Label l;
+
+ __ LoadImmediate(R1, 0);
+ __ LoadImmediate(R2, 1);
+ __ Bind(&l);
+ __ addiu(R1, R1, Immediate(1));
+ __ beq(R1, R2, &l);
+ __ delay_slot()->addiu(R1, R1, Immediate(1));
+ __ ori(V0, R1, Immediate(0));
+ __ jr(RA);
+}
+
+
+ASSEMBLER_TEST_RUN(Beq_backward_delay, test) {
+ typedef int (*SimpleCode)();
+ EXPECT_EQ(4, EXECUTE_TEST_CODE_INT32(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(Beq_forward_taken, assembler) {
+ Label l;
+
+ __ LoadImmediate(R5, 1);
+ __ LoadImmediate(R6, 1);
+
+ __ LoadImmediate(V0, 42);
+ __ beq(R5, R6, &l);
+ __ LoadImmediate(V0, 0);
+ __ Bind(&l);
+ __ jr(RA);
+}
+
+
+ASSEMBLER_TEST_RUN(Beq_forward_taken, test) {
+ typedef int (*SimpleCode)();
+ EXPECT_EQ(42, EXECUTE_TEST_CODE_INT32(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(Beq_forward_not_taken, assembler) {
+ Label l;
+
+ __ LoadImmediate(R5, 0);
+ __ LoadImmediate(R6, 1);
+
+ __ LoadImmediate(V0, 42);
+ __ beq(R5, R6, &l);
+ __ LoadImmediate(V0, 0);
+ __ Bind(&l);
+ __ jr(RA);
+}
+
+
+ASSEMBLER_TEST_RUN(Beq_forward_not_taken, test) {
+ typedef int (*SimpleCode)();
+ EXPECT_EQ(0, EXECUTE_TEST_CODE_INT32(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(Beq_forward_taken2, assembler) {
+ Label l;
+
+ __ LoadImmediate(R5, 1);
+ __ LoadImmediate(R6, 1);
+
+ __ LoadImmediate(V0, 42);
+ __ beq(R5, R6, &l);
+ __ nop();
+ __ nop();
+ __ LoadImmediate(V0, 0);
+ __ Bind(&l);
+ __ jr(RA);
+}
+
+
+ASSEMBLER_TEST_RUN(Beq_forward_taken2, test) {
+ typedef int (*SimpleCode)();
+ EXPECT_EQ(42, EXECUTE_TEST_CODE_INT32(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(Beq_forward_taken_delay, assembler) {
+ Label l;
+
+ __ LoadImmediate(R5, 1);
+ __ LoadImmediate(R6, 1);
+
+ __ LoadImmediate(V0, 42);
+ __ beq(R5, R6, &l);
+ __ delay_slot()->ori(V0, V0, Immediate(1));
+ __ LoadImmediate(V0, 0);
+ __ Bind(&l);
+ __ jr(RA);
+}
+
+
+ASSEMBLER_TEST_RUN(Beq_forward_taken_delay, test) {
+ typedef int (*SimpleCode)();
+ EXPECT_EQ(43, EXECUTE_TEST_CODE_INT32(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(Beq_forward_not_taken_delay, assembler) {
+ Label l;
+
+ __ LoadImmediate(R5, 0);
+ __ LoadImmediate(R6, 1);
+
+ __ LoadImmediate(V0, 42);
+ __ beq(R5, R6, &l);
+ __ delay_slot()->ori(V0, V0, Immediate(1));
+ __ addiu(V0, V0, Immediate(1));
+ __ Bind(&l);
+ __ jr(RA);
+}
+
+
+ASSEMBLER_TEST_RUN(Beq_forward_not_taken_delay, test) {
+ typedef int (*SimpleCode)();
+ EXPECT_EQ(44, EXECUTE_TEST_CODE_INT32(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(Beql_backward_delay, assembler) {
+ Label l;
+
+ __ LoadImmediate(R5, 0);
+ __ LoadImmediate(R6, 1);
+ __ Bind(&l);
+ __ addiu(R5, R5, Immediate(1));
+ __ beql(R5, R6, &l);
+ __ delay_slot()->addiu(R5, R5, Immediate(1));
+ __ ori(V0, R5, Immediate(0));
+ __ jr(RA);
+}
+
+
+ASSEMBLER_TEST_RUN(Beql_backward_delay, test) {
+ typedef int (*SimpleCode)();
+ EXPECT_EQ(3, EXECUTE_TEST_CODE_INT32(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(Bgez, assembler) {
+ Label l;
+
+ __ LoadImmediate(R5, 3);
+ __ Bind(&l);
+ __ bgez(R5, &l);
+ __ delay_slot()->addiu(R5, R5, Immediate(-1));
+ __ ori(V0, R5, Immediate(0));
+ __ jr(RA);
+}
+
+
+ASSEMBLER_TEST_RUN(Bgez, test) {
+ typedef int (*SimpleCode)();
+ EXPECT_EQ(-2, EXECUTE_TEST_CODE_INT32(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(Bgezl, assembler) {
+ Label l;
+
+ __ LoadImmediate(R5, 3);
+ __ Bind(&l);
+ __ bgezl(R5, &l);
+ __ delay_slot()->addiu(R5, R5, Immediate(-1));
+ __ ori(V0, R5, Immediate(0));
+ __ jr(RA);
+}
+
+
+ASSEMBLER_TEST_RUN(Bgezl, test) {
+ typedef int (*SimpleCode)();
+ EXPECT_EQ(-1, EXECUTE_TEST_CODE_INT32(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(Blez, assembler) {
+ Label l;
+
+ __ LoadImmediate(R5, -3);
+ __ Bind(&l);
+ __ blez(R5, &l);
+ __ delay_slot()->addiu(R5, R5, Immediate(1));
+ __ ori(V0, R5, Immediate(0));
+ __ jr(RA);
+}
+
+
+ASSEMBLER_TEST_RUN(Blez, test) {
+ typedef int (*SimpleCode)();
+ EXPECT_EQ(2, EXECUTE_TEST_CODE_INT32(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(Blezl, assembler) {
+ Label l;
+
+ __ LoadImmediate(R5, -3);
+ __ Bind(&l);
+ __ blezl(R5, &l);
+ __ delay_slot()->addiu(R5, R5, Immediate(1));
+ __ ori(V0, R5, Immediate(0));
+ __ jr(RA);
+}
+
+
+ASSEMBLER_TEST_RUN(Blezl, test) {
+ typedef int (*SimpleCode)();
+ EXPECT_EQ(1, EXECUTE_TEST_CODE_INT32(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(Bgtz, assembler) {
+ Label l;
+
+ __ LoadImmediate(R5, 3);
+ __ Bind(&l);
+ __ bgtz(R5, &l);
+ __ delay_slot()->addiu(R5, R5, Immediate(-1));
+ __ ori(V0, R5, Immediate(0));
+ __ jr(RA);
+}
+
+
+ASSEMBLER_TEST_RUN(Bgtz, test) {
+ typedef int (*SimpleCode)();
+ EXPECT_EQ(-1, EXECUTE_TEST_CODE_INT32(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(Bgtzl, assembler) {
+ Label l;
+
+ __ LoadImmediate(R5, 3);
+ __ Bind(&l);
+ __ bgtzl(R5, &l);
+ __ delay_slot()->addiu(R5, R5, Immediate(-1));
+ __ ori(V0, R5, Immediate(0));
+ __ jr(RA);
+}
+
+
+ASSEMBLER_TEST_RUN(Bgtzl, test) {
+ typedef int (*SimpleCode)();
+ EXPECT_EQ(0, EXECUTE_TEST_CODE_INT32(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(Bltz, assembler) {
+ Label l;
+
+ __ LoadImmediate(R5, -3);
+ __ Bind(&l);
+ __ bltz(R5, &l);
+ __ delay_slot()->addiu(R5, R5, Immediate(1));
+ __ ori(V0, R5, Immediate(0));
+ __ jr(RA);
+}
+
+
+ASSEMBLER_TEST_RUN(Bltz, test) {
+ typedef int (*SimpleCode)();
+ EXPECT_EQ(1, EXECUTE_TEST_CODE_INT32(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(Bltzl, assembler) {
+ Label l;
+
+ __ LoadImmediate(R5, -3);
+ __ Bind(&l);
+ __ bltzl(R5, &l);
+ __ delay_slot()->addiu(R5, R5, Immediate(1));
+ __ ori(V0, R5, Immediate(0));
+ __ jr(RA);
+}
+
+
+ASSEMBLER_TEST_RUN(Bltzl, test) {
+ typedef int (*SimpleCode)();
+ EXPECT_EQ(0, EXECUTE_TEST_CODE_INT32(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(Bne, assembler) {
+ Label l;
+
+ __ LoadImmediate(R5, 3);
+ __ Bind(&l);
+ __ bne(R5, R0, &l);
+ __ delay_slot()->addiu(R5, R5, Immediate(-1));
+ __ ori(V0, R5, Immediate(0));
+ __ jr(RA);
+}
+
+
+ASSEMBLER_TEST_RUN(Bne, test) {
+ typedef int (*SimpleCode)();
+ EXPECT_EQ(-1, EXECUTE_TEST_CODE_INT32(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(Bnel, assembler) {
+ Label l;
+
+ __ LoadImmediate(R5, 3);
+ __ Bind(&l);
+ __ bnel(R5, R0, &l);
+ __ delay_slot()->addiu(R5, R5, Immediate(-1));
+ __ ori(V0, R5, Immediate(0));
+ __ jr(RA);
+}
+
+
+ASSEMBLER_TEST_RUN(Bnel, test) {
+ typedef int (*SimpleCode)();
+ EXPECT_EQ(0, EXECUTE_TEST_CODE_INT32(SimpleCode, test->entry()));
+}
+
+
ASSEMBLER_TEST_GENERATE(Jalr_delay, assembler) {
- __ Move(R2, RA);
+ __ mov(R2, RA);
__ jalr(R2, RA);
__ delay_slot()->ori(V0, ZR, Immediate(42));
}
diff --git a/runtime/vm/constants_mips.h b/runtime/vm/constants_mips.h
index a8a0a26..95a0799 100644
--- a/runtime/vm/constants_mips.h
+++ b/runtime/vm/constants_mips.h
@@ -174,6 +174,8 @@
kInstrBits = 26,
kBreakCodeShift = 5,
kBreakCodeBits = 20,
+
+ kBranchOffsetMask = 0x0000ffff,
};
@@ -366,6 +368,10 @@
return static_cast<SpecialFunction>(Bits(kFunctionShift, kFunctionBits));
}
+ inline RtRegImm RegImmFnField() const {
+ return static_cast<RtRegImm>(Bits(kRtShift, kRtBits));
+ }
+
inline bool IsBreakPoint() {
return (OpcodeField() == SPECIAL) && (FunctionField() == BREAK);
}
diff --git a/runtime/vm/disassembler_mips.cc b/runtime/vm/disassembler_mips.cc
index 09f32cd..ff74333 100644
--- a/runtime/vm/disassembler_mips.cc
+++ b/runtime/vm/disassembler_mips.cc
@@ -39,6 +39,7 @@
void DecodeSpecial(Instr* instr);
void DecodeSpecial2(Instr* instr);
+ void DecodeRegImm(Instr* instr);
// Convenience functions.
char* get_buffer() const { return buffer_; }
@@ -137,6 +138,16 @@
}
return 4;
}
+ case 'd': {
+ ASSERT(STRING_STARTS_WITH(format, "dest"));
+ int off = instr->SImmField() << 2;
+ uword destination = reinterpret_cast<uword>(instr) + off;
+ buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
+ remaining_size_in_buffer(),
+ "%#"Px"",
+ destination);
+ return 4;
+ }
case 'i': {
ASSERT(STRING_STARTS_WITH(format, "imm"));
if (format[3] == 'u') {
@@ -359,6 +370,33 @@
}
+void MIPSDecoder::DecodeRegImm(Instr* instr) {
+ ASSERT(instr->OpcodeField() == REGIMM);
+ switch (instr->RegImmFnField()) {
+ case BGEZ: {
+ Format(instr, "bgez 'rs, 'dest");
+ break;
+ }
+ case BGEZL: {
+ Format(instr, "bgezl 'rs, 'dest");
+ break;
+ }
+ case BLTZ: {
+ Format(instr, "bltz 'rs, 'dest");
+ break;
+ }
+ case BLTZL: {
+ Format(instr, "bltzl 'rs, 'dest");
+ break;
+ }
+ default: {
+ Unknown(instr);
+ break;
+ }
+ }
+}
+
+
void MIPSDecoder::InstructionDecode(Instr* instr) {
switch (instr->OpcodeField()) {
case SPECIAL: {
@@ -369,6 +407,10 @@
DecodeSpecial2(instr);
break;
}
+ case REGIMM: {
+ DecodeRegImm(instr);
+ break;
+ }
case ADDIU: {
Format(instr, "addiu 'rt, 'rs, 'imms");
break;
@@ -377,6 +419,38 @@
Format(instr, "andi 'rt, 'rs, 'immu");
break;
}
+ case BEQ: {
+ Format(instr, "beq 'rs, 'rt, 'dest");
+ break;
+ }
+ case BEQL: {
+ Format(instr, "beql 'rs, 'rt, 'dest");
+ break;
+ }
+ case BGTZ: {
+ Format(instr, "bgtz 'rs, 'dest");
+ break;
+ }
+ case BGTZL: {
+ Format(instr, "bgtzl 'rs, 'dest");
+ break;
+ }
+ case BLEZ: {
+ Format(instr, "blez 'rs, 'dest");
+ break;
+ }
+ case BLEZL: {
+ Format(instr, "blezl 'rs, 'dest");
+ break;
+ }
+ case BNE: {
+ Format(instr, "bne 'rs, 'rt, 'dest");
+ break;
+ }
+ case BNEL: {
+ Format(instr, "bnel 'rs, 'rt, 'dest");
+ break;
+ }
case LB: {
Format(instr, "lb 'rt, 'imms('rs)");
break;
diff --git a/runtime/vm/simulator_mips.cc b/runtime/vm/simulator_mips.cc
index 827fb4a..1e70e4e 100644
--- a/runtime/vm/simulator_mips.cc
+++ b/runtime/vm/simulator_mips.cc
@@ -990,6 +990,65 @@
}
+void Simulator::DoBranch(Instr* instr, bool taken, bool likely) {
+ ASSERT(!delay_slot_);
+ int32_t imm_val = instr->SImmField() << 2;
+
+ uword next_pc;
+ if (taken) {
+ // imm_val is added to the address of the instruction following the branch.
+ next_pc = pc_ + imm_val + Instr::kInstrSize;
+ if (likely) {
+ ExecuteDelaySlot();
+ }
+ } else {
+ next_pc = pc_ + (2 * Instr::kInstrSize); // Next after delay slot.
+ }
+ if (!likely) {
+ ExecuteDelaySlot();
+ }
+ pc_ = next_pc - Instr::kInstrSize;
+
+ return;
+}
+
+
+void Simulator::DecodeRegImm(Instr* instr) {
+ ASSERT(instr->OpcodeField() == REGIMM);
+ switch (instr->RegImmFnField()) {
+ case BGEZ: {
+ // Format(instr, "bgez 'rs, 'dest");
+ int32_t rs_val = get_register(instr->RsField());
+ DoBranch(instr, rs_val >= 0, false);
+ break;
+ }
+ case BGEZL: {
+ // Format(instr, "bgezl 'rs, 'dest");
+ int32_t rs_val = get_register(instr->RsField());
+ DoBranch(instr, rs_val >= 0, true);
+ break;
+ }
+ case BLTZ: {
+ // Format(instr, "bltz 'rs, 'dest");
+ int32_t rs_val = get_register(instr->RsField());
+ DoBranch(instr, rs_val < 0, false);
+ break;
+ }
+ case BLTZL: {
+ // Format(instr, "bltzl 'rs, 'dest");
+ int32_t rs_val = get_register(instr->RsField());
+ DoBranch(instr, rs_val < 0, true);
+ break;
+ }
+ default: {
+ OS::PrintErr("DecodeRegImm: 0x%x\n", instr->InstructionBits());
+ UnimplementedInstruction(instr);
+ break;
+ }
+ }
+}
+
+
void Simulator::InstructionDecode(Instr* instr) {
switch (instr->OpcodeField()) {
case SPECIAL: {
@@ -1000,6 +1059,10 @@
DecodeSpecial2(instr);
break;
}
+ case REGIMM: {
+ DecodeRegImm(instr);
+ break;
+ }
case ADDIU: {
// Format(instr, "addiu 'rt, 'rs, 'imms");
int32_t rs_val = get_register(instr->RsField());
@@ -1015,6 +1078,62 @@
set_register(instr->RtField(), rs_val & instr->UImmField());
break;
}
+ case BEQ: {
+ // Format(instr, "beq 'rs, 'rt, 'dest");
+ int32_t rs_val = get_register(instr->RsField());
+ int32_t rt_val = get_register(instr->RtField());
+ DoBranch(instr, rs_val == rt_val, false);
+ break;
+ }
+ case BEQL: {
+ // Format(instr, "beql 'rs, 'rt, 'dest");
+ int32_t rs_val = get_register(instr->RsField());
+ int32_t rt_val = get_register(instr->RtField());
+ DoBranch(instr, rs_val == rt_val, true);
+ break;
+ }
+ case BGTZ: {
+ ASSERT(instr->RtField() == R0);
+ // Format(instr, "bgtz 'rs, 'dest");
+ int32_t rs_val = get_register(instr->RsField());
+ DoBranch(instr, rs_val > 0, false);
+ break;
+ }
+ case BGTZL: {
+ ASSERT(instr->RtField() == R0);
+ // Format(instr, "bgtzl 'rs, 'dest");
+ int32_t rs_val = get_register(instr->RsField());
+ DoBranch(instr, rs_val > 0, true);
+ break;
+ }
+ case BLEZ: {
+ ASSERT(instr->RtField() == R0);
+ // Format(instr, "blez 'rs, 'dest");
+ int32_t rs_val = get_register(instr->RsField());
+ DoBranch(instr, rs_val <= 0, false);
+ break;
+ }
+ case BLEZL: {
+ ASSERT(instr->RtField() == R0);
+ // Format(instr, "blezl 'rs, 'dest");
+ int32_t rs_val = get_register(instr->RsField());
+ DoBranch(instr, rs_val <= 0, true);
+ break;
+ }
+ case BNE: {
+ // Format(instr, "bne 'rs, 'rt, 'dest");
+ int32_t rs_val = get_register(instr->RsField());
+ int32_t rt_val = get_register(instr->RtField());
+ DoBranch(instr, rs_val != rt_val, false);
+ break;
+ }
+ case BNEL: {
+ // Format(instr, "bnel 'rs, 'rt, 'dest");
+ int32_t rs_val = get_register(instr->RsField());
+ int32_t rt_val = get_register(instr->RtField());
+ DoBranch(instr, rs_val != rt_val, true);
+ break;
+ }
case LB: {
// Format(instr, "lb 'rt, 'imms('rs)");
int32_t base_val = get_register(instr->RsField());
diff --git a/runtime/vm/simulator_mips.h b/runtime/vm/simulator_mips.h
index f6790ad..2407de1 100644
--- a/runtime/vm/simulator_mips.h
+++ b/runtime/vm/simulator_mips.h
@@ -118,8 +118,10 @@
inline void WriteH(uword addr, uint16_t value, Instr* isntr);
inline void WriteW(uword addr, int value, Instr* instr);
+ void DoBranch(Instr* instr, bool taken, bool likely);
void DecodeSpecial(Instr* instr);
void DecodeSpecial2(Instr* instr);
+ void DecodeRegImm(Instr* instr);
void InstructionDecode(Instr* instr);
void Execute();