Enhance GenArrayGet, GenArrayPut for x86

As pointed out by Ian Rogers, the x86 versions didn't optimize
handling of constant index expressions.  Added that support,
simplified checking of constant indices, and removed the use of
a temporary register for the 'wide' cases by using x86 scaled
addressing mode.

Change-Id: I82174e4e3674752d00d7c4730496f59d69f5f173
Signed-off-by: Mark Mendell <mark.p.mendell@intel.com>
diff --git a/compiler/dex/quick/x86/assemble_x86.cc b/compiler/dex/quick/x86/assemble_x86.cc
index c9c4f55..c24f0e3 100644
--- a/compiler/dex/quick/x86/assemble_x86.cc
+++ b/compiler/dex/quick/x86/assemble_x86.cc
@@ -772,6 +772,13 @@
   EmitImm(entry, imm);
 }
 
+void X86Mir2Lir::EmitMemImm(const X86EncodingMap* entry, uint8_t base, int disp, int32_t imm) {
+  EmitPrefixAndOpcode(entry);
+  EmitModrmDisp(entry->skeleton.modrm_opcode, base, disp);
+  DCHECK_EQ(0, entry->skeleton.ax_opcode);
+  EmitImm(entry, imm);
+}
+
 void X86Mir2Lir::EmitThreadImm(const X86EncodingMap* entry, int disp, int imm) {
   EmitPrefixAndOpcode(entry);
   uint8_t modrm = (0 << 6) | (entry->skeleton.modrm_opcode << 3) | rBP;
@@ -1127,6 +1134,9 @@
       case kMemReg:  // lir operands - 0: base, 1: disp, 2: reg
         EmitMemReg(entry, lir->operands[0], lir->operands[1], lir->operands[2]);
         break;
+      case kMemImm:  // lir operands - 0: base, 1: disp, 2: immediate
+        EmitMemImm(entry, lir->operands[0], lir->operands[1], lir->operands[2]);
+        break;
       case kArrayReg:  // lir operands - 0: base, 1: index, 2: scale, 3: disp, 4: reg
         EmitArrayReg(entry, lir->operands[0], lir->operands[1], lir->operands[2],
                      lir->operands[3], lir->operands[4]);
diff --git a/compiler/dex/quick/x86/codegen_x86.h b/compiler/dex/quick/x86/codegen_x86.h
index 6552607..62ec77a 100644
--- a/compiler/dex/quick/x86/codegen_x86.h
+++ b/compiler/dex/quick/x86/codegen_x86.h
@@ -115,6 +115,8 @@
     void GenXorLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
     LIR* GenRegMemCheck(ConditionCode c_code, int reg1, int base, int offset,
                                 ThrowKind kind);
+    LIR* GenMemImmedCheck(ConditionCode c_code, int base, int offset, int check_value,
+                          ThrowKind kind);
     RegLocation GenDivRem(RegLocation rl_dest, int reg_lo, int reg_hi, bool is_div);
     RegLocation GenDivRemLit(RegLocation rl_dest, int reg_lo, int lit, bool is_div);
     void GenCmpLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
@@ -184,6 +186,7 @@
     void EmitOpArray(const X86EncodingMap* entry, uint8_t base, uint8_t index,
                      int scale, int disp);
     void EmitMemReg(const X86EncodingMap* entry, uint8_t base, int disp, uint8_t reg);
+    void EmitMemImm(const X86EncodingMap* entry, uint8_t base, int disp, int32_t imm);
     void EmitRegMem(const X86EncodingMap* entry, uint8_t reg, uint8_t base, int disp);
     void EmitRegArray(const X86EncodingMap* entry, uint8_t reg, uint8_t base, uint8_t index,
                       int scale, int disp);
diff --git a/compiler/dex/quick/x86/int_x86.cc b/compiler/dex/quick/x86/int_x86.cc
index 340c74a..8ff9ded 100644
--- a/compiler/dex/quick/x86/int_x86.cc
+++ b/compiler/dex/quick/x86/int_x86.cc
@@ -38,6 +38,20 @@
 }
 
 /*
+ * Perform a compare of memory to immediate value
+ */
+LIR* X86Mir2Lir::GenMemImmedCheck(ConditionCode c_code,
+                                int base, int offset, int check_value, ThrowKind kind) {
+  LIR* tgt = RawLIR(0, kPseudoThrowTarget, kind,
+                    current_dalvik_offset_, base, check_value, 0);
+  NewLIR3(IS_SIMM8(check_value) ? kX86Cmp32MI8 : kX86Cmp32MI, base, offset, check_value);
+  LIR* branch = OpCondBranch(c_code, tgt);
+  // Remember branch target - will process later
+  throw_launchpads_.Insert(tgt);
+  return branch;
+}
+
+/*
  * Compare two 64-bit values
  *    x = y     return  0
  *    x < y     return -1
@@ -521,41 +535,49 @@
                              RegLocation rl_index, RegLocation rl_dest, int scale) {
   RegisterClass reg_class = oat_reg_class_by_size(size);
   int len_offset = mirror::Array::LengthOffset().Int32Value();
-  int data_offset;
   RegLocation rl_result;
   rl_array = LoadValue(rl_array, kCoreReg);
-  rl_index = LoadValue(rl_index, kCoreReg);
 
+  int data_offset;
   if (size == kLong || size == kDouble) {
     data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Int32Value();
   } else {
     data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Int32Value();
   }
 
+  bool constant_index = rl_index.is_const;
+  int32_t constant_index_value = 0;
+  if (!constant_index) {
+    rl_index = LoadValue(rl_index, kCoreReg);
+  } else {
+    constant_index_value = mir_graph_->ConstantValue(rl_index);
+    // If index is constant, just fold it into the data offset
+    data_offset += constant_index_value << scale;
+    // treat as non array below
+    rl_index.low_reg = INVALID_REG;
+  }
+
   /* null object? */
   GenNullCheck(rl_array.s_reg_low, rl_array.low_reg, opt_flags);
 
   if (!(opt_flags & MIR_IGNORE_RANGE_CHECK)) {
-    /* if (rl_index >= [rl_array + len_offset]) goto kThrowArrayBounds */
-    GenRegMemCheck(kCondUge, rl_index.low_reg, rl_array.low_reg,
-                   len_offset, kThrowArrayBounds);
+    if (constant_index) {
+      GenMemImmedCheck(kCondLs, rl_array.low_reg, len_offset,
+                       constant_index_value, kThrowConstantArrayBounds);
+    } else {
+      GenRegMemCheck(kCondUge, rl_index.low_reg, rl_array.low_reg,
+                     len_offset, kThrowArrayBounds);
+    }
   }
+  rl_result = EvalLoc(rl_dest, reg_class, true);
   if ((size == kLong) || (size == kDouble)) {
-    int reg_addr = AllocTemp();
-    OpLea(reg_addr, rl_array.low_reg, rl_index.low_reg, scale, data_offset);
-    FreeTemp(rl_array.low_reg);
-    FreeTemp(rl_index.low_reg);
-    rl_result = EvalLoc(rl_dest, reg_class, true);
-    LoadBaseIndexedDisp(reg_addr, INVALID_REG, 0, 0, rl_result.low_reg,
+    LoadBaseIndexedDisp(rl_array.low_reg, rl_index.low_reg, scale, data_offset, rl_result.low_reg,
                         rl_result.high_reg, size, INVALID_SREG);
     StoreValueWide(rl_dest, rl_result);
   } else {
-    rl_result = EvalLoc(rl_dest, reg_class, true);
-
     LoadBaseIndexedDisp(rl_array.low_reg, rl_index.low_reg, scale,
                         data_offset, rl_result.low_reg, INVALID_REG, size,
                         INVALID_SREG);
-
     StoreValue(rl_dest, rl_result);
   }
 }
@@ -577,14 +599,29 @@
   }
 
   rl_array = LoadValue(rl_array, kCoreReg);
-  rl_index = LoadValue(rl_index, kCoreReg);
+  bool constant_index = rl_index.is_const;
+  int32_t constant_index_value = 0;
+  if (!constant_index) {
+    rl_index = LoadValue(rl_index, kCoreReg);
+  } else {
+    // If index is constant, just fold it into the data offset
+    constant_index_value = mir_graph_->ConstantValue(rl_index);
+    data_offset += constant_index_value << scale;
+    // treat as non array below
+    rl_index.low_reg = INVALID_REG;
+  }
 
   /* null object? */
   GenNullCheck(rl_array.s_reg_low, rl_array.low_reg, opt_flags);
 
   if (!(opt_flags & MIR_IGNORE_RANGE_CHECK)) {
-    /* if (rl_index >= [rl_array + len_offset]) goto kThrowArrayBounds */
-    GenRegMemCheck(kCondUge, rl_index.low_reg, rl_array.low_reg, len_offset, kThrowArrayBounds);
+    if (constant_index) {
+      GenMemImmedCheck(kCondLs, rl_array.low_reg, len_offset,
+                       constant_index_value, kThrowConstantArrayBounds);
+    } else {
+      GenRegMemCheck(kCondUge, rl_index.low_reg, rl_array.low_reg,
+                     len_offset, kThrowArrayBounds);
+    }
   }
   if ((size == kLong) || (size == kDouble)) {
     rl_src = LoadValueWide(rl_src, reg_class);
@@ -603,7 +640,9 @@
   }
   if (card_mark) {
     // Free rl_index if its a temp. Ensures there are 2 free regs for card mark.
-    FreeTemp(rl_index.low_reg);
+    if (!constant_index) {
+      FreeTemp(rl_index.low_reg);
+    }
     MarkGCCard(rl_src.low_reg, rl_array.low_reg);
   }
 }