int_x86.cc revision a0dac3e82231654be01be1e31a62dd40ea4a03a6
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 (!(cu_->disable_opt & (1 << kSafeOptimizations)) && 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      dest_hi = AllocTempDouble();
159      NewLIR2(kX86MovdxrRR, dest_hi, src_hi);
160      NewLIR2(kX86PsllqRI, dest_hi, 32);
161      NewLIR2(kX86OrpsRR, dest_lo, dest_hi);
162      FreeTemp(dest_hi);
163    }
164  } else {
165    if (src_fp) {
166      NewLIR2(kX86MovdrxRR, dest_lo, src_lo);
167      NewLIR2(kX86PsrlqRI, src_lo, 32);
168      NewLIR2(kX86MovdrxRR, dest_hi, src_lo);
169    } else {
170      // Handle overlap
171      if (src_hi == dest_lo) {
172        OpRegCopy(dest_hi, src_hi);
173        OpRegCopy(dest_lo, src_lo);
174      } else {
175        OpRegCopy(dest_lo, src_lo);
176        OpRegCopy(dest_hi, src_hi);
177      }
178    }
179  }
180}
181
182void X86Mir2Lir::GenSelect(BasicBlock* bb, MIR* mir) {
183  UNIMPLEMENTED(FATAL) << "Need codegen for GenSelect";
184}
185
186void X86Mir2Lir::GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir) {
187  LIR* taken = &block_label_list_[bb->taken];
188  RegLocation rl_src1 = mir_graph_->GetSrcWide(mir, 0);
189  RegLocation rl_src2 = mir_graph_->GetSrcWide(mir, 2);
190  ConditionCode ccode = static_cast<ConditionCode>(mir->dalvikInsn.arg[0]);
191
192  if (rl_src1.is_const) {
193    std::swap(rl_src1, rl_src2);
194    ccode = FlipComparisonOrder(ccode);
195  }
196  if (rl_src2.is_const) {
197    // Do special compare/branch against simple const operand
198    int64_t val = mir_graph_->ConstantValueWide(rl_src2);
199    GenFusedLongCmpImmBranch(bb, rl_src1, val, ccode);
200    return;
201  }
202
203  FlushAllRegs();
204  LockCallTemps();  // Prepare for explicit register usage
205  LoadValueDirectWideFixed(rl_src1, r0, r1);
206  LoadValueDirectWideFixed(rl_src2, r2, r3);
207  // Swap operands and condition code to prevent use of zero flag.
208  if (ccode == kCondLe || ccode == kCondGt) {
209    // Compute (r3:r2) = (r3:r2) - (r1:r0)
210    OpRegReg(kOpSub, r2, r0);  // r2 = r2 - r0
211    OpRegReg(kOpSbc, r3, r1);  // r3 = r3 - r1 - CF
212  } else {
213    // Compute (r1:r0) = (r1:r0) - (r3:r2)
214    OpRegReg(kOpSub, r0, r2);  // r0 = r0 - r2
215    OpRegReg(kOpSbc, r1, r3);  // r1 = r1 - r3 - CF
216  }
217  switch (ccode) {
218    case kCondEq:
219    case kCondNe:
220      OpRegReg(kOpOr, r0, r1);  // r0 = r0 | r1
221      break;
222    case kCondLe:
223      ccode = kCondGe;
224      break;
225    case kCondGt:
226      ccode = kCondLt;
227      break;
228    case kCondLt:
229    case kCondGe:
230      break;
231    default:
232      LOG(FATAL) << "Unexpected ccode: " << ccode;
233  }
234  OpCondBranch(ccode, taken);
235}
236
237void X86Mir2Lir::GenFusedLongCmpImmBranch(BasicBlock* bb, RegLocation rl_src1,
238                                          int64_t val, ConditionCode ccode) {
239  int32_t val_lo = Low32Bits(val);
240  int32_t val_hi = High32Bits(val);
241  LIR* taken = &block_label_list_[bb->taken];
242  LIR* not_taken = &block_label_list_[bb->fall_through];
243  rl_src1 = LoadValueWide(rl_src1, kCoreReg);
244  int32_t low_reg = rl_src1.low_reg;
245  int32_t high_reg = rl_src1.high_reg;
246
247  if (val == 0 && (ccode == kCondEq || ccode == kCondNe)) {
248    int t_reg = AllocTemp();
249    OpRegRegReg(kOpOr, t_reg, low_reg, high_reg);
250    FreeTemp(t_reg);
251    OpCondBranch(ccode, taken);
252    return;
253  }
254
255  OpRegImm(kOpCmp, high_reg, val_hi);
256  switch (ccode) {
257    case kCondEq:
258    case kCondNe:
259      OpCondBranch(kCondNe, (ccode == kCondEq) ? not_taken : taken);
260      break;
261    case kCondLt:
262      OpCondBranch(kCondLt, taken);
263      OpCondBranch(kCondGt, not_taken);
264      ccode = kCondUlt;
265      break;
266    case kCondLe:
267      OpCondBranch(kCondLt, taken);
268      OpCondBranch(kCondGt, not_taken);
269      ccode = kCondLs;
270      break;
271    case kCondGt:
272      OpCondBranch(kCondGt, taken);
273      OpCondBranch(kCondLt, not_taken);
274      ccode = kCondHi;
275      break;
276    case kCondGe:
277      OpCondBranch(kCondGt, taken);
278      OpCondBranch(kCondLt, not_taken);
279      ccode = kCondUge;
280      break;
281    default:
282      LOG(FATAL) << "Unexpected ccode: " << ccode;
283  }
284  OpCmpImmBranch(ccode, low_reg, val_lo, taken);
285}
286
287RegLocation X86Mir2Lir::GenDivRemLit(RegLocation rl_dest, int reg_lo,
288                                     int lit, bool is_div) {
289  LOG(FATAL) << "Unexpected use of GenDivRemLit for x86";
290  return rl_dest;
291}
292
293RegLocation X86Mir2Lir::GenDivRem(RegLocation rl_dest, int reg_lo,
294                                  int reg_hi, bool is_div) {
295  LOG(FATAL) << "Unexpected use of GenDivRem for x86";
296  return rl_dest;
297}
298
299bool X86Mir2Lir::GenInlinedMinMaxInt(CallInfo* info, bool is_min) {
300  DCHECK_EQ(cu_->instruction_set, kX86);
301
302  // Get the two arguments to the invoke and place them in GP registers.
303  RegLocation rl_src1 = info->args[0];
304  RegLocation rl_src2 = info->args[1];
305  rl_src1 = LoadValue(rl_src1, kCoreReg);
306  rl_src2 = LoadValue(rl_src2, kCoreReg);
307
308  RegLocation rl_dest = InlineTarget(info);
309  RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
310
311  /*
312   * If the result register is the same as the second element, then we need to be careful.
313   * The reason is that the first copy will inadvertently clobber the second element with
314   * the first one thus yielding the wrong result. Thus we do a swap in that case.
315   */
316  if (rl_result.low_reg == rl_src2.low_reg) {
317    std::swap(rl_src1, rl_src2);
318  }
319
320  // Pick the first integer as min/max.
321  OpRegCopy(rl_result.low_reg, rl_src1.low_reg);
322
323  // If the integers are both in the same register, then there is nothing else to do
324  // because they are equal and we have already moved one into the result.
325  if (rl_src1.low_reg != rl_src2.low_reg) {
326    // It is possible we didn't pick correctly so do the actual comparison now.
327    OpRegReg(kOpCmp, rl_src1.low_reg, rl_src2.low_reg);
328
329    // Conditionally move the other integer into the destination register.
330    ConditionCode condition_code = is_min ? kCondGt : kCondLt;
331    OpCondRegReg(kOpCmov, condition_code, rl_result.low_reg, rl_src2.low_reg);
332  }
333
334  StoreValue(rl_dest, rl_result);
335  return true;
336}
337
338bool X86Mir2Lir::GenInlinedPeek(CallInfo* info, OpSize size) {
339  RegLocation rl_src_address = info->args[0];  // long address
340  rl_src_address.wide = 0;  // ignore high half in info->args[1]
341  RegLocation rl_dest = InlineTarget(info);
342  RegLocation rl_address = LoadValue(rl_src_address, kCoreReg);
343  RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
344  if (size == kLong) {
345    // Unaligned access is allowed on x86.
346    LoadBaseDispWide(rl_address.low_reg, 0, rl_result.low_reg, rl_result.high_reg, INVALID_SREG);
347    StoreValueWide(rl_dest, rl_result);
348  } else {
349    DCHECK(size == kSignedByte || size == kSignedHalf || size == kWord);
350    // Unaligned access is allowed on x86.
351    LoadBaseDisp(rl_address.low_reg, 0, rl_result.low_reg, size, INVALID_SREG);
352    StoreValue(rl_dest, rl_result);
353  }
354  return true;
355}
356
357bool X86Mir2Lir::GenInlinedPoke(CallInfo* info, OpSize size) {
358  RegLocation rl_src_address = info->args[0];  // long address
359  rl_src_address.wide = 0;  // ignore high half in info->args[1]
360  RegLocation rl_src_value = info->args[2];  // [size] value
361  RegLocation rl_address = LoadValue(rl_src_address, kCoreReg);
362  if (size == kLong) {
363    // Unaligned access is allowed on x86.
364    RegLocation rl_value = LoadValueWide(rl_src_value, kCoreReg);
365    StoreBaseDispWide(rl_address.low_reg, 0, rl_value.low_reg, rl_value.high_reg);
366  } else {
367    DCHECK(size == kSignedByte || size == kSignedHalf || size == kWord);
368    // Unaligned access is allowed on x86.
369    RegLocation rl_value = LoadValue(rl_src_value, kCoreReg);
370    StoreBaseDisp(rl_address.low_reg, 0, rl_value.low_reg, size);
371  }
372  return true;
373}
374
375void X86Mir2Lir::OpLea(int rBase, int reg1, int reg2, int scale, int offset) {
376  NewLIR5(kX86Lea32RA, rBase, reg1, reg2, scale, offset);
377}
378
379void X86Mir2Lir::OpTlsCmp(ThreadOffset offset, int val) {
380  NewLIR2(kX86Cmp16TI8, offset.Int32Value(), val);
381}
382
383bool X86Mir2Lir::GenInlinedCas(CallInfo* info, bool is_long, bool is_object) {
384  DCHECK_EQ(cu_->instruction_set, kX86);
385  // Unused - RegLocation rl_src_unsafe = info->args[0];
386  RegLocation rl_src_obj = info->args[1];  // Object - known non-null
387  RegLocation rl_src_offset = info->args[2];  // long low
388  rl_src_offset.wide = 0;  // ignore high half in info->args[3]
389  RegLocation rl_src_expected = info->args[4];  // int, long or Object
390  // If is_long, high half is in info->args[5]
391  RegLocation rl_src_new_value = info->args[is_long ? 6 : 5];  // int, long or Object
392  // If is_long, high half is in info->args[7]
393
394  if (is_long) {
395    FlushAllRegs();
396    LockCallTemps();
397    LoadValueDirectWideFixed(rl_src_expected, rAX, rDX);
398    LoadValueDirectWideFixed(rl_src_new_value, rBX, rCX);
399    NewLIR1(kX86Push32R, rDI);
400    MarkTemp(rDI);
401    LockTemp(rDI);
402    NewLIR1(kX86Push32R, rSI);
403    MarkTemp(rSI);
404    LockTemp(rSI);
405    const int push_offset = 4 /* push edi */ + 4 /* push esi */;
406    LoadWordDisp(TargetReg(kSp), SRegOffset(rl_src_obj.s_reg_low) + push_offset, rDI);
407    LoadWordDisp(TargetReg(kSp), SRegOffset(rl_src_offset.s_reg_low) + push_offset, rSI);
408    NewLIR4(kX86LockCmpxchg8bA, rDI, rSI, 0, 0);
409    FreeTemp(rSI);
410    UnmarkTemp(rSI);
411    NewLIR1(kX86Pop32R, rSI);
412    FreeTemp(rDI);
413    UnmarkTemp(rDI);
414    NewLIR1(kX86Pop32R, rDI);
415    FreeCallTemps();
416  } else {
417    // EAX must hold expected for CMPXCHG. Neither rl_new_value, nor r_ptr may be in EAX.
418    FlushReg(r0);
419    LockTemp(r0);
420
421    // Release store semantics, get the barrier out of the way.  TODO: revisit
422    GenMemBarrier(kStoreLoad);
423
424    RegLocation rl_object = LoadValue(rl_src_obj, kCoreReg);
425    RegLocation rl_new_value = LoadValue(rl_src_new_value, kCoreReg);
426
427    if (is_object && !mir_graph_->IsConstantNullRef(rl_new_value)) {
428      // Mark card for object assuming new value is stored.
429      FreeTemp(r0);  // Temporarily release EAX for MarkGCCard().
430      MarkGCCard(rl_new_value.low_reg, rl_object.low_reg);
431      LockTemp(r0);
432    }
433
434    RegLocation rl_offset = LoadValue(rl_src_offset, kCoreReg);
435    LoadValueDirect(rl_src_expected, r0);
436    NewLIR5(kX86LockCmpxchgAR, rl_object.low_reg, rl_offset.low_reg, 0, 0, rl_new_value.low_reg);
437
438    FreeTemp(r0);
439  }
440
441  // Convert ZF to boolean
442  RegLocation rl_dest = InlineTarget(info);  // boolean place for result
443  RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
444  NewLIR2(kX86Set8R, rl_result.low_reg, kX86CondZ);
445  NewLIR2(kX86Movzx8RR, rl_result.low_reg, rl_result.low_reg);
446  StoreValue(rl_dest, rl_result);
447  return true;
448}
449
450LIR* X86Mir2Lir::OpPcRelLoad(int reg, LIR* target) {
451  LOG(FATAL) << "Unexpected use of OpPcRelLoad for x86";
452  return NULL;
453}
454
455LIR* X86Mir2Lir::OpVldm(int rBase, int count) {
456  LOG(FATAL) << "Unexpected use of OpVldm for x86";
457  return NULL;
458}
459
460LIR* X86Mir2Lir::OpVstm(int rBase, int count) {
461  LOG(FATAL) << "Unexpected use of OpVstm for x86";
462  return NULL;
463}
464
465void X86Mir2Lir::GenMultiplyByTwoBitMultiplier(RegLocation rl_src,
466                                               RegLocation rl_result, int lit,
467                                               int first_bit, int second_bit) {
468  int t_reg = AllocTemp();
469  OpRegRegImm(kOpLsl, t_reg, rl_src.low_reg, second_bit - first_bit);
470  OpRegRegReg(kOpAdd, rl_result.low_reg, rl_src.low_reg, t_reg);
471  FreeTemp(t_reg);
472  if (first_bit != 0) {
473    OpRegRegImm(kOpLsl, rl_result.low_reg, rl_result.low_reg, first_bit);
474  }
475}
476
477void X86Mir2Lir::GenDivZeroCheck(int reg_lo, int reg_hi) {
478  // We are not supposed to clobber either of the provided registers, so allocate
479  // a temporary to use for the check.
480  int t_reg = AllocTemp();
481
482  // Doing an OR is a quick way to check if both registers are zero. This will set the flags.
483  OpRegRegReg(kOpOr, t_reg, reg_lo, reg_hi);
484
485  // In case of zero, throw ArithmeticException.
486  GenCheck(kCondEq, kThrowDivZero);
487
488  // The temp is no longer needed so free it at this time.
489  FreeTemp(t_reg);
490}
491
492// Test suspend flag, return target of taken suspend branch
493LIR* X86Mir2Lir::OpTestSuspend(LIR* target) {
494  OpTlsCmp(Thread::ThreadFlagsOffset(), 0);
495  return OpCondBranch((target == NULL) ? kCondNe : kCondEq, target);
496}
497
498// Decrement register and branch on condition
499LIR* X86Mir2Lir::OpDecAndBranch(ConditionCode c_code, int reg, LIR* target) {
500  OpRegImm(kOpSub, reg, 1);
501  return OpCondBranch(c_code, target);
502}
503
504bool X86Mir2Lir::SmallLiteralDivRem(Instruction::Code dalvik_opcode, bool is_div,
505                                    RegLocation rl_src, RegLocation rl_dest, int lit) {
506  LOG(FATAL) << "Unexpected use of smallLiteralDive in x86";
507  return false;
508}
509
510LIR* X86Mir2Lir::OpIT(ConditionCode cond, const char* guide) {
511  LOG(FATAL) << "Unexpected use of OpIT in x86";
512  return NULL;
513}
514
515void X86Mir2Lir::GenMulLong(RegLocation rl_dest, RegLocation rl_src1,
516                            RegLocation rl_src2) {
517  LOG(FATAL) << "Unexpected use of GenX86Long for x86";
518}
519void X86Mir2Lir::GenAddLong(RegLocation rl_dest, RegLocation rl_src1,
520                         RegLocation rl_src2) {
521  // TODO: fixed register usage here as we only have 4 temps and temporary allocation isn't smart
522  // enough.
523  FlushAllRegs();
524  LockCallTemps();  // Prepare for explicit register usage
525  LoadValueDirectWideFixed(rl_src1, r0, r1);
526  LoadValueDirectWideFixed(rl_src2, r2, r3);
527  // Compute (r1:r0) = (r1:r0) + (r2:r3)
528  OpRegReg(kOpAdd, r0, r2);  // r0 = r0 + r2
529  OpRegReg(kOpAdc, r1, r3);  // r1 = r1 + r3 + CF
530  RegLocation rl_result = {kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1, kVectorNotUsed, r0, r1,
531                          INVALID_SREG, INVALID_SREG};
532  StoreValueWide(rl_dest, rl_result);
533}
534
535void X86Mir2Lir::GenSubLong(RegLocation rl_dest, RegLocation rl_src1,
536                            RegLocation rl_src2) {
537  // TODO: fixed register usage here as we only have 4 temps and temporary allocation isn't smart
538  // enough.
539  FlushAllRegs();
540  LockCallTemps();  // Prepare for explicit register usage
541  LoadValueDirectWideFixed(rl_src1, r0, r1);
542  LoadValueDirectWideFixed(rl_src2, r2, r3);
543  // Compute (r1:r0) = (r1:r0) + (r2:r3)
544  OpRegReg(kOpSub, r0, r2);  // r0 = r0 - r2
545  OpRegReg(kOpSbc, r1, r3);  // r1 = r1 - r3 - CF
546  RegLocation rl_result = {kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1, kVectorNotUsed, r0, r1,
547                          INVALID_SREG, INVALID_SREG};
548  StoreValueWide(rl_dest, rl_result);
549}
550
551void X86Mir2Lir::GenAndLong(RegLocation rl_dest, RegLocation rl_src1,
552                            RegLocation rl_src2) {
553  // TODO: fixed register usage here as we only have 4 temps and temporary allocation isn't smart
554  // enough.
555  FlushAllRegs();
556  LockCallTemps();  // Prepare for explicit register usage
557  LoadValueDirectWideFixed(rl_src1, r0, r1);
558  LoadValueDirectWideFixed(rl_src2, r2, r3);
559  // Compute (r1:r0) = (r1:r0) & (r2:r3)
560  OpRegReg(kOpAnd, r0, r2);  // r0 = r0 & r2
561  OpRegReg(kOpAnd, r1, r3);  // r1 = r1 & r3
562  RegLocation rl_result = {kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1, kVectorNotUsed, r0, r1,
563                          INVALID_SREG, INVALID_SREG};
564  StoreValueWide(rl_dest, rl_result);
565}
566
567void X86Mir2Lir::GenOrLong(RegLocation rl_dest,
568                           RegLocation rl_src1, RegLocation rl_src2) {
569  // TODO: fixed register usage here as we only have 4 temps and temporary allocation isn't smart
570  // enough.
571  FlushAllRegs();
572  LockCallTemps();  // Prepare for explicit register usage
573  LoadValueDirectWideFixed(rl_src1, r0, r1);
574  LoadValueDirectWideFixed(rl_src2, r2, r3);
575  // Compute (r1:r0) = (r1:r0) | (r2:r3)
576  OpRegReg(kOpOr, r0, r2);  // r0 = r0 | r2
577  OpRegReg(kOpOr, r1, r3);  // r1 = r1 | r3
578  RegLocation rl_result = {kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1, kVectorNotUsed, r0, r1,
579                          INVALID_SREG, INVALID_SREG};
580  StoreValueWide(rl_dest, rl_result);
581}
582
583void X86Mir2Lir::GenXorLong(RegLocation rl_dest,
584                            RegLocation rl_src1, RegLocation rl_src2) {
585  // TODO: fixed register usage here as we only have 4 temps and temporary allocation isn't smart
586  // enough.
587  FlushAllRegs();
588  LockCallTemps();  // Prepare for explicit register usage
589  LoadValueDirectWideFixed(rl_src1, r0, r1);
590  LoadValueDirectWideFixed(rl_src2, r2, r3);
591  // Compute (r1:r0) = (r1:r0) ^ (r2:r3)
592  OpRegReg(kOpXor, r0, r2);  // r0 = r0 ^ r2
593  OpRegReg(kOpXor, r1, r3);  // r1 = r1 ^ r3
594  RegLocation rl_result = {kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1, kVectorNotUsed, r0, r1,
595                          INVALID_SREG, INVALID_SREG};
596  StoreValueWide(rl_dest, rl_result);
597}
598
599void X86Mir2Lir::GenNegLong(RegLocation rl_dest, RegLocation rl_src) {
600  FlushAllRegs();
601  LockCallTemps();  // Prepare for explicit register usage
602  LoadValueDirectWideFixed(rl_src, r0, r1);
603  // Compute (r1:r0) = -(r1:r0)
604  OpRegReg(kOpNeg, r0, r0);  // r0 = -r0
605  OpRegImm(kOpAdc, r1, 0);   // r1 = r1 + CF
606  OpRegReg(kOpNeg, r1, r1);  // r1 = -r1
607  RegLocation rl_result = {kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1, kVectorNotUsed, r0, r1,
608                          INVALID_SREG, INVALID_SREG};
609  StoreValueWide(rl_dest, rl_result);
610}
611
612void X86Mir2Lir::OpRegThreadMem(OpKind op, int r_dest, ThreadOffset thread_offset) {
613  X86OpCode opcode = kX86Bkpt;
614  switch (op) {
615  case kOpCmp: opcode = kX86Cmp32RT;  break;
616  case kOpMov: opcode = kX86Mov32RT;  break;
617  default:
618    LOG(FATAL) << "Bad opcode: " << op;
619    break;
620  }
621  NewLIR2(opcode, r_dest, thread_offset.Int32Value());
622}
623
624/*
625 * Generate array load
626 */
627void X86Mir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array,
628                             RegLocation rl_index, RegLocation rl_dest, int scale) {
629  RegisterClass reg_class = oat_reg_class_by_size(size);
630  int len_offset = mirror::Array::LengthOffset().Int32Value();
631  RegLocation rl_result;
632  rl_array = LoadValue(rl_array, kCoreReg);
633
634  int data_offset;
635  if (size == kLong || size == kDouble) {
636    data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Int32Value();
637  } else {
638    data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Int32Value();
639  }
640
641  bool constant_index = rl_index.is_const;
642  int32_t constant_index_value = 0;
643  if (!constant_index) {
644    rl_index = LoadValue(rl_index, kCoreReg);
645  } else {
646    constant_index_value = mir_graph_->ConstantValue(rl_index);
647    // If index is constant, just fold it into the data offset
648    data_offset += constant_index_value << scale;
649    // treat as non array below
650    rl_index.low_reg = INVALID_REG;
651  }
652
653  /* null object? */
654  GenNullCheck(rl_array.s_reg_low, rl_array.low_reg, opt_flags);
655
656  if (!(opt_flags & MIR_IGNORE_RANGE_CHECK)) {
657    if (constant_index) {
658      GenMemImmedCheck(kCondLs, rl_array.low_reg, len_offset,
659                       constant_index_value, kThrowConstantArrayBounds);
660    } else {
661      GenRegMemCheck(kCondUge, rl_index.low_reg, rl_array.low_reg,
662                     len_offset, kThrowArrayBounds);
663    }
664  }
665  rl_result = EvalLoc(rl_dest, reg_class, true);
666  if ((size == kLong) || (size == kDouble)) {
667    LoadBaseIndexedDisp(rl_array.low_reg, rl_index.low_reg, scale, data_offset, rl_result.low_reg,
668                        rl_result.high_reg, size, INVALID_SREG);
669    StoreValueWide(rl_dest, rl_result);
670  } else {
671    LoadBaseIndexedDisp(rl_array.low_reg, rl_index.low_reg, scale,
672                        data_offset, rl_result.low_reg, INVALID_REG, size,
673                        INVALID_SREG);
674    StoreValue(rl_dest, rl_result);
675  }
676}
677
678/*
679 * Generate array store
680 *
681 */
682void X86Mir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array,
683                             RegLocation rl_index, RegLocation rl_src, int scale, bool card_mark) {
684  RegisterClass reg_class = oat_reg_class_by_size(size);
685  int len_offset = mirror::Array::LengthOffset().Int32Value();
686  int data_offset;
687
688  if (size == kLong || size == kDouble) {
689    data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Int32Value();
690  } else {
691    data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Int32Value();
692  }
693
694  rl_array = LoadValue(rl_array, kCoreReg);
695  bool constant_index = rl_index.is_const;
696  int32_t constant_index_value = 0;
697  if (!constant_index) {
698    rl_index = LoadValue(rl_index, kCoreReg);
699  } else {
700    // If index is constant, just fold it into the data offset
701    constant_index_value = mir_graph_->ConstantValue(rl_index);
702    data_offset += constant_index_value << scale;
703    // treat as non array below
704    rl_index.low_reg = INVALID_REG;
705  }
706
707  /* null object? */
708  GenNullCheck(rl_array.s_reg_low, rl_array.low_reg, opt_flags);
709
710  if (!(opt_flags & MIR_IGNORE_RANGE_CHECK)) {
711    if (constant_index) {
712      GenMemImmedCheck(kCondLs, rl_array.low_reg, len_offset,
713                       constant_index_value, kThrowConstantArrayBounds);
714    } else {
715      GenRegMemCheck(kCondUge, rl_index.low_reg, rl_array.low_reg,
716                     len_offset, kThrowArrayBounds);
717    }
718  }
719  if ((size == kLong) || (size == kDouble)) {
720    rl_src = LoadValueWide(rl_src, reg_class);
721  } else {
722    rl_src = LoadValue(rl_src, reg_class);
723  }
724  // If the src reg can't be byte accessed, move it to a temp first.
725  if ((size == kSignedByte || size == kUnsignedByte) && rl_src.low_reg >= 4) {
726    int temp = AllocTemp();
727    OpRegCopy(temp, rl_src.low_reg);
728    StoreBaseIndexedDisp(rl_array.low_reg, rl_index.low_reg, scale, data_offset, temp,
729                         INVALID_REG, size, INVALID_SREG);
730  } else {
731    StoreBaseIndexedDisp(rl_array.low_reg, rl_index.low_reg, scale, data_offset, rl_src.low_reg,
732                         rl_src.high_reg, size, INVALID_SREG);
733  }
734  if (card_mark) {
735    // Free rl_index if its a temp. Ensures there are 2 free regs for card mark.
736    if (!constant_index) {
737      FreeTemp(rl_index.low_reg);
738    }
739    MarkGCCard(rl_src.low_reg, rl_array.low_reg);
740  }
741}
742
743void X86Mir2Lir::GenShiftImmOpLong(Instruction::Code opcode, RegLocation rl_dest,
744                                   RegLocation rl_src1, RegLocation rl_shift) {
745  // Default implementation is just to ignore the constant case.
746  GenShiftOpLong(opcode, rl_dest, rl_src1, rl_shift);
747}
748
749void X86Mir2Lir::GenArithImmOpLong(Instruction::Code opcode,
750                                   RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) {
751  // Default - bail to non-const handler.
752  GenArithOpLong(opcode, rl_dest, rl_src1, rl_src2);
753}
754
755}  // namespace art
756