fp_arm.cc revision 2c1ed456dcdb027d097825dd98dbe48c71599b6c
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.GetReg(), rl_result.reg.GetHighReg()), S2d(rl_src1.reg.GetReg(), rl_src1.reg.GetHighReg()),
115          S2d(rl_src2.reg.GetReg(), 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 = kThumb2VcvtID;
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.GetReg(), rl_src.reg.GetHighReg());
147      rl_result = EvalLoc(rl_dest, kFPReg, true);
148      // TODO: clean up AllocTempDouble so that its result has the double bits set.
149      int tmp1 = AllocTempDouble();
150      int tmp2 = AllocTempDouble();
151
152      NewLIR2(kThumb2VcvtF64S32, tmp1 | ARM_FP_DOUBLE, (src_reg & ~ARM_FP_DOUBLE) + 1);
153      NewLIR2(kThumb2VcvtF64U32, S2d(rl_result.reg.GetReg(), rl_result.reg.GetHighReg()), (src_reg & ~ARM_FP_DOUBLE));
154      LoadConstantWide(tmp2, tmp2 + 1, 0x41f0000000000000LL);
155      NewLIR3(kThumb2VmlaF64, S2d(rl_result.reg.GetReg(), rl_result.reg.GetHighReg()), tmp1 | ARM_FP_DOUBLE,
156              tmp2 | ARM_FP_DOUBLE);
157      FreeTemp(tmp1);
158      FreeTemp(tmp2);
159      StoreValueWide(rl_dest, rl_result);
160      return;
161    }
162    case Instruction::FLOAT_TO_LONG:
163      GenConversionCall(QUICK_ENTRYPOINT_OFFSET(pF2l), rl_dest, rl_src);
164      return;
165    case Instruction::LONG_TO_FLOAT:
166      GenConversionCall(QUICK_ENTRYPOINT_OFFSET(pL2f), rl_dest, rl_src);
167      return;
168    case Instruction::DOUBLE_TO_LONG:
169      GenConversionCall(QUICK_ENTRYPOINT_OFFSET(pD2l), rl_dest, rl_src);
170      return;
171    default:
172      LOG(FATAL) << "Unexpected opcode: " << opcode;
173  }
174  if (rl_src.wide) {
175    rl_src = LoadValueWide(rl_src, kFPReg);
176    src_reg = S2d(rl_src.reg.GetReg(), rl_src.reg.GetHighReg());
177  } else {
178    rl_src = LoadValue(rl_src, kFPReg);
179    src_reg = rl_src.reg.GetReg();
180  }
181  if (rl_dest.wide) {
182    rl_result = EvalLoc(rl_dest, kFPReg, true);
183    NewLIR2(op, S2d(rl_result.reg.GetReg(), rl_result.reg.GetHighReg()), src_reg);
184    StoreValueWide(rl_dest, rl_result);
185  } else {
186    rl_result = EvalLoc(rl_dest, kFPReg, true);
187    NewLIR2(op, rl_result.reg.GetReg(), src_reg);
188    StoreValue(rl_dest, rl_result);
189  }
190}
191
192void ArmMir2Lir::GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias,
193                                     bool is_double) {
194  LIR* target = &block_label_list_[bb->taken];
195  RegLocation rl_src1;
196  RegLocation rl_src2;
197  if (is_double) {
198    rl_src1 = mir_graph_->GetSrcWide(mir, 0);
199    rl_src2 = mir_graph_->GetSrcWide(mir, 2);
200    rl_src1 = LoadValueWide(rl_src1, kFPReg);
201    rl_src2 = LoadValueWide(rl_src2, kFPReg);
202    NewLIR2(kThumb2Vcmpd, S2d(rl_src1.reg.GetReg(), rl_src2.reg.GetHighReg()),
203            S2d(rl_src2.reg.GetReg(), rl_src2.reg.GetHighReg()));
204  } else {
205    rl_src1 = mir_graph_->GetSrc(mir, 0);
206    rl_src2 = mir_graph_->GetSrc(mir, 1);
207    rl_src1 = LoadValue(rl_src1, kFPReg);
208    rl_src2 = LoadValue(rl_src2, kFPReg);
209    NewLIR2(kThumb2Vcmps, rl_src1.reg.GetReg(), rl_src2.reg.GetReg());
210  }
211  NewLIR0(kThumb2Fmstat);
212  ConditionCode ccode = mir->meta.ccode;
213  switch (ccode) {
214    case kCondEq:
215    case kCondNe:
216      break;
217    case kCondLt:
218      if (gt_bias) {
219        ccode = kCondMi;
220      }
221      break;
222    case kCondLe:
223      if (gt_bias) {
224        ccode = kCondLs;
225      }
226      break;
227    case kCondGt:
228      if (gt_bias) {
229        ccode = kCondHi;
230      }
231      break;
232    case kCondGe:
233      if (gt_bias) {
234        ccode = kCondUge;
235      }
236      break;
237    default:
238      LOG(FATAL) << "Unexpected ccode: " << ccode;
239  }
240  OpCondBranch(ccode, target);
241}
242
243
244void ArmMir2Lir::GenCmpFP(Instruction::Code opcode, RegLocation rl_dest,
245                          RegLocation rl_src1, RegLocation rl_src2) {
246  bool is_double = false;
247  int default_result = -1;
248  RegLocation rl_result;
249
250  switch (opcode) {
251    case Instruction::CMPL_FLOAT:
252      is_double = false;
253      default_result = -1;
254      break;
255    case Instruction::CMPG_FLOAT:
256      is_double = false;
257      default_result = 1;
258      break;
259    case Instruction::CMPL_DOUBLE:
260      is_double = true;
261      default_result = -1;
262      break;
263    case Instruction::CMPG_DOUBLE:
264      is_double = true;
265      default_result = 1;
266      break;
267    default:
268      LOG(FATAL) << "Unexpected opcode: " << opcode;
269  }
270  if (is_double) {
271    rl_src1 = LoadValueWide(rl_src1, kFPReg);
272    rl_src2 = LoadValueWide(rl_src2, kFPReg);
273    // In case result vreg is also a src vreg, break association to avoid useless copy by EvalLoc()
274    ClobberSReg(rl_dest.s_reg_low);
275    rl_result = EvalLoc(rl_dest, kCoreReg, true);
276    LoadConstant(rl_result.reg.GetReg(), default_result);
277    NewLIR2(kThumb2Vcmpd, S2d(rl_src1.reg.GetReg(), rl_src2.reg.GetHighReg()),
278            S2d(rl_src2.reg.GetReg(), rl_src2.reg.GetHighReg()));
279  } else {
280    rl_src1 = LoadValue(rl_src1, kFPReg);
281    rl_src2 = LoadValue(rl_src2, kFPReg);
282    // In case result vreg is also a srcvreg, break association to avoid useless copy by EvalLoc()
283    ClobberSReg(rl_dest.s_reg_low);
284    rl_result = EvalLoc(rl_dest, kCoreReg, true);
285    LoadConstant(rl_result.reg.GetReg(), default_result);
286    NewLIR2(kThumb2Vcmps, rl_src1.reg.GetReg(), rl_src2.reg.GetReg());
287  }
288  DCHECK(!ARM_FPREG(rl_result.reg.GetReg()));
289  NewLIR0(kThumb2Fmstat);
290
291  OpIT((default_result == -1) ? kCondGt : kCondMi, "");
292  NewLIR2(kThumb2MovI8M, rl_result.reg.GetReg(),
293          ModifiedImmediate(-default_result));  // Must not alter ccodes
294  GenBarrier();
295
296  OpIT(kCondEq, "");
297  LoadConstant(rl_result.reg.GetReg(), 0);
298  GenBarrier();
299
300  StoreValue(rl_dest, rl_result);
301}
302
303void ArmMir2Lir::GenNegFloat(RegLocation rl_dest, RegLocation rl_src) {
304  RegLocation rl_result;
305  rl_src = LoadValue(rl_src, kFPReg);
306  rl_result = EvalLoc(rl_dest, kFPReg, true);
307  NewLIR2(kThumb2Vnegs, rl_result.reg.GetReg(), rl_src.reg.GetReg());
308  StoreValue(rl_dest, rl_result);
309}
310
311void ArmMir2Lir::GenNegDouble(RegLocation rl_dest, RegLocation rl_src) {
312  RegLocation rl_result;
313  rl_src = LoadValueWide(rl_src, kFPReg);
314  rl_result = EvalLoc(rl_dest, kFPReg, true);
315  NewLIR2(kThumb2Vnegd, S2d(rl_result.reg.GetReg(), rl_result.reg.GetHighReg()),
316          S2d(rl_src.reg.GetReg(), rl_src.reg.GetHighReg()));
317  StoreValueWide(rl_dest, rl_result);
318}
319
320bool ArmMir2Lir::GenInlinedSqrt(CallInfo* info) {
321  DCHECK_EQ(cu_->instruction_set, kThumb2);
322  LIR *branch;
323  RegLocation rl_src = info->args[0];
324  RegLocation rl_dest = InlineTargetWide(info);  // double place for result
325  rl_src = LoadValueWide(rl_src, kFPReg);
326  RegLocation rl_result = EvalLoc(rl_dest, kFPReg, true);
327  NewLIR2(kThumb2Vsqrtd, S2d(rl_result.reg.GetReg(), rl_result.reg.GetHighReg()),
328          S2d(rl_src.reg.GetReg(), rl_src.reg.GetHighReg()));
329  NewLIR2(kThumb2Vcmpd, S2d(rl_result.reg.GetReg(), rl_result.reg.GetHighReg()),
330          S2d(rl_result.reg.GetReg(), rl_result.reg.GetHighReg()));
331  NewLIR0(kThumb2Fmstat);
332  branch = NewLIR2(kThumbBCond, 0, kArmCondEq);
333  ClobberCallerSave();
334  LockCallTemps();  // Using fixed registers
335  int r_tgt = LoadHelper(QUICK_ENTRYPOINT_OFFSET(pSqrt));
336  NewLIR3(kThumb2Fmrrd, r0, r1, S2d(rl_src.reg.GetReg(), rl_src.reg.GetHighReg()));
337  NewLIR1(kThumbBlxR, r_tgt);
338  NewLIR3(kThumb2Fmdrr, S2d(rl_result.reg.GetReg(), rl_result.reg.GetHighReg()), r0, r1);
339  branch->target = NewLIR0(kPseudoTargetLabel);
340  StoreValueWide(rl_dest, rl_result);
341  return true;
342}
343
344
345}  // namespace art
346