fp_x86.cc revision 02959eae949f37445c184ae6f3df4d068ff309e0
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#include "codegen_x86.h"
18#include "dex/quick/mir_to_lir-inl.h"
19#include "x86_lir.h"
20
21namespace art {
22
23void X86Mir2Lir::GenArithOpFloat(Instruction::Code opcode,
24                                 RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) {
25  X86OpCode op = kX86Nop;
26  RegLocation rl_result;
27
28  /*
29   * Don't attempt to optimize register usage since these opcodes call out to
30   * the handlers.
31   */
32  switch (opcode) {
33    case Instruction::ADD_FLOAT_2ADDR:
34    case Instruction::ADD_FLOAT:
35      op = kX86AddssRR;
36      break;
37    case Instruction::SUB_FLOAT_2ADDR:
38    case Instruction::SUB_FLOAT:
39      op = kX86SubssRR;
40      break;
41    case Instruction::DIV_FLOAT_2ADDR:
42    case Instruction::DIV_FLOAT:
43      op = kX86DivssRR;
44      break;
45    case Instruction::MUL_FLOAT_2ADDR:
46    case Instruction::MUL_FLOAT:
47      op = kX86MulssRR;
48      break;
49    case Instruction::REM_FLOAT_2ADDR:
50    case Instruction::REM_FLOAT:
51      GenRemFP(rl_dest, rl_src1, rl_src2, false /* is_double */);
52      return;
53    case Instruction::NEG_FLOAT:
54      GenNegFloat(rl_dest, rl_src1);
55      return;
56    default:
57      LOG(FATAL) << "Unexpected opcode: " << opcode;
58  }
59  rl_src1 = LoadValue(rl_src1, kFPReg);
60  rl_src2 = LoadValue(rl_src2, kFPReg);
61  rl_result = EvalLoc(rl_dest, kFPReg, true);
62  RegStorage r_dest = rl_result.reg;
63  RegStorage r_src1 = rl_src1.reg;
64  RegStorage r_src2 = rl_src2.reg;
65  if (r_dest == r_src2) {
66    r_src2 = AllocTempSingle();
67    OpRegCopy(r_src2, r_dest);
68  }
69  OpRegCopy(r_dest, r_src1);
70  NewLIR2(op, r_dest.GetReg(), r_src2.GetReg());
71  StoreValue(rl_dest, rl_result);
72}
73
74void X86Mir2Lir::GenArithOpDouble(Instruction::Code opcode,
75                                  RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) {
76  DCHECK(rl_dest.wide);
77  DCHECK(rl_dest.fp);
78  DCHECK(rl_src1.wide);
79  DCHECK(rl_src1.fp);
80  DCHECK(rl_src2.wide);
81  DCHECK(rl_src2.fp);
82  X86OpCode op = kX86Nop;
83  RegLocation rl_result;
84
85  switch (opcode) {
86    case Instruction::ADD_DOUBLE_2ADDR:
87    case Instruction::ADD_DOUBLE:
88      op = kX86AddsdRR;
89      break;
90    case Instruction::SUB_DOUBLE_2ADDR:
91    case Instruction::SUB_DOUBLE:
92      op = kX86SubsdRR;
93      break;
94    case Instruction::DIV_DOUBLE_2ADDR:
95    case Instruction::DIV_DOUBLE:
96      op = kX86DivsdRR;
97      break;
98    case Instruction::MUL_DOUBLE_2ADDR:
99    case Instruction::MUL_DOUBLE:
100      op = kX86MulsdRR;
101      break;
102    case Instruction::REM_DOUBLE_2ADDR:
103    case Instruction::REM_DOUBLE:
104      GenRemFP(rl_dest, rl_src1, rl_src2, true /* is_double */);
105      return;
106    case Instruction::NEG_DOUBLE:
107      GenNegDouble(rl_dest, rl_src1);
108      return;
109    default:
110      LOG(FATAL) << "Unexpected opcode: " << opcode;
111  }
112  rl_src1 = LoadValueWide(rl_src1, kFPReg);
113  rl_src2 = LoadValueWide(rl_src2, kFPReg);
114  rl_result = EvalLoc(rl_dest, kFPReg, true);
115  if (rl_result.reg == rl_src2.reg) {
116    rl_src2.reg = AllocTempDouble();
117    OpRegCopy(rl_src2.reg, rl_result.reg);
118  }
119  OpRegCopy(rl_result.reg, rl_src1.reg);
120  NewLIR2(op, rl_result.reg.GetReg(), rl_src2.reg.GetReg());
121  StoreValueWide(rl_dest, rl_result);
122}
123
124void X86Mir2Lir::GenLongToFP(RegLocation rl_dest, RegLocation rl_src, bool is_double) {
125  // Compute offsets to the source and destination VRs on stack
126  int src_v_reg_offset = SRegOffset(rl_src.s_reg_low);
127  int dest_v_reg_offset = SRegOffset(rl_dest.s_reg_low);
128
129  // Update the in-register state of source.
130  rl_src = UpdateLocWide(rl_src);
131
132  // All memory accesses below reference dalvik regs.
133  ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
134
135  // If the source is in physical register, then put it in its location on stack.
136  if (rl_src.location == kLocPhysReg) {
137    RegisterInfo* reg_info = GetRegInfo(rl_src.reg);
138
139    if (reg_info != nullptr && reg_info->IsTemp()) {
140      // Calling FlushSpecificReg because it will only write back VR if it is dirty.
141      FlushSpecificReg(reg_info);
142      // ResetDef to prevent NullifyRange from removing stores.
143      ResetDef(rl_src.reg);
144    } else {
145      // It must have been register promoted if it is not a temp but is still in physical
146      // register. Since we need it to be in memory to convert, we place it there now.
147      StoreBaseDisp(TargetReg(kSp), src_v_reg_offset, rl_src.reg, k64);
148    }
149  }
150
151  // Push the source virtual register onto the x87 stack.
152  LIR *fild64 = NewLIR2NoDest(kX86Fild64M, TargetReg(kSp).GetReg(),
153                              src_v_reg_offset + LOWORD_OFFSET);
154  AnnotateDalvikRegAccess(fild64, (src_v_reg_offset + LOWORD_OFFSET) >> 2,
155                          true /* is_load */, true /* is64bit */);
156
157  // Now pop off x87 stack and store it in the destination VR's stack location.
158  int opcode = is_double ? kX86Fstp64M : kX86Fstp32M;
159  int displacement = is_double ? dest_v_reg_offset + LOWORD_OFFSET : dest_v_reg_offset;
160  LIR *fstp = NewLIR2NoDest(opcode, TargetReg(kSp).GetReg(), displacement);
161  AnnotateDalvikRegAccess(fstp, displacement >> 2, false /* is_load */, is_double);
162
163  /*
164   * The result is in a physical register if it was in a temp or was register
165   * promoted. For that reason it is enough to check if it is in physical
166   * register. If it is, then we must do all of the bookkeeping necessary to
167   * invalidate temp (if needed) and load in promoted register (if needed).
168   * If the result's location is in memory, then we do not need to do anything
169   * more since the fstp has already placed the correct value in memory.
170   */
171  RegLocation rl_result = is_double ? UpdateLocWideTyped(rl_dest, kFPReg) :
172      UpdateLocTyped(rl_dest, kFPReg);
173  if (rl_result.location == kLocPhysReg) {
174    /*
175     * We already know that the result is in a physical register but do not know if it is the
176     * right class. So we call EvalLoc(Wide) first which will ensure that it will get moved to the
177     * correct register class.
178     */
179    rl_result = EvalLoc(rl_dest, kFPReg, true);
180    if (is_double) {
181      LoadBaseDisp(TargetReg(kSp), dest_v_reg_offset, rl_result.reg, k64);
182
183      StoreFinalValueWide(rl_dest, rl_result);
184    } else {
185      Load32Disp(TargetReg(kSp), dest_v_reg_offset, rl_result.reg);
186
187      StoreFinalValue(rl_dest, rl_result);
188    }
189  }
190}
191
192void X86Mir2Lir::GenConversion(Instruction::Code opcode, RegLocation rl_dest,
193                               RegLocation rl_src) {
194  RegisterClass rcSrc = kFPReg;
195  X86OpCode op = kX86Nop;
196  RegLocation rl_result;
197  switch (opcode) {
198    case Instruction::INT_TO_FLOAT:
199      rcSrc = kCoreReg;
200      op = kX86Cvtsi2ssRR;
201      break;
202    case Instruction::DOUBLE_TO_FLOAT:
203      rcSrc = kFPReg;
204      op = kX86Cvtsd2ssRR;
205      break;
206    case Instruction::FLOAT_TO_DOUBLE:
207      rcSrc = kFPReg;
208      op = kX86Cvtss2sdRR;
209      break;
210    case Instruction::INT_TO_DOUBLE:
211      rcSrc = kCoreReg;
212      op = kX86Cvtsi2sdRR;
213      break;
214    case Instruction::FLOAT_TO_INT: {
215      rl_src = LoadValue(rl_src, kFPReg);
216      // In case result vreg is also src vreg, break association to avoid useless copy by EvalLoc()
217      ClobberSReg(rl_dest.s_reg_low);
218      rl_result = EvalLoc(rl_dest, kCoreReg, true);
219      RegStorage temp_reg = AllocTempSingle();
220
221      LoadConstant(rl_result.reg, 0x7fffffff);
222      NewLIR2(kX86Cvtsi2ssRR, temp_reg.GetReg(), rl_result.reg.GetReg());
223      NewLIR2(kX86ComissRR, rl_src.reg.GetReg(), temp_reg.GetReg());
224      LIR* branch_pos_overflow = NewLIR2(kX86Jcc8, 0, kX86CondA);
225      LIR* branch_na_n = NewLIR2(kX86Jcc8, 0, kX86CondP);
226      NewLIR2(kX86Cvttss2siRR, rl_result.reg.GetReg(), rl_src.reg.GetReg());
227      LIR* branch_normal = NewLIR1(kX86Jmp8, 0);
228      branch_na_n->target = NewLIR0(kPseudoTargetLabel);
229      NewLIR2(kX86Xor32RR, rl_result.reg.GetReg(), rl_result.reg.GetReg());
230      branch_pos_overflow->target = NewLIR0(kPseudoTargetLabel);
231      branch_normal->target = NewLIR0(kPseudoTargetLabel);
232      StoreValue(rl_dest, rl_result);
233      return;
234    }
235    case Instruction::DOUBLE_TO_INT: {
236      rl_src = LoadValueWide(rl_src, kFPReg);
237      // In case result vreg is also src vreg, break association to avoid useless copy by EvalLoc()
238      ClobberSReg(rl_dest.s_reg_low);
239      rl_result = EvalLoc(rl_dest, kCoreReg, true);
240      RegStorage temp_reg = AllocTempDouble();
241
242      LoadConstant(rl_result.reg, 0x7fffffff);
243      NewLIR2(kX86Cvtsi2sdRR, temp_reg.GetReg(), rl_result.reg.GetReg());
244      NewLIR2(kX86ComisdRR, rl_src.reg.GetReg(), temp_reg.GetReg());
245      LIR* branch_pos_overflow = NewLIR2(kX86Jcc8, 0, kX86CondA);
246      LIR* branch_na_n = NewLIR2(kX86Jcc8, 0, kX86CondP);
247      NewLIR2(kX86Cvttsd2siRR, rl_result.reg.GetReg(), rl_src.reg.GetReg());
248      LIR* branch_normal = NewLIR1(kX86Jmp8, 0);
249      branch_na_n->target = NewLIR0(kPseudoTargetLabel);
250      NewLIR2(kX86Xor32RR, rl_result.reg.GetReg(), rl_result.reg.GetReg());
251      branch_pos_overflow->target = NewLIR0(kPseudoTargetLabel);
252      branch_normal->target = NewLIR0(kPseudoTargetLabel);
253      StoreValue(rl_dest, rl_result);
254      return;
255    }
256    case Instruction::LONG_TO_DOUBLE:
257      if (Gen64Bit()) {
258        rcSrc = kCoreReg;
259        op = kX86Cvtsqi2sdRR;
260        break;
261      }
262      GenLongToFP(rl_dest, rl_src, true /* is_double */);
263      return;
264    case Instruction::LONG_TO_FLOAT:
265      if (Gen64Bit()) {
266        rcSrc = kCoreReg;
267        op = kX86Cvtsqi2ssRR;
268       break;
269      }
270      GenLongToFP(rl_dest, rl_src, false /* is_double */);
271      return;
272    case Instruction::FLOAT_TO_LONG:
273      if (Gen64Bit()) {
274        rl_src = LoadValue(rl_src, kFPReg);
275        // If result vreg is also src vreg, break association to avoid useless copy by EvalLoc()
276        ClobberSReg(rl_dest.s_reg_low);
277        rl_result = EvalLoc(rl_dest, kCoreReg, true);
278        RegStorage temp_reg = AllocTempSingle();
279
280        // Set 0x7fffffffffffffff to rl_result
281        LoadConstantWide(rl_result.reg, 0x7fffffffffffffff);
282        NewLIR2(kX86Cvtsqi2ssRR, temp_reg.GetReg(), rl_result.reg.GetReg());
283        NewLIR2(kX86ComissRR, rl_src.reg.GetReg(), temp_reg.GetReg());
284        LIR* branch_pos_overflow = NewLIR2(kX86Jcc8, 0, kX86CondA);
285        LIR* branch_na_n = NewLIR2(kX86Jcc8, 0, kX86CondP);
286        NewLIR2(kX86Cvttss2sqiRR, rl_result.reg.GetReg(), rl_src.reg.GetReg());
287        LIR* branch_normal = NewLIR1(kX86Jmp8, 0);
288        branch_na_n->target = NewLIR0(kPseudoTargetLabel);
289        NewLIR2(kX86Xor64RR, rl_result.reg.GetReg(), rl_result.reg.GetReg());
290        branch_pos_overflow->target = NewLIR0(kPseudoTargetLabel);
291        branch_normal->target = NewLIR0(kPseudoTargetLabel);
292        StoreValueWide(rl_dest, rl_result);
293      } else {
294        GenConversionCall(QUICK_ENTRYPOINT_OFFSET(4, pF2l), rl_dest, rl_src);
295      }
296      return;
297    case Instruction::DOUBLE_TO_LONG:
298      if (Gen64Bit()) {
299        rl_src = LoadValueWide(rl_src, kFPReg);
300        // If result vreg is also src vreg, break association to avoid useless copy by EvalLoc()
301        ClobberSReg(rl_dest.s_reg_low);
302        rl_result = EvalLoc(rl_dest, kCoreReg, true);
303        RegStorage temp_reg = AllocTempDouble();
304
305        // Set 0x7fffffffffffffff to rl_result
306        LoadConstantWide(rl_result.reg, 0x7fffffffffffffff);
307        NewLIR2(kX86Cvtsqi2sdRR, temp_reg.GetReg(), rl_result.reg.GetReg());
308        NewLIR2(kX86ComisdRR, rl_src.reg.GetReg(), temp_reg.GetReg());
309        LIR* branch_pos_overflow = NewLIR2(kX86Jcc8, 0, kX86CondA);
310        LIR* branch_na_n = NewLIR2(kX86Jcc8, 0, kX86CondP);
311        NewLIR2(kX86Cvttsd2sqiRR, rl_result.reg.GetReg(), rl_src.reg.GetReg());
312        LIR* branch_normal = NewLIR1(kX86Jmp8, 0);
313        branch_na_n->target = NewLIR0(kPseudoTargetLabel);
314        NewLIR2(kX86Xor64RR, rl_result.reg.GetReg(), rl_result.reg.GetReg());
315        branch_pos_overflow->target = NewLIR0(kPseudoTargetLabel);
316        branch_normal->target = NewLIR0(kPseudoTargetLabel);
317        StoreValueWide(rl_dest, rl_result);
318      } else {
319        GenConversionCall(QUICK_ENTRYPOINT_OFFSET(4, pD2l), rl_dest, rl_src);
320      }
321      return;
322    default:
323      LOG(INFO) << "Unexpected opcode: " << opcode;
324  }
325  // At this point, target will be either float or double.
326  DCHECK(rl_dest.fp);
327  if (rl_src.wide) {
328    rl_src = LoadValueWide(rl_src, rcSrc);
329  } else {
330    rl_src = LoadValue(rl_src, rcSrc);
331  }
332  rl_result = EvalLoc(rl_dest, kFPReg, true);
333  NewLIR2(op, rl_result.reg.GetReg(), rl_src.reg.GetReg());
334  if (rl_dest.wide) {
335    StoreValueWide(rl_dest, rl_result);
336  } else {
337    StoreValue(rl_dest, rl_result);
338  }
339}
340
341void X86Mir2Lir::GenRemFP(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2, bool is_double) {
342  // Compute offsets to the source and destination VRs on stack.
343  int src1_v_reg_offset = SRegOffset(rl_src1.s_reg_low);
344  int src2_v_reg_offset = SRegOffset(rl_src2.s_reg_low);
345  int dest_v_reg_offset = SRegOffset(rl_dest.s_reg_low);
346
347  // Update the in-register state of sources.
348  rl_src1 = is_double ? UpdateLocWide(rl_src1) : UpdateLoc(rl_src1);
349  rl_src2 = is_double ? UpdateLocWide(rl_src2) : UpdateLoc(rl_src2);
350
351  // All memory accesses below reference dalvik regs.
352  ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
353
354  // If the source is in physical register, then put it in its location on stack.
355  if (rl_src1.location == kLocPhysReg) {
356    RegisterInfo* reg_info = GetRegInfo(rl_src1.reg);
357
358    if (reg_info != nullptr && reg_info->IsTemp()) {
359      // Calling FlushSpecificReg because it will only write back VR if it is dirty.
360      FlushSpecificReg(reg_info);
361      // ResetDef to prevent NullifyRange from removing stores.
362      ResetDef(rl_src1.reg);
363    } else {
364      // It must have been register promoted if it is not a temp but is still in physical
365      // register. Since we need it to be in memory to convert, we place it there now.
366      StoreBaseDisp(TargetReg(kSp), src1_v_reg_offset, rl_src1.reg, is_double ? k64 : k32);
367    }
368  }
369
370  if (rl_src2.location == kLocPhysReg) {
371    RegisterInfo* reg_info = GetRegInfo(rl_src2.reg);
372    if (reg_info != nullptr && reg_info->IsTemp()) {
373      FlushSpecificReg(reg_info);
374      ResetDef(rl_src2.reg);
375    } else {
376      StoreBaseDisp(TargetReg(kSp), src2_v_reg_offset, rl_src2.reg, is_double ? k64 : k32);
377    }
378  }
379
380  int fld_opcode = is_double ? kX86Fld64M : kX86Fld32M;
381
382  // Push the source virtual registers onto the x87 stack.
383  LIR *fld_2 = NewLIR2NoDest(fld_opcode, TargetReg(kSp).GetReg(),
384                             src2_v_reg_offset + LOWORD_OFFSET);
385  AnnotateDalvikRegAccess(fld_2, (src2_v_reg_offset + LOWORD_OFFSET) >> 2,
386                          true /* is_load */, is_double /* is64bit */);
387
388  LIR *fld_1 = NewLIR2NoDest(fld_opcode, TargetReg(kSp).GetReg(),
389                             src1_v_reg_offset + LOWORD_OFFSET);
390  AnnotateDalvikRegAccess(fld_1, (src1_v_reg_offset + LOWORD_OFFSET) >> 2,
391                          true /* is_load */, is_double /* is64bit */);
392
393  FlushReg(rs_rAX);
394  Clobber(rs_rAX);
395  LockTemp(rs_rAX);
396
397  LIR* retry = NewLIR0(kPseudoTargetLabel);
398
399  // Divide ST(0) by ST(1) and place result to ST(0).
400  NewLIR0(kX86Fprem);
401
402  // Move FPU status word to AX.
403  NewLIR0(kX86Fstsw16R);
404
405  // Check if reduction is complete.
406  OpRegImm(kOpAnd, rs_rAX, 0x400);
407
408  // If no then continue to compute remainder.
409  LIR* branch = NewLIR2(kX86Jcc8, 0, kX86CondNe);
410  branch->target = retry;
411
412  FreeTemp(rs_rAX);
413
414  // Now store result in the destination VR's stack location.
415  int displacement = dest_v_reg_offset + LOWORD_OFFSET;
416  int opcode = is_double ? kX86Fst64M : kX86Fst32M;
417  LIR *fst = NewLIR2NoDest(opcode, TargetReg(kSp).GetReg(), displacement);
418  AnnotateDalvikRegAccess(fst, displacement >> 2, false /* is_load */, is_double /* is64bit */);
419
420  // Pop ST(1) and ST(0).
421  NewLIR0(kX86Fucompp);
422
423  /*
424   * The result is in a physical register if it was in a temp or was register
425   * promoted. For that reason it is enough to check if it is in physical
426   * register. If it is, then we must do all of the bookkeeping necessary to
427   * invalidate temp (if needed) and load in promoted register (if needed).
428   * If the result's location is in memory, then we do not need to do anything
429   * more since the fstp has already placed the correct value in memory.
430   */
431  RegLocation rl_result = is_double ? UpdateLocWideTyped(rl_dest, kFPReg) :
432      UpdateLocTyped(rl_dest, kFPReg);
433  if (rl_result.location == kLocPhysReg) {
434    rl_result = EvalLoc(rl_dest, kFPReg, true);
435    if (is_double) {
436      LoadBaseDisp(TargetReg(kSp), dest_v_reg_offset, rl_result.reg, k64);
437      StoreFinalValueWide(rl_dest, rl_result);
438    } else {
439      Load32Disp(TargetReg(kSp), dest_v_reg_offset, rl_result.reg);
440      StoreFinalValue(rl_dest, rl_result);
441    }
442  }
443}
444
445void X86Mir2Lir::GenCmpFP(Instruction::Code code, RegLocation rl_dest,
446                          RegLocation rl_src1, RegLocation rl_src2) {
447  bool single = (code == Instruction::CMPL_FLOAT) || (code == Instruction::CMPG_FLOAT);
448  bool unordered_gt = (code == Instruction::CMPG_DOUBLE) || (code == Instruction::CMPG_FLOAT);
449  if (single) {
450    rl_src1 = LoadValue(rl_src1, kFPReg);
451    rl_src2 = LoadValue(rl_src2, kFPReg);
452  } else {
453    rl_src1 = LoadValueWide(rl_src1, kFPReg);
454    rl_src2 = LoadValueWide(rl_src2, kFPReg);
455  }
456  // In case result vreg is also src vreg, break association to avoid useless copy by EvalLoc()
457  ClobberSReg(rl_dest.s_reg_low);
458  RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
459  LoadConstantNoClobber(rl_result.reg, unordered_gt ? 1 : 0);
460  if (single) {
461    NewLIR2(kX86UcomissRR, rl_src1.reg.GetReg(), rl_src2.reg.GetReg());
462  } else {
463    NewLIR2(kX86UcomisdRR, rl_src1.reg.GetReg(), rl_src2.reg.GetReg());
464  }
465  LIR* branch = NULL;
466  if (unordered_gt) {
467    branch = NewLIR2(kX86Jcc8, 0, kX86CondPE);
468  }
469  // If the result reg can't be byte accessed, use a jump and move instead of a set.
470  if (!IsByteRegister(rl_result.reg)) {
471    LIR* branch2 = NULL;
472    if (unordered_gt) {
473      branch2 = NewLIR2(kX86Jcc8, 0, kX86CondA);
474      NewLIR2(kX86Mov32RI, rl_result.reg.GetReg(), 0x0);
475    } else {
476      branch2 = NewLIR2(kX86Jcc8, 0, kX86CondBe);
477      NewLIR2(kX86Mov32RI, rl_result.reg.GetReg(), 0x1);
478    }
479    branch2->target = NewLIR0(kPseudoTargetLabel);
480  } else {
481    NewLIR2(kX86Set8R, rl_result.reg.GetReg(), kX86CondA /* above - unsigned > */);
482  }
483  NewLIR2(kX86Sbb32RI, rl_result.reg.GetReg(), 0);
484  if (unordered_gt) {
485    branch->target = NewLIR0(kPseudoTargetLabel);
486  }
487  StoreValue(rl_dest, rl_result);
488}
489
490void X86Mir2Lir::GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias,
491                                     bool is_double) {
492  LIR* taken = &block_label_list_[bb->taken];
493  LIR* not_taken = &block_label_list_[bb->fall_through];
494  LIR* branch = NULL;
495  RegLocation rl_src1;
496  RegLocation rl_src2;
497  if (is_double) {
498    rl_src1 = mir_graph_->GetSrcWide(mir, 0);
499    rl_src2 = mir_graph_->GetSrcWide(mir, 2);
500    rl_src1 = LoadValueWide(rl_src1, kFPReg);
501    rl_src2 = LoadValueWide(rl_src2, kFPReg);
502    NewLIR2(kX86UcomisdRR, rl_src1.reg.GetReg(), rl_src2.reg.GetReg());
503  } else {
504    rl_src1 = mir_graph_->GetSrc(mir, 0);
505    rl_src2 = mir_graph_->GetSrc(mir, 1);
506    rl_src1 = LoadValue(rl_src1, kFPReg);
507    rl_src2 = LoadValue(rl_src2, kFPReg);
508    NewLIR2(kX86UcomissRR, rl_src1.reg.GetReg(), rl_src2.reg.GetReg());
509  }
510  ConditionCode ccode = mir->meta.ccode;
511  switch (ccode) {
512    case kCondEq:
513      if (!gt_bias) {
514        branch = NewLIR2(kX86Jcc8, 0, kX86CondPE);
515        branch->target = not_taken;
516      }
517      break;
518    case kCondNe:
519      if (!gt_bias) {
520        branch = NewLIR2(kX86Jcc8, 0, kX86CondPE);
521        branch->target = taken;
522      }
523      break;
524    case kCondLt:
525      if (gt_bias) {
526        branch = NewLIR2(kX86Jcc8, 0, kX86CondPE);
527        branch->target = not_taken;
528      }
529      ccode = kCondUlt;
530      break;
531    case kCondLe:
532      if (gt_bias) {
533        branch = NewLIR2(kX86Jcc8, 0, kX86CondPE);
534        branch->target = not_taken;
535      }
536      ccode = kCondLs;
537      break;
538    case kCondGt:
539      if (gt_bias) {
540        branch = NewLIR2(kX86Jcc8, 0, kX86CondPE);
541        branch->target = taken;
542      }
543      ccode = kCondHi;
544      break;
545    case kCondGe:
546      if (gt_bias) {
547        branch = NewLIR2(kX86Jcc8, 0, kX86CondPE);
548        branch->target = taken;
549      }
550      ccode = kCondUge;
551      break;
552    default:
553      LOG(FATAL) << "Unexpected ccode: " << ccode;
554  }
555  OpCondBranch(ccode, taken);
556}
557
558void X86Mir2Lir::GenNegFloat(RegLocation rl_dest, RegLocation rl_src) {
559  RegLocation rl_result;
560  rl_src = LoadValue(rl_src, kCoreReg);
561  rl_result = EvalLoc(rl_dest, kCoreReg, true);
562  OpRegRegImm(kOpAdd, rl_result.reg, rl_src.reg, 0x80000000);
563  StoreValue(rl_dest, rl_result);
564}
565
566void X86Mir2Lir::GenNegDouble(RegLocation rl_dest, RegLocation rl_src) {
567  RegLocation rl_result;
568  rl_src = LoadValueWide(rl_src, kCoreReg);
569  rl_result = EvalLocWide(rl_dest, kCoreReg, true);
570  if (Gen64Bit()) {
571    OpRegCopy(rl_result.reg, rl_src.reg);
572    // Flip sign bit.
573    NewLIR2(kX86Rol64RI, rl_result.reg.GetReg(), 1);
574    NewLIR2(kX86Xor64RI, rl_result.reg.GetReg(), 1);
575    NewLIR2(kX86Ror64RI, rl_result.reg.GetReg(), 1);
576  } else {
577    OpRegRegImm(kOpAdd, rl_result.reg.GetHigh(), rl_src.reg.GetHigh(), 0x80000000);
578    OpRegCopy(rl_result.reg, rl_src.reg);
579  }
580  StoreValueWide(rl_dest, rl_result);
581}
582
583bool X86Mir2Lir::GenInlinedSqrt(CallInfo* info) {
584  RegLocation rl_src = info->args[0];
585  RegLocation rl_dest = InlineTargetWide(info);  // double place for result
586  rl_src = LoadValueWide(rl_src, kFPReg);
587  RegLocation rl_result = EvalLoc(rl_dest, kFPReg, true);
588  NewLIR2(kX86SqrtsdRR, rl_result.reg.GetReg(), rl_src.reg.GetReg());
589  StoreValueWide(rl_dest, rl_result);
590  return true;
591}
592
593bool X86Mir2Lir::GenInlinedAbsFloat(CallInfo* info) {
594  // Get the argument
595  RegLocation rl_src = info->args[0];
596
597  // Get the inlined intrinsic target virtual register
598  RegLocation rl_dest = InlineTarget(info);
599
600  // Get the virtual register number
601  DCHECK_NE(rl_src.s_reg_low, INVALID_SREG);
602  if (rl_dest.s_reg_low == INVALID_SREG) {
603    // Result is unused, the code is dead. Inlining successful, no code generated.
604    return true;
605  }
606  int v_src_reg = mir_graph_->SRegToVReg(rl_src.s_reg_low);
607  int v_dst_reg = mir_graph_->SRegToVReg(rl_dest.s_reg_low);
608
609  // if argument is the same as inlined intrinsic target
610  if (v_src_reg == v_dst_reg) {
611    rl_src = UpdateLoc(rl_src);
612
613    // if argument is in the physical register
614    if (rl_src.location == kLocPhysReg) {
615      rl_src = LoadValue(rl_src, kCoreReg);
616      OpRegImm(kOpAnd, rl_src.reg, 0x7fffffff);
617      StoreValue(rl_dest, rl_src);
618      return true;
619    }
620    // the argument is in memory
621    DCHECK((rl_src.location == kLocDalvikFrame) ||
622         (rl_src.location == kLocCompilerTemp));
623
624    // Operate directly into memory.
625    int displacement = SRegOffset(rl_dest.s_reg_low);
626    ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
627    LIR *lir = NewLIR3(kX86And32MI, TargetReg(kSp).GetReg(), displacement, 0x7fffffff);
628    AnnotateDalvikRegAccess(lir, displacement >> 2, false /*is_load */, false /* is_64bit */);
629    AnnotateDalvikRegAccess(lir, displacement >> 2, true /* is_load */, false /* is_64bit*/);
630    return true;
631  } else {
632    rl_src = LoadValue(rl_src, kCoreReg);
633    RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
634    OpRegRegImm(kOpAnd, rl_result.reg, rl_src.reg, 0x7fffffff);
635    StoreValue(rl_dest, rl_result);
636    return true;
637  }
638}
639
640bool X86Mir2Lir::GenInlinedAbsDouble(CallInfo* info) {
641  RegLocation rl_src = info->args[0];
642  RegLocation rl_dest = InlineTargetWide(info);
643  DCHECK_NE(rl_src.s_reg_low, INVALID_SREG);
644  if (rl_dest.s_reg_low == INVALID_SREG) {
645    // Result is unused, the code is dead. Inlining successful, no code generated.
646    return true;
647  }
648  int v_src_reg = mir_graph_->SRegToVReg(rl_src.s_reg_low);
649  int v_dst_reg = mir_graph_->SRegToVReg(rl_dest.s_reg_low);
650  rl_src = UpdateLocWide(rl_src);
651
652  // if argument is in the physical XMM register
653  if (rl_src.location == kLocPhysReg && rl_src.reg.IsFloat()) {
654    RegLocation rl_result = EvalLoc(rl_dest, kFPReg, true);
655    if (rl_result.reg != rl_src.reg) {
656      LoadConstantWide(rl_result.reg, 0x7fffffffffffffff);
657      NewLIR2(kX86PandRR, rl_result.reg.GetReg(), rl_src.reg.GetReg());
658    } else {
659      RegStorage sign_mask = AllocTempDouble();
660      LoadConstantWide(sign_mask, 0x7fffffffffffffff);
661      NewLIR2(kX86PandRR, rl_result.reg.GetReg(), sign_mask.GetReg());
662      FreeTemp(sign_mask);
663    }
664    StoreValueWide(rl_dest, rl_result);
665    return true;
666  } else if (v_src_reg == v_dst_reg) {
667    // if argument is the same as inlined intrinsic target
668    // if argument is in the physical register
669    if (rl_src.location == kLocPhysReg) {
670      rl_src = LoadValueWide(rl_src, kCoreReg);
671      OpRegImm(kOpAnd, rl_src.reg.GetHigh(), 0x7fffffff);
672      StoreValueWide(rl_dest, rl_src);
673      return true;
674    }
675    // the argument is in memory
676    DCHECK((rl_src.location == kLocDalvikFrame) ||
677           (rl_src.location == kLocCompilerTemp));
678
679    // Operate directly into memory.
680    int displacement = SRegOffset(rl_dest.s_reg_low);
681    ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
682    LIR *lir = NewLIR3(kX86And32MI, TargetReg(kSp).GetReg(), displacement  + HIWORD_OFFSET, 0x7fffffff);
683    AnnotateDalvikRegAccess(lir, (displacement + HIWORD_OFFSET) >> 2, true /* is_load */, true /* is_64bit*/);
684    AnnotateDalvikRegAccess(lir, (displacement + HIWORD_OFFSET) >> 2, false /*is_load */, true /* is_64bit */);
685    return true;
686  } else {
687    rl_src = LoadValueWide(rl_src, kCoreReg);
688    RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
689    OpRegCopyWide(rl_result.reg, rl_src.reg);
690    OpRegImm(kOpAnd, rl_result.reg.GetHigh(), 0x7fffffff);
691    StoreValueWide(rl_dest, rl_result);
692    return true;
693  }
694}
695
696}  // namespace art
697