AArch64: fixes in A64 code generation.

- Disabled special method compilation, as it requires hard-float ABI,
- Disabled suspend checks, as runtime is not yet ready (e.g. trampolines
  are not setting the suspend register, etc),
- Changing definition of zero register (the zero register has now 0x3f
  as its register number),
- Fixing some issues with handling of cmp instructions in the assembler:
  we now use the shift-register rather than the extended-register variant
  of cmp and cmn,
- Partially fixing register setup (register sN is now mapped to dN),
- Fixing and completing implementation of register spills/unspills,
- Fixing LoadBaseDispBody() and StoreBaseDispBody().

Change-Id: Ia49ba48b6ca0f782380066345b7a198cb6c1dc1d
diff --git a/compiler/dex/quick/arm64/utility_arm64.cc b/compiler/dex/quick/arm64/utility_arm64.cc
index 77e4c3c..39e9fad 100644
--- a/compiler/dex/quick/arm64/utility_arm64.cc
+++ b/compiler/dex/quick/arm64/utility_arm64.cc
@@ -360,18 +360,17 @@
   return NewLIR1(opcode, r_dest_src.GetReg());
 }
 
-LIR* Arm64Mir2Lir::OpRegRegShift(OpKind op, int r_dest_src1, int r_src2,
-                                 int shift, bool is_wide) {
-  ArmOpcode wide = (is_wide) ? WIDE(0) : UNWIDE(0);
+LIR* Arm64Mir2Lir::OpRegRegShift(OpKind op, RegStorage r_dest_src1, RegStorage r_src2, int shift) {
+  ArmOpcode wide = (r_dest_src1.Is64Bit()) ? WIDE(0) : UNWIDE(0);
+  CHECK_EQ(r_dest_src1.Is64Bit(), r_src2.Is64Bit());
   ArmOpcode opcode = kA64Brk1d;
 
-  switch (OP_KIND_UNWIDE(op)) {
+  switch (op) {
     case kOpCmn:
-      opcode = kA64Cmn3Rro;
+      opcode = kA64Cmn3rro;
       break;
     case kOpCmp:
-      // TODO(Arm64): check the instruction above: "cmp w0, w1" is rendered as "cmp w0, w1, uxtb".
-      opcode = kA64Cmp3Rro;
+      opcode = kA64Cmp3rro;
       break;
     case kOpMov:
       opcode = kA64Mov2rr;
@@ -388,39 +387,38 @@
     case kOpRev:
       DCHECK_EQ(shift, 0);
       // Binary, but rm is encoded twice.
-      return NewLIR3(kA64Rev2rr | wide, r_dest_src1, r_src2, r_src2);
+      return NewLIR3(kA64Rev2rr | wide, r_dest_src1.GetReg(), r_src2.GetReg(), r_src2.GetReg());
       break;
     case kOpRevsh:
       // Binary, but rm is encoded twice.
-      return NewLIR3(kA64Rev162rr | wide, r_dest_src1, r_src2, r_src2);
+      return NewLIR3(kA64Rev162rr | wide, r_dest_src1.GetReg(), r_src2.GetReg(), r_src2.GetReg());
       break;
     case kOp2Byte:
       DCHECK_EQ(shift, ENCODE_NO_SHIFT);
       // "sbfx r1, r2, #imm1, #imm2" is "sbfm r1, r2, #imm1, #(imm1 + imm2 - 1)".
       // For now we use sbfm directly.
-      return NewLIR4(kA64Sbfm4rrdd | wide, r_dest_src1, r_src2, 0, 7);
+      return NewLIR4(kA64Sbfm4rrdd | wide, r_dest_src1.GetReg(), r_src2.GetReg(), 0, 7);
     case kOp2Short:
       DCHECK_EQ(shift, ENCODE_NO_SHIFT);
       // For now we use sbfm rather than its alias, sbfx.
-      return NewLIR4(kA64Sbfm4rrdd | wide, r_dest_src1, r_src2, 0, 15);
+      return NewLIR4(kA64Sbfm4rrdd | wide, r_dest_src1.GetReg(), r_src2.GetReg(), 0, 15);
     case kOp2Char:
       // "ubfx r1, r2, #imm1, #imm2" is "ubfm r1, r2, #imm1, #(imm1 + imm2 - 1)".
       // For now we use ubfm directly.
       DCHECK_EQ(shift, ENCODE_NO_SHIFT);
-      return NewLIR4(kA64Ubfm4rrdd | wide, r_dest_src1, r_src2, 0, 15);
+      return NewLIR4(kA64Ubfm4rrdd | wide, r_dest_src1.GetReg(), r_src2.GetReg(), 0, 15);
     default:
-      return OpRegRegRegShift(op, r_dest_src1, r_dest_src1, r_src2, shift);
+      return OpRegRegRegShift(op, r_dest_src1.GetReg(), r_dest_src1.GetReg(), r_src2.GetReg(), shift);
   }
 
   DCHECK(!IsPseudoLirOp(opcode));
   if (EncodingMap[opcode].flags & IS_BINARY_OP) {
     DCHECK_EQ(shift, ENCODE_NO_SHIFT);
-    return NewLIR2(opcode | wide, r_dest_src1, r_src2);
+    return NewLIR2(opcode | wide, r_dest_src1.GetReg(), r_src2.GetReg());
   } else if (EncodingMap[opcode].flags & IS_TERTIARY_OP) {
     ArmEncodingKind kind = EncodingMap[opcode].field_loc[2].kind;
-    if (kind == kFmtExtend || kind == kFmtShift) {
-      DCHECK_EQ(kind == kFmtExtend, IsExtendEncoding(shift));
-      return NewLIR3(opcode | wide, r_dest_src1, r_src2, shift);
+    if (kind == kFmtShift) {
+      return NewLIR3(opcode | wide, r_dest_src1.GetReg(), r_src2.GetReg(), shift);
     }
   }
 
@@ -429,8 +427,7 @@
 }
 
 LIR* Arm64Mir2Lir::OpRegReg(OpKind op, RegStorage r_dest_src1, RegStorage r_src2) {
-  return OpRegRegShift(op, r_dest_src1.GetReg(), r_src2.GetReg(), ENCODE_NO_SHIFT,
-                       r_dest_src1.Is64Bit());
+  return OpRegRegShift(op, r_dest_src1, r_src2, ENCODE_NO_SHIFT);
 }
 
 LIR* Arm64Mir2Lir::OpMovRegMem(RegStorage r_dest, RegStorage r_base, int offset, MoveType move_type) {
@@ -452,7 +449,7 @@
                                     int r_src2, int shift, bool is_wide) {
   ArmOpcode opcode = kA64Brk1d;
 
-  switch (OP_KIND_UNWIDE(op)) {
+  switch (op) {
     case kOpAdd:
       opcode = kA64Add4rrro;
       break;
@@ -525,10 +522,10 @@
   ArmOpcode opcode = kA64Brk1d;
   ArmOpcode alt_opcode = kA64Brk1d;
   int32_t log_imm = -1;
-  bool is_wide = OP_KIND_IS_WIDE(op);
+  bool is_wide = r_dest.Is64Bit();
   ArmOpcode wide = (is_wide) ? WIDE(0) : UNWIDE(0);
 
-  switch (OP_KIND_UNWIDE(op)) {
+  switch (op) {
     case kOpLsl: {
       // "lsl w1, w2, #imm" is an alias of "ubfm w1, w2, #(-imm MOD 32), #(31-imm)"
       // and "lsl x1, x2, #imm" of "ubfm x1, x2, #(-imm MOD 32), #(31-imm)".
@@ -639,7 +636,7 @@
     return res;
   }
 
-  switch (OP_KIND_UNWIDE(op)) {
+  switch (op) {
     case kOpAdd:
       neg_opcode = kA64Sub4RRdT;
       opcode = kA64Add4RRdT;
@@ -828,99 +825,66 @@
                                     OpSize size) {
   LIR* load = NULL;
   ArmOpcode opcode = kA64Brk1d;
-  bool short_form = false;
-  int encoded_disp = displacement;
+  ArmOpcode alt_opcode = kA64Brk1d;
+  int scale = 0;
+
   switch (size) {
     case kDouble:     // Intentional fall-through.
     case kWord:       // Intentional fall-through.
     case k64:
-      DCHECK_EQ(encoded_disp & 0x3, 0);
+      scale = 3;
       if (r_dest.IsFloat()) {
-        // Currently double values may be misaligned.
-        if ((displacement & 0x7) == 0 && displacement >= 0 && displacement <= 32760) {
-          // Can use scaled load.
-          opcode = FWIDE(kA64Ldr3fXD);
-          encoded_disp >>= 3;
-          short_form = true;
-        } else if (IS_SIGNED_IMM9(displacement)) {
-          // Can use unscaled load.
-          opcode = FWIDE(kA64Ldur3fXd);
-          short_form = true;
-        } else {
-          short_form = false;
-        }
+        DCHECK(r_dest.IsDouble());
+        opcode = FWIDE(kA64Ldr3fXD);
+        alt_opcode = FWIDE(kA64Ldur3fXd);
       } else {
-        // Currently long values may be misaligned.
-        if ((displacement & 0x7) == 0 && displacement >= 0 && displacement <= 32760) {
-          // Can use scaled store.
-          opcode = FWIDE(kA64Ldr3rXD);
-          encoded_disp >>= 3;
-          short_form = true;
-        } else if (IS_SIGNED_IMM9(displacement)) {
-          // Can use unscaled store.
-          opcode = FWIDE(kA64Ldur3rXd);
-          short_form = true;
-        }  // else: use long sequence (short_form = false).
+        opcode = FWIDE(kA64Ldr3rXD);
+        alt_opcode = FWIDE(kA64Ldur3rXd);
       }
       break;
     case kSingle:     // Intentional fall-through.
     case k32:         // Intentional fall-trough.
     case kReference:
+      scale = 2;
       if (r_dest.IsFloat()) {
+        DCHECK(r_dest.IsSingle());
         opcode = kA64Ldr3fXD;
-        if (displacement <= 1020) {
-          short_form = true;
-          encoded_disp >>= 2;
-        }
-        break;
-      }
-      if (displacement <= 16380 && displacement >= 0) {
-        DCHECK_EQ((displacement & 0x3), 0);
-        short_form = true;
-        encoded_disp >>= 2;
+      } else {
         opcode = kA64Ldr3rXD;
       }
       break;
     case kUnsignedHalf:
-      if (displacement < 64 && displacement >= 0) {
-        DCHECK_EQ((displacement & 0x1), 0);
-        short_form = true;
-        encoded_disp >>= 1;
-        opcode = kA64Ldrh3wXF;
-      } else if (displacement < 4092 && displacement >= 0) {
-        short_form = true;
-        opcode = kA64Ldrh3wXF;
-      }
+      scale = 1;
+      opcode = kA64Ldrh3wXF;
       break;
     case kSignedHalf:
-      short_form = true;
+      scale = 1;
       opcode = kA64Ldrsh3rXF;
       break;
     case kUnsignedByte:
-      short_form = true;
       opcode = kA64Ldrb3wXd;
       break;
     case kSignedByte:
-      short_form = true;
       opcode = kA64Ldrsb3rXd;
       break;
     default:
       LOG(FATAL) << "Bad size: " << size;
   }
 
-  if (short_form) {
-    load = NewLIR3(opcode, r_dest.GetReg(), r_base.GetReg(), encoded_disp);
+  bool displacement_is_aligned = (displacement & ((1 << scale) - 1)) == 0;
+  int scaled_disp = displacement >> scale;
+  if (displacement_is_aligned && scaled_disp >= 0 && scaled_disp < 4096) {
+    // Can use scaled load.
+    load = NewLIR3(opcode, r_dest.GetReg(), r_base.GetReg(), scaled_disp);
+  } else if (alt_opcode != kA64Brk1d && IS_SIGNED_IMM9(displacement)) {
+    // Can use unscaled load.
+    load = NewLIR3(alt_opcode, r_dest.GetReg(), r_base.GetReg(), displacement);
   } else {
-    RegStorage reg_offset = AllocTemp();
-    LoadConstant(reg_offset, encoded_disp);
-    if (r_dest.IsFloat()) {
-      // No index ops - must use a long sequence.  Turn the offset into a direct pointer.
-      OpRegReg(kOpAdd, reg_offset, r_base);
-      load = LoadBaseDispBody(reg_offset, 0, r_dest, size);
-    } else {
-      load = LoadBaseIndexed(r_base, reg_offset, r_dest, 0, size);
-    }
-    FreeTemp(reg_offset);
+    // Use long sequence.
+    RegStorage r_scratch = AllocTemp();
+    LoadConstant(r_scratch, displacement);
+    load = LoadBaseIndexed(r_base, r_scratch, r_dest, 0, size);
+    FreeTemp(r_scratch);
   }
 
   // TODO: in future may need to differentiate Dalvik accesses w/ spills
@@ -947,92 +911,64 @@
                                      OpSize size) {
   LIR* store = NULL;
   ArmOpcode opcode = kA64Brk1d;
-  bool short_form = false;
-  int encoded_disp = displacement;
+  ArmOpcode alt_opcode = kA64Brk1d;
+  int scale = 0;
+
   switch (size) {
     case kDouble:     // Intentional fall-through.
     case kWord:       // Intentional fall-through.
     case k64:
-      DCHECK_EQ(encoded_disp & 0x3, 0);
+      scale = 3;
       if (r_src.IsFloat()) {
-        // Currently double values may be misaligned.
-        if ((displacement & 0x7) == 0 && displacement >= 0 && displacement <= 32760) {
-          // Can use scaled store.
-          opcode = FWIDE(kA64Str3fXD);
-          encoded_disp >>= 3;
-          short_form = true;
-        } else if (IS_SIGNED_IMM9(displacement)) {
-          // Can use unscaled store.
-          opcode = FWIDE(kA64Stur3fXd);
-          short_form = true;
-        }  // else: use long sequence (short_form = false).
+        DCHECK(r_src.IsDouble());
+        opcode = FWIDE(kA64Str3fXD);
+        alt_opcode = FWIDE(kA64Stur3fXd);
       } else {
-        // Currently long values may be misaligned.
-        if ((displacement & 0x7) == 0 && displacement >= 0 && displacement <= 32760) {
-          // Can use scaled store.
-          opcode = FWIDE(kA64Str3rXD);
-          encoded_disp >>= 3;
-          short_form = true;
-        } else if (IS_SIGNED_IMM9(displacement)) {
-          // Can use unscaled store.
-          opcode = FWIDE(kA64Stur3rXd);
-          short_form = true;
-        }  // else: use long sequence (short_form = false).
+        opcode = FWIDE(kA64Str3rXD);
+        alt_opcode = FWIDE(kA64Stur3rXd);
       }
       break;
     case kSingle:     // Intentional fall-through.
     case k32:         // Intentional fall-trough.
     case kReference:
+      scale = 2;
       if (r_src.IsFloat()) {
         DCHECK(r_src.IsSingle());
-        DCHECK_EQ(encoded_disp & 0x3, 0);
         opcode = kA64Str3fXD;
-        if (displacement <= 1020) {
-          short_form = true;
-          encoded_disp >>= 2;
-        }
-        break;
-      }
-
-      if (displacement <= 16380 && displacement >= 0) {
-        DCHECK_EQ((displacement & 0x3), 0);
-        short_form = true;
-        encoded_disp >>= 2;
+      } else {
         opcode = kA64Str3rXD;
       }
       break;
     case kUnsignedHalf:
     case kSignedHalf:
-      DCHECK_EQ((displacement & 0x1), 0);
-      short_form = true;
-      encoded_disp >>= 1;
+      scale = 1;
       opcode = kA64Strh3wXF;
       break;
     case kUnsignedByte:
     case kSignedByte:
-      short_form = true;
       opcode = kA64Strb3wXd;
       break;
     default:
       LOG(FATAL) << "Bad size: " << size;
   }
 
-  if (short_form) {
-    store = NewLIR3(opcode, r_src.GetReg(), r_base.GetReg(), encoded_disp);
+  bool displacement_is_aligned = (displacement & ((1 << scale) - 1)) == 0;
+  int scaled_disp = displacement >> scale;
+  if (displacement_is_aligned && scaled_disp >= 0 && scaled_disp < 4096) {
+    // Can use scaled store.
+    store = NewLIR3(opcode, r_src.GetReg(), r_base.GetReg(), scaled_disp);
+  } else if (alt_opcode != kA64Brk1d && IS_SIGNED_IMM9(displacement)) {
+    // Can use unscaled store.
+    store = NewLIR3(alt_opcode, r_src.GetReg(), r_base.GetReg(), displacement);
   } else {
+    // Use long sequence.
     RegStorage r_scratch = AllocTemp();
-    LoadConstant(r_scratch, encoded_disp);
-    if (r_src.IsFloat()) {
-      // No index ops - must use a long sequence.  Turn the offset into a direct pointer.
-      OpRegReg(kOpAdd, r_scratch, r_base);
-      store = StoreBaseDispBody(r_scratch, 0, r_src, size);
-    } else {
-      store = StoreBaseIndexed(r_base, r_scratch, r_src, 0, size);
-    }
+    LoadConstant(r_scratch, displacement);
+    store = StoreBaseIndexed(r_base, r_scratch, r_src, 0, size);
     FreeTemp(r_scratch);
   }
 
-  // TODO: In future, may need to differentiate Dalvik & spill accesses
+  // TODO: In future, may need to differentiate Dalvik & spill accesses.
   if (r_base == rs_rA64_SP) {
     AnnotateDalvikRegAccess(store, displacement >> 2, false /* is_load */, r_src.Is64Bit());
   }