fp_arm.cc revision f9d6aede77c700118e225f8312cd888262b77862
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(4, pFmodf), rl_src1, rl_src2,
53                                              false);
54      rl_result = GetReturn(kFPReg);
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(4, pFmod), rl_src1, rl_src2,
96                                              false);
97      rl_result = GetReturnWide(kFPReg);
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, rl_result.reg.GetReg(), rl_src1.reg.GetReg(), rl_src2.reg.GetReg());
115  StoreValueWide(rl_dest, rl_result);
116}
117
118void ArmMir2Lir::GenConversion(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src) {
119  int op = kThumbBkpt;
120  int src_reg;
121  RegLocation rl_result;
122
123  switch (opcode) {
124    case Instruction::INT_TO_FLOAT:
125      op = kThumb2VcvtIF;
126      break;
127    case Instruction::FLOAT_TO_INT:
128      op = kThumb2VcvtFI;
129      break;
130    case Instruction::DOUBLE_TO_FLOAT:
131      op = kThumb2VcvtDF;
132      break;
133    case Instruction::FLOAT_TO_DOUBLE:
134      op = kThumb2VcvtFd;
135      break;
136    case Instruction::INT_TO_DOUBLE:
137      op = kThumb2VcvtF64S32;
138      break;
139    case Instruction::DOUBLE_TO_INT:
140      op = kThumb2VcvtDI;
141      break;
142    case Instruction::LONG_TO_DOUBLE: {
143      rl_src = LoadValueWide(rl_src, kFPReg);
144      RegisterInfo* info = GetRegInfo(rl_src.reg);
145      RegStorage src_low = info->FindMatchingView(RegisterInfo::kLowSingleStorageMask)->GetReg();
146      DCHECK(src_low.Valid());
147      RegStorage src_high = info->FindMatchingView(RegisterInfo::kHighSingleStorageMask)->GetReg();
148      DCHECK(src_high.Valid());
149      rl_result = EvalLoc(rl_dest, kFPReg, true);
150      RegStorage tmp1 = AllocTempDouble();
151      RegStorage tmp2 = AllocTempDouble();
152
153      NewLIR2(kThumb2VcvtF64S32, tmp1.GetReg(), src_high.GetReg());
154      NewLIR2(kThumb2VcvtF64U32, rl_result.reg.GetReg(), src_low.GetReg());
155      LoadConstantWide(tmp2, 0x41f0000000000000LL);
156      NewLIR3(kThumb2VmlaF64, rl_result.reg.GetReg(), tmp1.GetReg(), tmp2.GetReg());
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(4, pF2l), rl_dest, rl_src);
164      return;
165    case Instruction::LONG_TO_FLOAT: {
166      rl_src = LoadValueWide(rl_src, kFPReg);
167      RegisterInfo* info = GetRegInfo(rl_src.reg);
168      RegStorage src_low = info->FindMatchingView(RegisterInfo::kLowSingleStorageMask)->GetReg();
169      DCHECK(src_low.Valid());
170      RegStorage src_high = info->FindMatchingView(RegisterInfo::kHighSingleStorageMask)->GetReg();
171      DCHECK(src_high.Valid());
172      rl_result = EvalLoc(rl_dest, kFPReg, true);
173      // Allocate temp registers.
174      RegStorage high_val = AllocTempDouble();
175      RegStorage low_val = AllocTempDouble();
176      RegStorage const_val = AllocTempDouble();
177      // Long to double.
178      NewLIR2(kThumb2VcvtF64S32, high_val.GetReg(), src_high.GetReg());
179      NewLIR2(kThumb2VcvtF64U32, low_val.GetReg(), src_low.GetReg());
180      LoadConstantWide(const_val, INT64_C(0x41f0000000000000));
181      NewLIR3(kThumb2VmlaF64, low_val.GetReg(), high_val.GetReg(), const_val.GetReg());
182      // Double to float.
183      NewLIR2(kThumb2VcvtDF, rl_result.reg.GetReg(), low_val.GetReg());
184      // Free temp registers.
185      FreeTemp(high_val);
186      FreeTemp(low_val);
187      FreeTemp(const_val);
188      // Store result.
189      StoreValue(rl_dest, rl_result);
190      return;
191    }
192    case Instruction::DOUBLE_TO_LONG:
193      GenConversionCall(QUICK_ENTRYPOINT_OFFSET(4, pD2l), rl_dest, rl_src);
194      return;
195    default:
196      LOG(FATAL) << "Unexpected opcode: " << opcode;
197  }
198  if (rl_src.wide) {
199    rl_src = LoadValueWide(rl_src, kFPReg);
200    src_reg = rl_src.reg.GetReg();
201  } else {
202    rl_src = LoadValue(rl_src, kFPReg);
203    src_reg = rl_src.reg.GetReg();
204  }
205  if (rl_dest.wide) {
206    rl_result = EvalLoc(rl_dest, kFPReg, true);
207    NewLIR2(op, rl_result.reg.GetReg(), src_reg);
208    StoreValueWide(rl_dest, rl_result);
209  } else {
210    rl_result = EvalLoc(rl_dest, kFPReg, true);
211    NewLIR2(op, rl_result.reg.GetReg(), src_reg);
212    StoreValue(rl_dest, rl_result);
213  }
214}
215
216void ArmMir2Lir::GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias,
217                                     bool is_double) {
218  LIR* target = &block_label_list_[bb->taken];
219  RegLocation rl_src1;
220  RegLocation rl_src2;
221  if (is_double) {
222    rl_src1 = mir_graph_->GetSrcWide(mir, 0);
223    rl_src2 = mir_graph_->GetSrcWide(mir, 2);
224    rl_src1 = LoadValueWide(rl_src1, kFPReg);
225    rl_src2 = LoadValueWide(rl_src2, kFPReg);
226    NewLIR2(kThumb2Vcmpd, rl_src1.reg.GetReg(), rl_src2.reg.GetReg());
227  } else {
228    rl_src1 = mir_graph_->GetSrc(mir, 0);
229    rl_src2 = mir_graph_->GetSrc(mir, 1);
230    rl_src1 = LoadValue(rl_src1, kFPReg);
231    rl_src2 = LoadValue(rl_src2, kFPReg);
232    NewLIR2(kThumb2Vcmps, rl_src1.reg.GetReg(), rl_src2.reg.GetReg());
233  }
234  NewLIR0(kThumb2Fmstat);
235  ConditionCode ccode = mir->meta.ccode;
236  switch (ccode) {
237    case kCondEq:
238    case kCondNe:
239      break;
240    case kCondLt:
241      if (gt_bias) {
242        ccode = kCondMi;
243      }
244      break;
245    case kCondLe:
246      if (gt_bias) {
247        ccode = kCondLs;
248      }
249      break;
250    case kCondGt:
251      if (gt_bias) {
252        ccode = kCondHi;
253      }
254      break;
255    case kCondGe:
256      if (gt_bias) {
257        ccode = kCondUge;
258      }
259      break;
260    default:
261      LOG(FATAL) << "Unexpected ccode: " << ccode;
262  }
263  OpCondBranch(ccode, target);
264}
265
266
267void ArmMir2Lir::GenCmpFP(Instruction::Code opcode, RegLocation rl_dest,
268                          RegLocation rl_src1, RegLocation rl_src2) {
269  bool is_double = false;
270  int default_result = -1;
271  RegLocation rl_result;
272
273  switch (opcode) {
274    case Instruction::CMPL_FLOAT:
275      is_double = false;
276      default_result = -1;
277      break;
278    case Instruction::CMPG_FLOAT:
279      is_double = false;
280      default_result = 1;
281      break;
282    case Instruction::CMPL_DOUBLE:
283      is_double = true;
284      default_result = -1;
285      break;
286    case Instruction::CMPG_DOUBLE:
287      is_double = true;
288      default_result = 1;
289      break;
290    default:
291      LOG(FATAL) << "Unexpected opcode: " << opcode;
292  }
293  if (is_double) {
294    rl_src1 = LoadValueWide(rl_src1, kFPReg);
295    rl_src2 = LoadValueWide(rl_src2, kFPReg);
296    // In case result vreg is also a src vreg, break association to avoid useless copy by EvalLoc()
297    ClobberSReg(rl_dest.s_reg_low);
298    rl_result = EvalLoc(rl_dest, kCoreReg, true);
299    LoadConstant(rl_result.reg, default_result);
300    NewLIR2(kThumb2Vcmpd, rl_src1.reg.GetReg(), rl_src2.reg.GetReg());
301  } else {
302    rl_src1 = LoadValue(rl_src1, kFPReg);
303    rl_src2 = LoadValue(rl_src2, kFPReg);
304    // In case result vreg is also a srcvreg, break association to avoid useless copy by EvalLoc()
305    ClobberSReg(rl_dest.s_reg_low);
306    rl_result = EvalLoc(rl_dest, kCoreReg, true);
307    LoadConstant(rl_result.reg, default_result);
308    NewLIR2(kThumb2Vcmps, rl_src1.reg.GetReg(), rl_src2.reg.GetReg());
309  }
310  DCHECK(!rl_result.reg.IsFloat());
311  NewLIR0(kThumb2Fmstat);
312
313  LIR* it = OpIT((default_result == -1) ? kCondGt : kCondMi, "");
314  NewLIR2(kThumb2MovI8M, rl_result.reg.GetReg(),
315          ModifiedImmediate(-default_result));  // Must not alter ccodes
316  OpEndIT(it);
317
318  it = OpIT(kCondEq, "");
319  LoadConstant(rl_result.reg, 0);
320  OpEndIT(it);
321
322  StoreValue(rl_dest, rl_result);
323}
324
325void ArmMir2Lir::GenNegFloat(RegLocation rl_dest, RegLocation rl_src) {
326  RegLocation rl_result;
327  rl_src = LoadValue(rl_src, kFPReg);
328  rl_result = EvalLoc(rl_dest, kFPReg, true);
329  NewLIR2(kThumb2Vnegs, rl_result.reg.GetReg(), rl_src.reg.GetReg());
330  StoreValue(rl_dest, rl_result);
331}
332
333void ArmMir2Lir::GenNegDouble(RegLocation rl_dest, RegLocation rl_src) {
334  RegLocation rl_result;
335  rl_src = LoadValueWide(rl_src, kFPReg);
336  rl_result = EvalLoc(rl_dest, kFPReg, true);
337  NewLIR2(kThumb2Vnegd, rl_result.reg.GetReg(), rl_src.reg.GetReg());
338  StoreValueWide(rl_dest, rl_result);
339}
340
341static RegisterClass RegClassForAbsFP(RegLocation rl_src, RegLocation rl_dest) {
342  // If src is in a core reg or, unlikely, dest has been promoted to a core reg, use core reg.
343  if ((rl_src.location == kLocPhysReg && !rl_src.reg.IsFloat()) ||
344      (rl_dest.location == kLocPhysReg && !rl_dest.reg.IsFloat())) {
345    return kCoreReg;
346  }
347  // If src is in an fp reg or dest has been promoted to an fp reg, use fp reg.
348  if (rl_src.location == kLocPhysReg || rl_dest.location == kLocPhysReg) {
349    return kFPReg;
350  }
351  // With both src and dest in the stack frame we have to perform load+abs+store. Whether this
352  // is faster using a core reg or fp reg depends on the particular CPU. Without further
353  // investigation and testing we prefer core register. (If the result is subsequently used in
354  // another fp operation, the dalvik reg will probably get promoted and that should be handled
355  // by the cases above.)
356  return kCoreReg;
357}
358
359bool ArmMir2Lir::GenInlinedAbsFloat(CallInfo* info) {
360  if (info->result.location == kLocInvalid) {
361    return true;  // Result is unused: inlining successful, no code generated.
362  }
363  RegLocation rl_dest = info->result;
364  RegLocation rl_src = UpdateLoc(info->args[0]);
365  RegisterClass reg_class = RegClassForAbsFP(rl_src, rl_dest);
366  rl_src = LoadValue(rl_src, reg_class);
367  RegLocation rl_result = EvalLoc(rl_dest, reg_class, true);
368  if (reg_class == kFPReg) {
369    NewLIR2(kThumb2Vabss, rl_result.reg.GetReg(), rl_src.reg.GetReg());
370  } else {
371    OpRegRegImm(kOpAnd, rl_result.reg, rl_src.reg, 0x7fffffff);
372  }
373  StoreValue(rl_dest, rl_result);
374  return true;
375}
376
377bool ArmMir2Lir::GenInlinedAbsDouble(CallInfo* info) {
378  if (info->result.location == kLocInvalid) {
379    return true;  // Result is unused: inlining successful, no code generated.
380  }
381  RegLocation rl_dest = info->result;
382  RegLocation rl_src = UpdateLocWide(info->args[0]);
383  RegisterClass reg_class = RegClassForAbsFP(rl_src, rl_dest);
384  rl_src = LoadValueWide(rl_src, reg_class);
385  RegLocation rl_result = EvalLoc(rl_dest, reg_class, true);
386  if (reg_class == kFPReg) {
387    NewLIR2(kThumb2Vabsd, rl_result.reg.GetReg(), rl_src.reg.GetReg());
388  } else {
389    OpRegImm(kOpAnd, rl_result.reg.GetHigh(), 0x7fffffff);
390  }
391  StoreValueWide(rl_dest, rl_result);
392  return true;
393}
394
395bool ArmMir2Lir::GenInlinedSqrt(CallInfo* info) {
396  DCHECK_EQ(cu_->instruction_set, kThumb2);
397  RegLocation rl_src = info->args[0];
398  RegLocation rl_dest = InlineTargetWide(info);  // double place for result
399  rl_src = LoadValueWide(rl_src, kFPReg);
400  RegLocation rl_result = EvalLoc(rl_dest, kFPReg, true);
401  NewLIR2(kThumb2Vsqrtd, rl_result.reg.GetReg(), rl_src.reg.GetReg());
402  StoreValueWide(rl_dest, rl_result);
403  return true;
404}
405
406
407}  // namespace art
408