fp_arm.cc revision 2700f7e1edbcd2518f4978e4cd0e05a4149f91b6
1/*
2 * Copyright (C) 2011 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 "arm_lir.h"
18#include "codegen_arm.h"
19#include "dex/quick/mir_to_lir-inl.h"
20
21namespace art {
22
23void ArmMir2Lir::GenArithOpFloat(Instruction::Code opcode, RegLocation rl_dest,
24                                 RegLocation rl_src1, RegLocation rl_src2) {
25  int op = kThumbBkpt;
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 = kThumb2Vadds;
36      break;
37    case Instruction::SUB_FLOAT_2ADDR:
38    case Instruction::SUB_FLOAT:
39      op = kThumb2Vsubs;
40      break;
41    case Instruction::DIV_FLOAT_2ADDR:
42    case Instruction::DIV_FLOAT:
43      op = kThumb2Vdivs;
44      break;
45    case Instruction::MUL_FLOAT_2ADDR:
46    case Instruction::MUL_FLOAT:
47      op = kThumb2Vmuls;
48      break;
49    case Instruction::REM_FLOAT_2ADDR:
50    case Instruction::REM_FLOAT:
51      FlushAllRegs();   // Send everything to home location
52      CallRuntimeHelperRegLocationRegLocation(QUICK_ENTRYPOINT_OFFSET(pFmodf), rl_src1, rl_src2,
53                                              false);
54      rl_result = GetReturn(true);
55      StoreValue(rl_dest, rl_result);
56      return;
57    case Instruction::NEG_FLOAT:
58      GenNegFloat(rl_dest, rl_src1);
59      return;
60    default:
61      LOG(FATAL) << "Unexpected opcode: " << opcode;
62  }
63  rl_src1 = LoadValue(rl_src1, kFPReg);
64  rl_src2 = LoadValue(rl_src2, kFPReg);
65  rl_result = EvalLoc(rl_dest, kFPReg, true);
66  NewLIR3(op, rl_result.reg.GetReg(), rl_src1.reg.GetReg(), rl_src2.reg.GetReg());
67  StoreValue(rl_dest, rl_result);
68}
69
70void ArmMir2Lir::GenArithOpDouble(Instruction::Code opcode,
71                                  RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) {
72  int op = kThumbBkpt;
73  RegLocation rl_result;
74
75  switch (opcode) {
76    case Instruction::ADD_DOUBLE_2ADDR:
77    case Instruction::ADD_DOUBLE:
78      op = kThumb2Vaddd;
79      break;
80    case Instruction::SUB_DOUBLE_2ADDR:
81    case Instruction::SUB_DOUBLE:
82      op = kThumb2Vsubd;
83      break;
84    case Instruction::DIV_DOUBLE_2ADDR:
85    case Instruction::DIV_DOUBLE:
86      op = kThumb2Vdivd;
87      break;
88    case Instruction::MUL_DOUBLE_2ADDR:
89    case Instruction::MUL_DOUBLE:
90      op = kThumb2Vmuld;
91      break;
92    case Instruction::REM_DOUBLE_2ADDR:
93    case Instruction::REM_DOUBLE:
94      FlushAllRegs();   // Send everything to home location
95      CallRuntimeHelperRegLocationRegLocation(QUICK_ENTRYPOINT_OFFSET(pFmod), rl_src1, rl_src2,
96                                              false);
97      rl_result = GetReturnWide(true);
98      StoreValueWide(rl_dest, rl_result);
99      return;
100    case Instruction::NEG_DOUBLE:
101      GenNegDouble(rl_dest, rl_src1);
102      return;
103    default:
104      LOG(FATAL) << "Unexpected opcode: " << opcode;
105  }
106
107  rl_src1 = LoadValueWide(rl_src1, kFPReg);
108  DCHECK(rl_src1.wide);
109  rl_src2 = LoadValueWide(rl_src2, kFPReg);
110  DCHECK(rl_src2.wide);
111  rl_result = EvalLoc(rl_dest, kFPReg, true);
112  DCHECK(rl_dest.wide);
113  DCHECK(rl_result.wide);
114  NewLIR3(op, S2d(rl_result.reg.GetLowReg(), rl_result.reg.GetHighReg()), S2d(rl_src1.reg.GetLowReg(), rl_src1.reg.GetHighReg()),
115          S2d(rl_src2.reg.GetLowReg(), rl_src2.reg.GetHighReg()));
116  StoreValueWide(rl_dest, rl_result);
117}
118
119void ArmMir2Lir::GenConversion(Instruction::Code opcode,
120                               RegLocation rl_dest, RegLocation rl_src) {
121  int op = kThumbBkpt;
122  int src_reg;
123  RegLocation rl_result;
124
125  switch (opcode) {
126    case Instruction::INT_TO_FLOAT:
127      op = kThumb2VcvtIF;
128      break;
129    case Instruction::FLOAT_TO_INT:
130      op = kThumb2VcvtFI;
131      break;
132    case Instruction::DOUBLE_TO_FLOAT:
133      op = kThumb2VcvtDF;
134      break;
135    case Instruction::FLOAT_TO_DOUBLE:
136      op = kThumb2VcvtFd;
137      break;
138    case Instruction::INT_TO_DOUBLE:
139      op = kThumb2VcvtF64S32;
140      break;
141    case Instruction::DOUBLE_TO_INT:
142      op = kThumb2VcvtDI;
143      break;
144    case Instruction::LONG_TO_DOUBLE: {
145      rl_src = LoadValueWide(rl_src, kFPReg);
146      src_reg = S2d(rl_src.reg.GetLowReg(), rl_src.reg.GetHighReg());
147      rl_result = EvalLoc(rl_dest, kFPReg, true);
148      // TODO: fix AllocTempDouble to return a k64BitSolo double reg and lose the ARM_FP_DOUBLE.
149      RegStorage tmp1 = AllocTempDouble();
150      RegStorage tmp2 = AllocTempDouble();
151
152      // FIXME: needs 64-bit register cleanup.
153      NewLIR2(kThumb2VcvtF64S32, tmp1.GetLowReg() | ARM_FP_DOUBLE, (src_reg & ~ARM_FP_DOUBLE) + 1);
154      NewLIR2(kThumb2VcvtF64U32, S2d(rl_result.reg.GetLowReg(), rl_result.reg.GetHighReg()),
155              (src_reg & ~ARM_FP_DOUBLE));
156      LoadConstantWide(tmp2, 0x41f0000000000000LL);
157      NewLIR3(kThumb2VmlaF64, S2d(rl_result.reg.GetLowReg(), rl_result.reg.GetHighReg()),
158              tmp1.GetLowReg() | ARM_FP_DOUBLE, tmp2.GetLowReg() | ARM_FP_DOUBLE);
159      FreeTemp(tmp1);
160      FreeTemp(tmp2);
161      StoreValueWide(rl_dest, rl_result);
162      return;
163    }
164    case Instruction::FLOAT_TO_LONG:
165      GenConversionCall(QUICK_ENTRYPOINT_OFFSET(pF2l), rl_dest, rl_src);
166      return;
167    case Instruction::LONG_TO_FLOAT: {
168      rl_src = LoadValueWide(rl_src, kFPReg);
169      src_reg = S2d(rl_src.reg.GetLowReg(), rl_src.reg.GetHighReg());
170      rl_result = EvalLoc(rl_dest, kFPReg, true);
171      // Allocate temp registers.
172      RegStorage high_val = AllocTempDouble();
173      RegStorage low_val = AllocTempDouble();
174      RegStorage const_val = AllocTempDouble();
175      // Long to double.
176      NewLIR2(kThumb2VcvtF64S32, high_val.GetLowReg() | ARM_FP_DOUBLE,
177              (src_reg & ~ARM_FP_DOUBLE) + 1);
178      NewLIR2(kThumb2VcvtF64U32, low_val.GetLowReg() | ARM_FP_DOUBLE,
179              (src_reg & ~ARM_FP_DOUBLE));
180      LoadConstantWide(const_val, INT64_C(0x41f0000000000000));
181      NewLIR3(kThumb2VmlaF64, low_val.GetLowReg() | ARM_FP_DOUBLE,
182              high_val.GetLowReg() | ARM_FP_DOUBLE,
183              const_val.GetLowReg() | ARM_FP_DOUBLE);
184      // Double to float.
185      NewLIR2(kThumb2VcvtDF, rl_result.reg.GetReg(), low_val.GetLowReg() | ARM_FP_DOUBLE);
186      // Free temp registers.
187      FreeTemp(high_val);
188      FreeTemp(low_val);
189      FreeTemp(const_val);
190      // Store result.
191      StoreValue(rl_dest, rl_result);
192      return;
193    }
194    case Instruction::DOUBLE_TO_LONG:
195      GenConversionCall(QUICK_ENTRYPOINT_OFFSET(pD2l), rl_dest, rl_src);
196      return;
197    default:
198      LOG(FATAL) << "Unexpected opcode: " << opcode;
199  }
200  if (rl_src.wide) {
201    rl_src = LoadValueWide(rl_src, kFPReg);
202    src_reg = S2d(rl_src.reg.GetLowReg(), rl_src.reg.GetHighReg());
203  } else {
204    rl_src = LoadValue(rl_src, kFPReg);
205    src_reg = rl_src.reg.GetReg();
206  }
207  if (rl_dest.wide) {
208    rl_result = EvalLoc(rl_dest, kFPReg, true);
209    NewLIR2(op, S2d(rl_result.reg.GetLowReg(), rl_result.reg.GetHighReg()), src_reg);
210    StoreValueWide(rl_dest, rl_result);
211  } else {
212    rl_result = EvalLoc(rl_dest, kFPReg, true);
213    NewLIR2(op, rl_result.reg.GetReg(), src_reg);
214    StoreValue(rl_dest, rl_result);
215  }
216}
217
218void ArmMir2Lir::GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias,
219                                     bool is_double) {
220  LIR* target = &block_label_list_[bb->taken];
221  RegLocation rl_src1;
222  RegLocation rl_src2;
223  if (is_double) {
224    rl_src1 = mir_graph_->GetSrcWide(mir, 0);
225    rl_src2 = mir_graph_->GetSrcWide(mir, 2);
226    rl_src1 = LoadValueWide(rl_src1, kFPReg);
227    rl_src2 = LoadValueWide(rl_src2, kFPReg);
228    NewLIR2(kThumb2Vcmpd, S2d(rl_src1.reg.GetLowReg(), rl_src2.reg.GetHighReg()),
229            S2d(rl_src2.reg.GetLowReg(), rl_src2.reg.GetHighReg()));
230  } else {
231    rl_src1 = mir_graph_->GetSrc(mir, 0);
232    rl_src2 = mir_graph_->GetSrc(mir, 1);
233    rl_src1 = LoadValue(rl_src1, kFPReg);
234    rl_src2 = LoadValue(rl_src2, kFPReg);
235    NewLIR2(kThumb2Vcmps, rl_src1.reg.GetReg(), rl_src2.reg.GetReg());
236  }
237  NewLIR0(kThumb2Fmstat);
238  ConditionCode ccode = mir->meta.ccode;
239  switch (ccode) {
240    case kCondEq:
241    case kCondNe:
242      break;
243    case kCondLt:
244      if (gt_bias) {
245        ccode = kCondMi;
246      }
247      break;
248    case kCondLe:
249      if (gt_bias) {
250        ccode = kCondLs;
251      }
252      break;
253    case kCondGt:
254      if (gt_bias) {
255        ccode = kCondHi;
256      }
257      break;
258    case kCondGe:
259      if (gt_bias) {
260        ccode = kCondUge;
261      }
262      break;
263    default:
264      LOG(FATAL) << "Unexpected ccode: " << ccode;
265  }
266  OpCondBranch(ccode, target);
267}
268
269
270void ArmMir2Lir::GenCmpFP(Instruction::Code opcode, RegLocation rl_dest,
271                          RegLocation rl_src1, RegLocation rl_src2) {
272  bool is_double = false;
273  int default_result = -1;
274  RegLocation rl_result;
275
276  switch (opcode) {
277    case Instruction::CMPL_FLOAT:
278      is_double = false;
279      default_result = -1;
280      break;
281    case Instruction::CMPG_FLOAT:
282      is_double = false;
283      default_result = 1;
284      break;
285    case Instruction::CMPL_DOUBLE:
286      is_double = true;
287      default_result = -1;
288      break;
289    case Instruction::CMPG_DOUBLE:
290      is_double = true;
291      default_result = 1;
292      break;
293    default:
294      LOG(FATAL) << "Unexpected opcode: " << opcode;
295  }
296  if (is_double) {
297    rl_src1 = LoadValueWide(rl_src1, kFPReg);
298    rl_src2 = LoadValueWide(rl_src2, kFPReg);
299    // In case result vreg is also a src vreg, break association to avoid useless copy by EvalLoc()
300    ClobberSReg(rl_dest.s_reg_low);
301    rl_result = EvalLoc(rl_dest, kCoreReg, true);
302    LoadConstant(rl_result.reg, default_result);
303    NewLIR2(kThumb2Vcmpd, S2d(rl_src1.reg.GetLowReg(), rl_src2.reg.GetHighReg()),
304            S2d(rl_src2.reg.GetLowReg(), rl_src2.reg.GetHighReg()));
305  } else {
306    rl_src1 = LoadValue(rl_src1, kFPReg);
307    rl_src2 = LoadValue(rl_src2, kFPReg);
308    // In case result vreg is also a srcvreg, break association to avoid useless copy by EvalLoc()
309    ClobberSReg(rl_dest.s_reg_low);
310    rl_result = EvalLoc(rl_dest, kCoreReg, true);
311    LoadConstant(rl_result.reg, default_result);
312    NewLIR2(kThumb2Vcmps, rl_src1.reg.GetReg(), rl_src2.reg.GetReg());
313  }
314  DCHECK(!ARM_FPREG(rl_result.reg.GetReg()));
315  NewLIR0(kThumb2Fmstat);
316
317  OpIT((default_result == -1) ? kCondGt : kCondMi, "");
318  NewLIR2(kThumb2MovI8M, rl_result.reg.GetReg(),
319          ModifiedImmediate(-default_result));  // Must not alter ccodes
320  GenBarrier();
321
322  OpIT(kCondEq, "");
323  LoadConstant(rl_result.reg, 0);
324  GenBarrier();
325
326  StoreValue(rl_dest, rl_result);
327}
328
329void ArmMir2Lir::GenNegFloat(RegLocation rl_dest, RegLocation rl_src) {
330  RegLocation rl_result;
331  rl_src = LoadValue(rl_src, kFPReg);
332  rl_result = EvalLoc(rl_dest, kFPReg, true);
333  NewLIR2(kThumb2Vnegs, rl_result.reg.GetReg(), rl_src.reg.GetReg());
334  StoreValue(rl_dest, rl_result);
335}
336
337void ArmMir2Lir::GenNegDouble(RegLocation rl_dest, RegLocation rl_src) {
338  RegLocation rl_result;
339  rl_src = LoadValueWide(rl_src, kFPReg);
340  rl_result = EvalLoc(rl_dest, kFPReg, true);
341  NewLIR2(kThumb2Vnegd, S2d(rl_result.reg.GetLowReg(), rl_result.reg.GetHighReg()),
342          S2d(rl_src.reg.GetLowReg(), rl_src.reg.GetHighReg()));
343  StoreValueWide(rl_dest, rl_result);
344}
345
346bool ArmMir2Lir::GenInlinedSqrt(CallInfo* info) {
347  DCHECK_EQ(cu_->instruction_set, kThumb2);
348  LIR *branch;
349  RegLocation rl_src = info->args[0];
350  RegLocation rl_dest = InlineTargetWide(info);  // double place for result
351  rl_src = LoadValueWide(rl_src, kFPReg);
352  RegLocation rl_result = EvalLoc(rl_dest, kFPReg, true);
353  // TODO: shouldn't need S2d once 64bitSolo has proper double tag bit.
354  NewLIR2(kThumb2Vsqrtd, S2d(rl_result.reg.GetLowReg(), rl_result.reg.GetHighReg()),
355          S2d(rl_src.reg.GetLowReg(), rl_src.reg.GetHighReg()));
356  NewLIR2(kThumb2Vcmpd, S2d(rl_result.reg.GetLowReg(), rl_result.reg.GetHighReg()),
357          S2d(rl_result.reg.GetLowReg(), rl_result.reg.GetHighReg()));
358  NewLIR0(kThumb2Fmstat);
359  branch = NewLIR2(kThumbBCond, 0, kArmCondEq);
360  ClobberCallerSave();
361  LockCallTemps();  // Using fixed registers
362  RegStorage r_tgt = LoadHelper(QUICK_ENTRYPOINT_OFFSET(pSqrt));
363  NewLIR3(kThumb2Fmrrd, r0, r1, S2d(rl_src.reg.GetLowReg(), rl_src.reg.GetHighReg()));
364  NewLIR1(kThumbBlxR, r_tgt.GetReg());
365  NewLIR3(kThumb2Fmdrr, S2d(rl_result.reg.GetLowReg(), rl_result.reg.GetHighReg()), r0, r1);
366  branch->target = NewLIR0(kPseudoTargetLabel);
367  StoreValueWide(rl_dest, rl_result);
368  return true;
369}
370
371
372}  // namespace art
373