int_x86.cc revision 58af1f9385742f70aca4fcb5e13aba53b8be2ef4
1/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/* This file contains codegen for the X86 ISA */
18
19#include "codegen_x86.h"
20#include "dex/quick/mir_to_lir-inl.h"
21#include "mirror/array.h"
22#include "x86_lir.h"
23
24namespace art {
25
26/*
27 * Perform register memory operation.
28 */
29LIR* X86Mir2Lir::GenRegMemCheck(ConditionCode c_code,
30                                int reg1, int base, int offset, ThrowKind kind) {
31  LIR* tgt = RawLIR(0, kPseudoThrowTarget, kind,
32                    current_dalvik_offset_, reg1, base, offset);
33  OpRegMem(kOpCmp, reg1, base, offset);
34  LIR* branch = OpCondBranch(c_code, tgt);
35  // Remember branch target - will process later
36  throw_launchpads_.Insert(tgt);
37  return branch;
38}
39
40/*
41 * Perform a compare of memory to immediate value
42 */
43LIR* X86Mir2Lir::GenMemImmedCheck(ConditionCode c_code,
44                                int base, int offset, int check_value, ThrowKind kind) {
45  LIR* tgt = RawLIR(0, kPseudoThrowTarget, kind,
46                    current_dalvik_offset_, base, check_value, 0);
47  NewLIR3(IS_SIMM8(check_value) ? kX86Cmp32MI8 : kX86Cmp32MI, base, offset, check_value);
48  LIR* branch = OpCondBranch(c_code, tgt);
49  // Remember branch target - will process later
50  throw_launchpads_.Insert(tgt);
51  return branch;
52}
53
54/*
55 * Compare two 64-bit values
56 *    x = y     return  0
57 *    x < y     return -1
58 *    x > y     return  1
59 */
60void X86Mir2Lir::GenCmpLong(RegLocation rl_dest, RegLocation rl_src1,
61                            RegLocation rl_src2) {
62  FlushAllRegs();
63  LockCallTemps();  // Prepare for explicit register usage
64  LoadValueDirectWideFixed(rl_src1, r0, r1);
65  LoadValueDirectWideFixed(rl_src2, r2, r3);
66  // Compute (r1:r0) = (r1:r0) - (r3:r2)
67  OpRegReg(kOpSub, r0, r2);  // r0 = r0 - r2
68  OpRegReg(kOpSbc, r1, r3);  // r1 = r1 - r3 - CF
69  NewLIR2(kX86Set8R, r2, kX86CondL);  // r2 = (r1:r0) < (r3:r2) ? 1 : 0
70  NewLIR2(kX86Movzx8RR, r2, r2);
71  OpReg(kOpNeg, r2);         // r2 = -r2
72  OpRegReg(kOpOr, r0, r1);   // r0 = high | low - sets ZF
73  NewLIR2(kX86Set8R, r0, kX86CondNz);  // r0 = (r1:r0) != (r3:r2) ? 1 : 0
74  NewLIR2(kX86Movzx8RR, r0, r0);
75  OpRegReg(kOpOr, r0, r2);   // r0 = r0 | r2
76  RegLocation rl_result = LocCReturn();
77  StoreValue(rl_dest, rl_result);
78}
79
80X86ConditionCode X86ConditionEncoding(ConditionCode cond) {
81  switch (cond) {
82    case kCondEq: return kX86CondEq;
83    case kCondNe: return kX86CondNe;
84    case kCondCs: return kX86CondC;
85    case kCondCc: return kX86CondNc;
86    case kCondUlt: return kX86CondC;
87    case kCondUge: return kX86CondNc;
88    case kCondMi: return kX86CondS;
89    case kCondPl: return kX86CondNs;
90    case kCondVs: return kX86CondO;
91    case kCondVc: return kX86CondNo;
92    case kCondHi: return kX86CondA;
93    case kCondLs: return kX86CondBe;
94    case kCondGe: return kX86CondGe;
95    case kCondLt: return kX86CondL;
96    case kCondGt: return kX86CondG;
97    case kCondLe: return kX86CondLe;
98    case kCondAl:
99    case kCondNv: LOG(FATAL) << "Should not reach here";
100  }
101  return kX86CondO;
102}
103
104LIR* X86Mir2Lir::OpCmpBranch(ConditionCode cond, int src1, int src2,
105                             LIR* target) {
106  NewLIR2(kX86Cmp32RR, src1, src2);
107  X86ConditionCode cc = X86ConditionEncoding(cond);
108  LIR* branch = NewLIR2(kX86Jcc8, 0 /* lir operand for Jcc offset */ ,
109                        cc);
110  branch->target = target;
111  return branch;
112}
113
114LIR* X86Mir2Lir::OpCmpImmBranch(ConditionCode cond, int reg,
115                                int check_value, LIR* target) {
116  if ((check_value == 0) && (cond == kCondEq || cond == kCondNe)) {
117    // TODO: when check_value == 0 and reg is rCX, use the jcxz/nz opcode
118    NewLIR2(kX86Test32RR, reg, reg);
119  } else {
120    NewLIR2(IS_SIMM8(check_value) ? kX86Cmp32RI8 : kX86Cmp32RI, reg, check_value);
121  }
122  X86ConditionCode cc = X86ConditionEncoding(cond);
123  LIR* branch = NewLIR2(kX86Jcc8, 0 /* lir operand for Jcc offset */ , cc);
124  branch->target = target;
125  return branch;
126}
127
128LIR* X86Mir2Lir::OpRegCopyNoInsert(int r_dest, int r_src) {
129  if (X86_FPREG(r_dest) || X86_FPREG(r_src))
130    return OpFpRegCopy(r_dest, r_src);
131  LIR* res = RawLIR(current_dalvik_offset_, kX86Mov32RR,
132                    r_dest, r_src);
133  if (r_dest == r_src) {
134    res->flags.is_nop = true;
135  }
136  return res;
137}
138
139LIR* X86Mir2Lir::OpRegCopy(int r_dest, int r_src) {
140  LIR *res = OpRegCopyNoInsert(r_dest, r_src);
141  AppendLIR(res);
142  return res;
143}
144
145void X86Mir2Lir::OpRegCopyWide(int dest_lo, int dest_hi,
146                               int src_lo, int src_hi) {
147  bool dest_fp = X86_FPREG(dest_lo) && X86_FPREG(dest_hi);
148  bool src_fp = X86_FPREG(src_lo) && X86_FPREG(src_hi);
149  assert(X86_FPREG(src_lo) == X86_FPREG(src_hi));
150  assert(X86_FPREG(dest_lo) == X86_FPREG(dest_hi));
151  if (dest_fp) {
152    if (src_fp) {
153      OpRegCopy(S2d(dest_lo, dest_hi), S2d(src_lo, src_hi));
154    } else {
155      // TODO: Prevent this from happening in the code. The result is often
156      // unused or could have been loaded more easily from memory.
157      NewLIR2(kX86MovdxrRR, dest_lo, src_lo);
158      NewLIR2(kX86MovdxrRR, dest_hi, src_hi);
159      NewLIR2(kX86PsllqRI, dest_hi, 32);
160      NewLIR2(kX86OrpsRR, dest_lo, dest_hi);
161    }
162  } else {
163    if (src_fp) {
164      NewLIR2(kX86MovdrxRR, dest_lo, src_lo);
165      NewLIR2(kX86PsrlqRI, src_lo, 32);
166      NewLIR2(kX86MovdrxRR, dest_hi, src_lo);
167    } else {
168      // Handle overlap
169      if (src_hi == dest_lo) {
170        OpRegCopy(dest_hi, src_hi);
171        OpRegCopy(dest_lo, src_lo);
172      } else {
173        OpRegCopy(dest_lo, src_lo);
174        OpRegCopy(dest_hi, src_hi);
175      }
176    }
177  }
178}
179
180void X86Mir2Lir::GenSelect(BasicBlock* bb, MIR* mir) {
181  UNIMPLEMENTED(FATAL) << "Need codegen for GenSelect";
182}
183
184void X86Mir2Lir::GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir) {
185  LIR* taken = &block_label_list_[bb->taken];
186  RegLocation rl_src1 = mir_graph_->GetSrcWide(mir, 0);
187  RegLocation rl_src2 = mir_graph_->GetSrcWide(mir, 2);
188  ConditionCode ccode = static_cast<ConditionCode>(mir->dalvikInsn.arg[0]);
189
190  if (rl_src1.is_const) {
191    std::swap(rl_src1, rl_src2);
192    ccode = FlipComparisonOrder(ccode);
193  }
194  if (rl_src2.is_const) {
195    // Do special compare/branch against simple const operand
196    int64_t val = mir_graph_->ConstantValueWide(rl_src2);
197    GenFusedLongCmpImmBranch(bb, rl_src1, val, ccode);
198    return;
199  }
200
201  FlushAllRegs();
202  LockCallTemps();  // Prepare for explicit register usage
203  LoadValueDirectWideFixed(rl_src1, r0, r1);
204  LoadValueDirectWideFixed(rl_src2, r2, r3);
205  // Swap operands and condition code to prevent use of zero flag.
206  if (ccode == kCondLe || ccode == kCondGt) {
207    // Compute (r3:r2) = (r3:r2) - (r1:r0)
208    OpRegReg(kOpSub, r2, r0);  // r2 = r2 - r0
209    OpRegReg(kOpSbc, r3, r1);  // r3 = r3 - r1 - CF
210  } else {
211    // Compute (r1:r0) = (r1:r0) - (r3:r2)
212    OpRegReg(kOpSub, r0, r2);  // r0 = r0 - r2
213    OpRegReg(kOpSbc, r1, r3);  // r1 = r1 - r3 - CF
214  }
215  switch (ccode) {
216    case kCondEq:
217    case kCondNe:
218      OpRegReg(kOpOr, r0, r1);  // r0 = r0 | r1
219      break;
220    case kCondLe:
221      ccode = kCondGe;
222      break;
223    case kCondGt:
224      ccode = kCondLt;
225      break;
226    case kCondLt:
227    case kCondGe:
228      break;
229    default:
230      LOG(FATAL) << "Unexpected ccode: " << ccode;
231  }
232  OpCondBranch(ccode, taken);
233}
234
235void X86Mir2Lir::GenFusedLongCmpImmBranch(BasicBlock* bb, RegLocation rl_src1,
236                                          int64_t val, ConditionCode ccode) {
237  int32_t val_lo = Low32Bits(val);
238  int32_t val_hi = High32Bits(val);
239  LIR* taken = &block_label_list_[bb->taken];
240  LIR* not_taken = &block_label_list_[bb->fall_through];
241  rl_src1 = LoadValueWide(rl_src1, kCoreReg);
242  int32_t low_reg = rl_src1.low_reg;
243  int32_t high_reg = rl_src1.high_reg;
244
245  if (val == 0 && (ccode == kCondEq || ccode == kCondNe)) {
246    int t_reg = AllocTemp();
247    OpRegRegReg(kOpOr, t_reg, low_reg, high_reg);
248    FreeTemp(t_reg);
249    OpCondBranch(ccode, taken);
250    return;
251  }
252
253  OpRegImm(kOpCmp, high_reg, val_hi);
254  switch (ccode) {
255    case kCondEq:
256    case kCondNe:
257      OpCondBranch(kCondNe, (ccode == kCondEq) ? not_taken : taken);
258      break;
259    case kCondLt:
260      OpCondBranch(kCondLt, taken);
261      OpCondBranch(kCondGt, not_taken);
262      ccode = kCondUlt;
263      break;
264    case kCondLe:
265      OpCondBranch(kCondLt, taken);
266      OpCondBranch(kCondGt, not_taken);
267      ccode = kCondLs;
268      break;
269    case kCondGt:
270      OpCondBranch(kCondGt, taken);
271      OpCondBranch(kCondLt, not_taken);
272      ccode = kCondHi;
273      break;
274    case kCondGe:
275      OpCondBranch(kCondGt, taken);
276      OpCondBranch(kCondLt, not_taken);
277      ccode = kCondUge;
278      break;
279    default:
280      LOG(FATAL) << "Unexpected ccode: " << ccode;
281  }
282  OpCmpImmBranch(ccode, low_reg, val_lo, taken);
283}
284
285RegLocation X86Mir2Lir::GenDivRemLit(RegLocation rl_dest, int reg_lo,
286                                     int lit, bool is_div) {
287  LOG(FATAL) << "Unexpected use of GenDivRemLit for x86";
288  return rl_dest;
289}
290
291RegLocation X86Mir2Lir::GenDivRem(RegLocation rl_dest, int reg_lo,
292                                  int reg_hi, bool is_div) {
293  LOG(FATAL) << "Unexpected use of GenDivRem for x86";
294  return rl_dest;
295}
296
297bool X86Mir2Lir::GenInlinedMinMaxInt(CallInfo* info, bool is_min) {
298  DCHECK_EQ(cu_->instruction_set, kX86);
299  RegLocation rl_src1 = info->args[0];
300  RegLocation rl_src2 = info->args[1];
301  rl_src1 = LoadValue(rl_src1, kCoreReg);
302  rl_src2 = LoadValue(rl_src2, kCoreReg);
303  RegLocation rl_dest = InlineTarget(info);
304  RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
305  OpRegReg(kOpCmp, rl_src1.low_reg, rl_src2.low_reg);
306  DCHECK_EQ(cu_->instruction_set, kX86);
307  LIR* branch = NewLIR2(kX86Jcc8, 0, is_min ? kX86CondG : kX86CondL);
308  OpRegReg(kOpMov, rl_result.low_reg, rl_src1.low_reg);
309  LIR* branch2 = NewLIR1(kX86Jmp8, 0);
310  branch->target = NewLIR0(kPseudoTargetLabel);
311  OpRegReg(kOpMov, rl_result.low_reg, rl_src2.low_reg);
312  branch2->target = NewLIR0(kPseudoTargetLabel);
313  StoreValue(rl_dest, rl_result);
314  return true;
315}
316
317bool X86Mir2Lir::GenInlinedPeek(CallInfo* info, OpSize size) {
318  RegLocation rl_src_address = info->args[0];  // long address
319  rl_src_address.wide = 0;  // ignore high half in info->args[1]
320  RegLocation rl_dest = InlineTarget(info);
321  RegLocation rl_address = LoadValue(rl_src_address, kCoreReg);
322  RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
323  if (size == kLong) {
324    // Unaligned access is allowed on x86.
325    LoadBaseDispWide(rl_address.low_reg, 0, rl_result.low_reg, rl_result.high_reg, INVALID_SREG);
326    StoreValueWide(rl_dest, rl_result);
327  } else {
328    DCHECK(size == kSignedByte || size == kSignedHalf || size == kWord);
329    // Unaligned access is allowed on x86.
330    LoadBaseDisp(rl_address.low_reg, 0, rl_result.low_reg, size, INVALID_SREG);
331    StoreValue(rl_dest, rl_result);
332  }
333  return true;
334}
335
336bool X86Mir2Lir::GenInlinedPoke(CallInfo* info, OpSize size) {
337  RegLocation rl_src_address = info->args[0];  // long address
338  rl_src_address.wide = 0;  // ignore high half in info->args[1]
339  RegLocation rl_src_value = info->args[2];  // [size] value
340  RegLocation rl_address = LoadValue(rl_src_address, kCoreReg);
341  if (size == kLong) {
342    // Unaligned access is allowed on x86.
343    RegLocation rl_value = LoadValueWide(rl_src_value, kCoreReg);
344    StoreBaseDispWide(rl_address.low_reg, 0, rl_value.low_reg, rl_value.high_reg);
345  } else {
346    DCHECK(size == kSignedByte || size == kSignedHalf || size == kWord);
347    // Unaligned access is allowed on x86.
348    RegLocation rl_value = LoadValue(rl_src_value, kCoreReg);
349    StoreBaseDisp(rl_address.low_reg, 0, rl_value.low_reg, size);
350  }
351  return true;
352}
353
354void X86Mir2Lir::OpLea(int rBase, int reg1, int reg2, int scale, int offset) {
355  NewLIR5(kX86Lea32RA, rBase, reg1, reg2, scale, offset);
356}
357
358void X86Mir2Lir::OpTlsCmp(ThreadOffset offset, int val) {
359  NewLIR2(kX86Cmp16TI8, offset.Int32Value(), val);
360}
361
362bool X86Mir2Lir::GenInlinedCas(CallInfo* info, bool is_long, bool is_object) {
363  DCHECK_EQ(cu_->instruction_set, kX86);
364  // Unused - RegLocation rl_src_unsafe = info->args[0];
365  RegLocation rl_src_obj = info->args[1];  // Object - known non-null
366  RegLocation rl_src_offset = info->args[2];  // long low
367  rl_src_offset.wide = 0;  // ignore high half in info->args[3]
368  RegLocation rl_src_expected = info->args[4];  // int, long or Object
369  // If is_long, high half is in info->args[5]
370  RegLocation rl_src_new_value = info->args[is_long ? 6 : 5];  // int, long or Object
371  // If is_long, high half is in info->args[7]
372
373  if (is_long) {
374    FlushAllRegs();
375    LockCallTemps();
376    LoadValueDirectWideFixed(rl_src_expected, rAX, rDX);
377    LoadValueDirectWideFixed(rl_src_new_value, rBX, rCX);
378    NewLIR1(kX86Push32R, rDI);
379    MarkTemp(rDI);
380    LockTemp(rDI);
381    NewLIR1(kX86Push32R, rSI);
382    MarkTemp(rSI);
383    LockTemp(rSI);
384    const int push_offset = 4 /* push edi */ + 4 /* push esi */;
385    LoadWordDisp(TargetReg(kSp), SRegOffset(rl_src_obj.s_reg_low) + push_offset, rDI);
386    LoadWordDisp(TargetReg(kSp), SRegOffset(rl_src_offset.s_reg_low) + push_offset, rSI);
387    NewLIR4(kX86LockCmpxchg8bA, rDI, rSI, 0, 0);
388    FreeTemp(rSI);
389    UnmarkTemp(rSI);
390    NewLIR1(kX86Pop32R, rSI);
391    FreeTemp(rDI);
392    UnmarkTemp(rDI);
393    NewLIR1(kX86Pop32R, rDI);
394    FreeCallTemps();
395  } else {
396    // EAX must hold expected for CMPXCHG. Neither rl_new_value, nor r_ptr may be in EAX.
397    FlushReg(r0);
398    LockTemp(r0);
399
400    // Release store semantics, get the barrier out of the way.  TODO: revisit
401    GenMemBarrier(kStoreLoad);
402
403    RegLocation rl_object = LoadValue(rl_src_obj, kCoreReg);
404    RegLocation rl_new_value = LoadValue(rl_src_new_value, kCoreReg);
405
406    if (is_object && !mir_graph_->IsConstantNullRef(rl_new_value)) {
407      // Mark card for object assuming new value is stored.
408      FreeTemp(r0);  // Temporarily release EAX for MarkGCCard().
409      MarkGCCard(rl_new_value.low_reg, rl_object.low_reg);
410      LockTemp(r0);
411    }
412
413    RegLocation rl_offset = LoadValue(rl_src_offset, kCoreReg);
414    LoadValueDirect(rl_src_expected, r0);
415    NewLIR5(kX86LockCmpxchgAR, rl_object.low_reg, rl_offset.low_reg, 0, 0, rl_new_value.low_reg);
416
417    FreeTemp(r0);
418  }
419
420  // Convert ZF to boolean
421  RegLocation rl_dest = InlineTarget(info);  // boolean place for result
422  RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
423  NewLIR2(kX86Set8R, rl_result.low_reg, kX86CondZ);
424  NewLIR2(kX86Movzx8RR, rl_result.low_reg, rl_result.low_reg);
425  StoreValue(rl_dest, rl_result);
426  return true;
427}
428
429LIR* X86Mir2Lir::OpPcRelLoad(int reg, LIR* target) {
430  LOG(FATAL) << "Unexpected use of OpPcRelLoad for x86";
431  return NULL;
432}
433
434LIR* X86Mir2Lir::OpVldm(int rBase, int count) {
435  LOG(FATAL) << "Unexpected use of OpVldm for x86";
436  return NULL;
437}
438
439LIR* X86Mir2Lir::OpVstm(int rBase, int count) {
440  LOG(FATAL) << "Unexpected use of OpVstm for x86";
441  return NULL;
442}
443
444void X86Mir2Lir::GenMultiplyByTwoBitMultiplier(RegLocation rl_src,
445                                               RegLocation rl_result, int lit,
446                                               int first_bit, int second_bit) {
447  int t_reg = AllocTemp();
448  OpRegRegImm(kOpLsl, t_reg, rl_src.low_reg, second_bit - first_bit);
449  OpRegRegReg(kOpAdd, rl_result.low_reg, rl_src.low_reg, t_reg);
450  FreeTemp(t_reg);
451  if (first_bit != 0) {
452    OpRegRegImm(kOpLsl, rl_result.low_reg, rl_result.low_reg, first_bit);
453  }
454}
455
456void X86Mir2Lir::GenDivZeroCheck(int reg_lo, int reg_hi) {
457  int t_reg = AllocTemp();
458  OpRegRegReg(kOpOr, t_reg, reg_lo, reg_hi);
459  GenImmedCheck(kCondEq, t_reg, 0, kThrowDivZero);
460  FreeTemp(t_reg);
461}
462
463// Test suspend flag, return target of taken suspend branch
464LIR* X86Mir2Lir::OpTestSuspend(LIR* target) {
465  OpTlsCmp(Thread::ThreadFlagsOffset(), 0);
466  return OpCondBranch((target == NULL) ? kCondNe : kCondEq, target);
467}
468
469// Decrement register and branch on condition
470LIR* X86Mir2Lir::OpDecAndBranch(ConditionCode c_code, int reg, LIR* target) {
471  OpRegImm(kOpSub, reg, 1);
472  return OpCmpImmBranch(c_code, reg, 0, target);
473}
474
475bool X86Mir2Lir::SmallLiteralDivRem(Instruction::Code dalvik_opcode, bool is_div,
476                                    RegLocation rl_src, RegLocation rl_dest, int lit) {
477  LOG(FATAL) << "Unexpected use of smallLiteralDive in x86";
478  return false;
479}
480
481LIR* X86Mir2Lir::OpIT(ConditionCode cond, const char* guide) {
482  LOG(FATAL) << "Unexpected use of OpIT in x86";
483  return NULL;
484}
485
486void X86Mir2Lir::GenMulLong(RegLocation rl_dest, RegLocation rl_src1,
487                            RegLocation rl_src2) {
488  LOG(FATAL) << "Unexpected use of GenX86Long for x86";
489}
490void X86Mir2Lir::GenAddLong(RegLocation rl_dest, RegLocation rl_src1,
491                         RegLocation rl_src2) {
492  // TODO: fixed register usage here as we only have 4 temps and temporary allocation isn't smart
493  // enough.
494  FlushAllRegs();
495  LockCallTemps();  // Prepare for explicit register usage
496  LoadValueDirectWideFixed(rl_src1, r0, r1);
497  LoadValueDirectWideFixed(rl_src2, r2, r3);
498  // Compute (r1:r0) = (r1:r0) + (r2:r3)
499  OpRegReg(kOpAdd, r0, r2);  // r0 = r0 + r2
500  OpRegReg(kOpAdc, r1, r3);  // r1 = r1 + r3 + CF
501  RegLocation rl_result = {kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1, r0, r1,
502                          INVALID_SREG, INVALID_SREG};
503  StoreValueWide(rl_dest, rl_result);
504}
505
506void X86Mir2Lir::GenSubLong(RegLocation rl_dest, RegLocation rl_src1,
507                            RegLocation rl_src2) {
508  // TODO: fixed register usage here as we only have 4 temps and temporary allocation isn't smart
509  // enough.
510  FlushAllRegs();
511  LockCallTemps();  // Prepare for explicit register usage
512  LoadValueDirectWideFixed(rl_src1, r0, r1);
513  LoadValueDirectWideFixed(rl_src2, r2, r3);
514  // Compute (r1:r0) = (r1:r0) + (r2:r3)
515  OpRegReg(kOpSub, r0, r2);  // r0 = r0 - r2
516  OpRegReg(kOpSbc, r1, r3);  // r1 = r1 - r3 - CF
517  RegLocation rl_result = {kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1, r0, r1,
518                          INVALID_SREG, INVALID_SREG};
519  StoreValueWide(rl_dest, rl_result);
520}
521
522void X86Mir2Lir::GenAndLong(RegLocation rl_dest, RegLocation rl_src1,
523                            RegLocation rl_src2) {
524  // TODO: fixed register usage here as we only have 4 temps and temporary allocation isn't smart
525  // enough.
526  FlushAllRegs();
527  LockCallTemps();  // Prepare for explicit register usage
528  LoadValueDirectWideFixed(rl_src1, r0, r1);
529  LoadValueDirectWideFixed(rl_src2, r2, r3);
530  // Compute (r1:r0) = (r1:r0) & (r2:r3)
531  OpRegReg(kOpAnd, r0, r2);  // r0 = r0 & r2
532  OpRegReg(kOpAnd, r1, r3);  // r1 = r1 & r3
533  RegLocation rl_result = {kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1, r0, r1,
534                          INVALID_SREG, INVALID_SREG};
535  StoreValueWide(rl_dest, rl_result);
536}
537
538void X86Mir2Lir::GenOrLong(RegLocation rl_dest,
539                           RegLocation rl_src1, RegLocation rl_src2) {
540  // TODO: fixed register usage here as we only have 4 temps and temporary allocation isn't smart
541  // enough.
542  FlushAllRegs();
543  LockCallTemps();  // Prepare for explicit register usage
544  LoadValueDirectWideFixed(rl_src1, r0, r1);
545  LoadValueDirectWideFixed(rl_src2, r2, r3);
546  // Compute (r1:r0) = (r1:r0) | (r2:r3)
547  OpRegReg(kOpOr, r0, r2);  // r0 = r0 | r2
548  OpRegReg(kOpOr, r1, r3);  // r1 = r1 | r3
549  RegLocation rl_result = {kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1, r0, r1,
550                          INVALID_SREG, INVALID_SREG};
551  StoreValueWide(rl_dest, rl_result);
552}
553
554void X86Mir2Lir::GenXorLong(RegLocation rl_dest,
555                            RegLocation rl_src1, RegLocation rl_src2) {
556  // TODO: fixed register usage here as we only have 4 temps and temporary allocation isn't smart
557  // enough.
558  FlushAllRegs();
559  LockCallTemps();  // Prepare for explicit register usage
560  LoadValueDirectWideFixed(rl_src1, r0, r1);
561  LoadValueDirectWideFixed(rl_src2, r2, r3);
562  // Compute (r1:r0) = (r1:r0) ^ (r2:r3)
563  OpRegReg(kOpXor, r0, r2);  // r0 = r0 ^ r2
564  OpRegReg(kOpXor, r1, r3);  // r1 = r1 ^ r3
565  RegLocation rl_result = {kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1, r0, r1,
566                          INVALID_SREG, INVALID_SREG};
567  StoreValueWide(rl_dest, rl_result);
568}
569
570void X86Mir2Lir::GenNegLong(RegLocation rl_dest, RegLocation rl_src) {
571  FlushAllRegs();
572  LockCallTemps();  // Prepare for explicit register usage
573  LoadValueDirectWideFixed(rl_src, r0, r1);
574  // Compute (r1:r0) = -(r1:r0)
575  OpRegReg(kOpNeg, r0, r0);  // r0 = -r0
576  OpRegImm(kOpAdc, r1, 0);   // r1 = r1 + CF
577  OpRegReg(kOpNeg, r1, r1);  // r1 = -r1
578  RegLocation rl_result = {kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1, r0, r1,
579                          INVALID_SREG, INVALID_SREG};
580  StoreValueWide(rl_dest, rl_result);
581}
582
583void X86Mir2Lir::OpRegThreadMem(OpKind op, int r_dest, ThreadOffset thread_offset) {
584  X86OpCode opcode = kX86Bkpt;
585  switch (op) {
586  case kOpCmp: opcode = kX86Cmp32RT;  break;
587  case kOpMov: opcode = kX86Mov32RT;  break;
588  default:
589    LOG(FATAL) << "Bad opcode: " << op;
590    break;
591  }
592  NewLIR2(opcode, r_dest, thread_offset.Int32Value());
593}
594
595/*
596 * Generate array load
597 */
598void X86Mir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array,
599                             RegLocation rl_index, RegLocation rl_dest, int scale) {
600  RegisterClass reg_class = oat_reg_class_by_size(size);
601  int len_offset = mirror::Array::LengthOffset().Int32Value();
602  RegLocation rl_result;
603  rl_array = LoadValue(rl_array, kCoreReg);
604
605  int data_offset;
606  if (size == kLong || size == kDouble) {
607    data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Int32Value();
608  } else {
609    data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Int32Value();
610  }
611
612  bool constant_index = rl_index.is_const;
613  int32_t constant_index_value = 0;
614  if (!constant_index) {
615    rl_index = LoadValue(rl_index, kCoreReg);
616  } else {
617    constant_index_value = mir_graph_->ConstantValue(rl_index);
618    // If index is constant, just fold it into the data offset
619    data_offset += constant_index_value << scale;
620    // treat as non array below
621    rl_index.low_reg = INVALID_REG;
622  }
623
624  /* null object? */
625  GenNullCheck(rl_array.s_reg_low, rl_array.low_reg, opt_flags);
626
627  if (!(opt_flags & MIR_IGNORE_RANGE_CHECK)) {
628    if (constant_index) {
629      GenMemImmedCheck(kCondLs, rl_array.low_reg, len_offset,
630                       constant_index_value, kThrowConstantArrayBounds);
631    } else {
632      GenRegMemCheck(kCondUge, rl_index.low_reg, rl_array.low_reg,
633                     len_offset, kThrowArrayBounds);
634    }
635  }
636  rl_result = EvalLoc(rl_dest, reg_class, true);
637  if ((size == kLong) || (size == kDouble)) {
638    LoadBaseIndexedDisp(rl_array.low_reg, rl_index.low_reg, scale, data_offset, rl_result.low_reg,
639                        rl_result.high_reg, size, INVALID_SREG);
640    StoreValueWide(rl_dest, rl_result);
641  } else {
642    LoadBaseIndexedDisp(rl_array.low_reg, rl_index.low_reg, scale,
643                        data_offset, rl_result.low_reg, INVALID_REG, size,
644                        INVALID_SREG);
645    StoreValue(rl_dest, rl_result);
646  }
647}
648
649/*
650 * Generate array store
651 *
652 */
653void X86Mir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array,
654                             RegLocation rl_index, RegLocation rl_src, int scale, bool card_mark) {
655  RegisterClass reg_class = oat_reg_class_by_size(size);
656  int len_offset = mirror::Array::LengthOffset().Int32Value();
657  int data_offset;
658
659  if (size == kLong || size == kDouble) {
660    data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Int32Value();
661  } else {
662    data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Int32Value();
663  }
664
665  rl_array = LoadValue(rl_array, kCoreReg);
666  bool constant_index = rl_index.is_const;
667  int32_t constant_index_value = 0;
668  if (!constant_index) {
669    rl_index = LoadValue(rl_index, kCoreReg);
670  } else {
671    // If index is constant, just fold it into the data offset
672    constant_index_value = mir_graph_->ConstantValue(rl_index);
673    data_offset += constant_index_value << scale;
674    // treat as non array below
675    rl_index.low_reg = INVALID_REG;
676  }
677
678  /* null object? */
679  GenNullCheck(rl_array.s_reg_low, rl_array.low_reg, opt_flags);
680
681  if (!(opt_flags & MIR_IGNORE_RANGE_CHECK)) {
682    if (constant_index) {
683      GenMemImmedCheck(kCondLs, rl_array.low_reg, len_offset,
684                       constant_index_value, kThrowConstantArrayBounds);
685    } else {
686      GenRegMemCheck(kCondUge, rl_index.low_reg, rl_array.low_reg,
687                     len_offset, kThrowArrayBounds);
688    }
689  }
690  if ((size == kLong) || (size == kDouble)) {
691    rl_src = LoadValueWide(rl_src, reg_class);
692  } else {
693    rl_src = LoadValue(rl_src, reg_class);
694  }
695  // If the src reg can't be byte accessed, move it to a temp first.
696  if ((size == kSignedByte || size == kUnsignedByte) && rl_src.low_reg >= 4) {
697    int temp = AllocTemp();
698    OpRegCopy(temp, rl_src.low_reg);
699    StoreBaseIndexedDisp(rl_array.low_reg, rl_index.low_reg, scale, data_offset, temp,
700                         INVALID_REG, size, INVALID_SREG);
701  } else {
702    StoreBaseIndexedDisp(rl_array.low_reg, rl_index.low_reg, scale, data_offset, rl_src.low_reg,
703                         rl_src.high_reg, size, INVALID_SREG);
704  }
705  if (card_mark) {
706    // Free rl_index if its a temp. Ensures there are 2 free regs for card mark.
707    if (!constant_index) {
708      FreeTemp(rl_index.low_reg);
709    }
710    MarkGCCard(rl_src.low_reg, rl_array.low_reg);
711  }
712}
713
714void X86Mir2Lir::GenShiftImmOpLong(Instruction::Code opcode, RegLocation rl_dest,
715                                   RegLocation rl_src1, RegLocation rl_shift) {
716  // Default implementation is just to ignore the constant case.
717  GenShiftOpLong(opcode, rl_dest, rl_src1, rl_shift);
718}
719
720void X86Mir2Lir::GenArithImmOpLong(Instruction::Code opcode,
721                                   RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) {
722  // Default - bail to non-const handler.
723  GenArithOpLong(opcode, rl_dest, rl_src1, rl_src2);
724}
725
726}  // namespace art
727