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();