ARM: VIXL32: Improve codegen on CBZ/CBNZ

This patch improves codegen on CBZ/CBNZ.
CompareAndBranchIfZero/CompareAndBranchIfNonZero are introduced.

These two functions can generate CMP+Bcc or Cbz/Cbnz.
CMP+Bcc are generated by default.
If a hint is given (is_far_target = false) and rn and label
can all fit into Cbz/Cbnz, then Cbz/Cbnz is generated.
Prefer these two interfaces to using vixl32::MacroAssembler::Cbz/Cbnz.
In T32, Cbz/Cbnz instructions have following limitations:
- Far targets, which are over 126 bytes away, are not supported.
- Only low registers can be encoded.
- Backward branches are not supported.

Test: ART_USE_VIXL_ARM_BACKEND=true m test-art-host
Test: ART_USE_VIXL_ARM_BACKEND=true m test-art-target

Change-Id: I5d2ada19ea4f83dab78baf0cf78c72e99e58d946
diff --git a/compiler/utils/arm/assembler_arm_vixl.cc b/compiler/utils/arm/assembler_arm_vixl.cc
index e3b9fb6..1aaa231 100644
--- a/compiler/utils/arm/assembler_arm_vixl.cc
+++ b/compiler/utils/arm/assembler_arm_vixl.cc
@@ -429,5 +429,31 @@
   }
 }
 
+void ArmVIXLMacroAssembler::CompareAndBranchIfZero(vixl32::Register rn,
+                                                   vixl32::Label* label,
+                                                   bool is_far_target) {
+  if (!is_far_target && rn.IsLow() && !label->IsBound()) {
+    // In T32, Cbz/Cbnz instructions have following limitations:
+    // - There are only 7 bits (i:imm5:0) to encode branch target address (cannot be far target).
+    // - Only low registers (i.e R0 .. R7) can be encoded.
+    // - Only forward branches (unbound labels) are supported.
+    Cbz(rn, label);
+    return;
+  }
+  Cmp(rn, 0);
+  B(eq, label);
+}
+
+void ArmVIXLMacroAssembler::CompareAndBranchIfNonZero(vixl32::Register rn,
+                                                      vixl32::Label* label,
+                                                      bool is_far_target) {
+  if (!is_far_target && rn.IsLow() && !label->IsBound()) {
+    Cbnz(rn, label);
+    return;
+  }
+  Cmp(rn, 0);
+  B(ne, label);
+}
+
 }  // namespace arm
 }  // namespace art
diff --git a/compiler/utils/arm/assembler_arm_vixl.h b/compiler/utils/arm/assembler_arm_vixl.h
index e020628..b4a4abc 100644
--- a/compiler/utils/arm/assembler_arm_vixl.h
+++ b/compiler/utils/arm/assembler_arm_vixl.h
@@ -37,6 +37,25 @@
 namespace art {
 namespace arm {
 
+class ArmVIXLMacroAssembler FINAL : public vixl32::MacroAssembler {
+ public:
+  // The following interfaces can generate CMP+Bcc or Cbz/Cbnz.
+  // CMP+Bcc are generated by default.
+  // If a hint is given (is_far_target = false) and rn and label can all fit into Cbz/Cbnz,
+  // then Cbz/Cbnz is generated.
+  // Prefer following interfaces to using vixl32::MacroAssembler::Cbz/Cbnz.
+  // In T32, Cbz/Cbnz instructions have following limitations:
+  // - Far targets, which are over 126 bytes away, are not supported.
+  // - Only low registers can be encoded.
+  // - Backward branches are not supported.
+  void CompareAndBranchIfZero(vixl32::Register rn,
+                              vixl32::Label* label,
+                              bool is_far_target = true);
+  void CompareAndBranchIfNonZero(vixl32::Register rn,
+                                 vixl32::Label* label,
+                                 bool is_far_target = true);
+};
+
 class ArmVIXLAssembler FINAL : public Assembler {
  private:
   class ArmException;
@@ -48,7 +67,7 @@
   }
 
   virtual ~ArmVIXLAssembler() {}
-  vixl32::MacroAssembler* GetVIXLAssembler() { return &vixl_masm_; }
+  ArmVIXLMacroAssembler* GetVIXLAssembler() { return &vixl_masm_; }
   void FinalizeCode() OVERRIDE;
 
   // Size of generated code.
@@ -117,7 +136,7 @@
 
  private:
   // VIXL assembler.
-  vixl32::MacroAssembler vixl_masm_;
+  ArmVIXLMacroAssembler vixl_masm_;
 };
 
 // Thread register declaration.
diff --git a/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc b/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc
index 23b2774..b2bbd72 100644
--- a/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc
+++ b/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc
@@ -574,10 +574,12 @@
 
   switch (condition) {
     case JNIMacroUnaryCondition::kZero:
-      ___ Cbz(test.AsArm().AsVIXLRegister(), ArmVIXLJNIMacroLabel::Cast(label)->AsArm());
+      ___ CompareAndBranchIfZero(test.AsArm().AsVIXLRegister(),
+                                 ArmVIXLJNIMacroLabel::Cast(label)->AsArm());
       break;
     case JNIMacroUnaryCondition::kNotZero:
-      ___ Cbnz(test.AsArm().AsVIXLRegister(), ArmVIXLJNIMacroLabel::Cast(label)->AsArm());
+      ___ CompareAndBranchIfNonZero(test.AsArm().AsVIXLRegister(),
+                                    ArmVIXLJNIMacroLabel::Cast(label)->AsArm());
       break;
     default:
       LOG(FATAL) << "Not implemented unary condition: " << static_cast<int>(condition);