[aarch32] Apply veneers to out-of-range backwards conditional branches (#114)

Backwards (ie. bound) conditional branches that were out of range of the
immediate encoded in the instruction would fail, so apply a veneer to
these instruction by inverting the condition and using an unconditional branch.
diff --git a/src/aarch32/macro-assembler-aarch32.cc b/src/aarch32/macro-assembler-aarch32.cc
index 89101e7..9ec86fb 100644
--- a/src/aarch32/macro-assembler-aarch32.cc
+++ b/src/aarch32/macro-assembler-aarch32.cc
@@ -1245,6 +1245,53 @@
 }
 
 
+void MacroAssembler::Delegate(InstructionType type,
+                              InstructionCondSizeL instruction,
+                              Condition cond,
+                              EncodingSize size,
+                              Location* location) {
+  VIXL_ASSERT(type == kB);
+
+  CONTEXT_SCOPE;
+
+  // Apply veneer to increase range of backwards conditional branches.
+  // This replaces:
+  //   label:
+  //    <instructions>
+  //    bcond label   ; T3
+  // With:
+  //   label:
+  //    <instructions>
+  //    binvcond skip ; T1
+  //    b label       ; T4
+  //   skip:
+  Location::Offset offset = location->GetLocation() -
+    (GetCursorOffset() + GetArchitectureStatePCOffset());
+  if (IsUsingT32() && location->IsBound() && ((offset & 0x1) == 0) &&
+      !cond.Is(al) && cond.IsNotNever()) {
+    // Bound locations must be earlier in the code.
+    VIXL_ASSERT(offset < 0);
+
+    // The offset must be within range of a T4 branch, accounting for the
+    // conditional branch (T1) we emit first, in order to jump over it.
+    offset -= k16BitT32InstructionSizeInBytes;
+    if (offset >= -16777216) {
+      CodeBufferCheckScope scope(this, k16BitT32InstructionSizeInBytes +
+                                       k32BitT32InstructionSizeInBytes);
+      Label skip;
+      b(cond.Negate(), Narrow, &skip);
+      b(location);
+      Bind(&skip);
+      return;
+    } else {
+      VIXL_ABORT_WITH_MSG("Conditional branch too far for veneer.\n");
+    }
+  }
+
+  Assembler::Delegate(type, instruction, cond, size, location);
+}
+
+
 template <typename T>
 static inline bool IsI64BitPattern(T imm) {
   for (T mask = 0xff << ((sizeof(T) - 1) * 8); mask != 0; mask >>= 8) {
diff --git a/src/aarch32/macro-assembler-aarch32.h b/src/aarch32/macro-assembler-aarch32.h
index 390b908..702f7cc 100644
--- a/src/aarch32/macro-assembler-aarch32.h
+++ b/src/aarch32/macro-assembler-aarch32.h
@@ -908,6 +908,12 @@
                         InstructionRL instruction,
                         Register rn,
                         Location* location) VIXL_OVERRIDE;
+  // B
+  virtual void Delegate(InstructionType type,
+                        InstructionCondSizeL instruction,
+                        Condition cond,
+                        EncodingSize size,
+                        Location* location) VIXL_OVERRIDE;
   // VMOV
   virtual void Delegate(InstructionType type,
                         InstructionCondDtSSop instruction,
diff --git a/test/aarch32/test-disasm-a32.cc b/test/aarch32/test-disasm-a32.cc
index c229c2f..95bb26e 100644
--- a/test/aarch32/test-disasm-a32.cc
+++ b/test/aarch32/test-disasm-a32.cc
@@ -1700,6 +1700,41 @@
 }
 
 
+TEST(macro_assembler_b_cond_t32) {
+  SETUP();
+
+#ifdef VIXL_INCLUDE_TARGET_T32
+  // Ensure backward conditional branches are veneered correctly.
+  __ UseT32();
+  int pc_off = __ GetArchitectureStatePCOffset();
+
+  // Largest encodable backwards offset.
+  int curs = __ GetCursorOffset() + pc_off;
+  Label label_neg1m(curs - 1048576);
+  COMPARE_T32(B(ne, &label_neg1m), "bne 0xfff00004\n");
+
+  // Next largest cannot be encoded.
+  curs = __ GetCursorOffset() + pc_off;
+  Label label_neg1m_plus_inst(curs - (1048576 + 2));
+  COMPARE_T32(B(ne, &label_neg1m_plus_inst), "beq 0x00000006\n"
+                                             "b 0xfff00002\n");
+
+  // Offset that requires largest unconditional branch in veneer.
+  curs = __ GetCursorOffset() + pc_off;
+  Label label_neg16m(curs - (16777216 - 2));
+  COMPARE_T32(B(ne, &label_neg16m), "beq 0x00000006\n"
+                                    "b 0xff000006\n");
+
+  // Next largest cannot be veneered.
+  curs = __ GetCursorOffset() + pc_off;
+  Label label_neg16m_plus_inst(curs - 16777216);
+  MUST_FAIL_TEST_T32(B(ne, &label_neg16m_plus_inst),
+                     "Conditional branch too far for veneer.\n");
+#endif
+
+  CLEANUP();
+}
+
 #ifdef VIXL_NEGATIVE_TESTING
 TEST(assembler_crc_negative) {
   SETUP();
diff --git a/tools/code_coverage.log b/tools/code_coverage.log
index 77cbd3a..bc12d15 100644
--- a/tools/code_coverage.log
+++ b/tools/code_coverage.log
@@ -31,3 +31,4 @@
 1715261843 82.84% 97.60% 94.69%
 1718190785 82.85% 97.60% 94.70%
 1722595938 82.94% 97.78% 94.72%
+1728570468 82.94% 97.78% 94.71%