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 "codegen_arm.h"
18
19#include "arm_lir.h"
20#include "base/logging.h"
21#include "dex/mir_graph.h"
22#include "dex/quick/mir_to_lir-inl.h"
23
24namespace art {
25
26void ArmMir2Lir::GenArithOpFloat(Instruction::Code opcode, RegLocation rl_dest,
27                                 RegLocation rl_src1, RegLocation rl_src2) {
28  int op = kThumbBkpt;
29  RegLocation rl_result;
30
31  /*
32   * Don't attempt to optimize register usage since these opcodes call out to
33   * the handlers.
34   */
35  switch (opcode) {
36    case Instruction::ADD_FLOAT_2ADDR:
37    case Instruction::ADD_FLOAT:
38      op = kThumb2Vadds;
39      break;
40    case Instruction::SUB_FLOAT_2ADDR:
41    case Instruction::SUB_FLOAT:
42      op = kThumb2Vsubs;
43      break;
44    case Instruction::DIV_FLOAT_2ADDR:
45    case Instruction::DIV_FLOAT:
46      op = kThumb2Vdivs;
47      break;
48    case Instruction::MUL_FLOAT_2ADDR:
49    case Instruction::MUL_FLOAT:
50      op = kThumb2Vmuls;
51      break;
52    case Instruction::REM_FLOAT_2ADDR:
53    case Instruction::REM_FLOAT:
54      FlushAllRegs();   // Send everything to home location
55      CallRuntimeHelperRegLocationRegLocation(kQuickFmodf, rl_src1, rl_src2, false);
56      rl_result = GetReturn(kFPReg);
57      StoreValue(rl_dest, rl_result);
58      return;
59    case Instruction::NEG_FLOAT:
60      GenNegFloat(rl_dest, rl_src1);
61      return;
62    default:
63      LOG(FATAL) << "Unexpected opcode: " << opcode;
64  }
65  rl_src1 = LoadValue(rl_src1, kFPReg);
66  rl_src2 = LoadValue(rl_src2, kFPReg);
67  rl_result = EvalLoc(rl_dest, kFPReg, true);
68  NewLIR3(op, rl_result.reg.GetReg(), rl_src1.reg.GetReg(), rl_src2.reg.GetReg());
69  StoreValue(rl_dest, rl_result);
70}
71
72void ArmMir2Lir::GenArithOpDouble(Instruction::Code opcode,
73                                  RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) {
74  int op = kThumbBkpt;
75  RegLocation rl_result;
76
77  switch (opcode) {
78    case Instruction::ADD_DOUBLE_2ADDR:
79    case Instruction::ADD_DOUBLE:
80      op = kThumb2Vaddd;
81      break;
82    case Instruction::SUB_DOUBLE_2ADDR:
83    case Instruction::SUB_DOUBLE:
84      op = kThumb2Vsubd;
85      break;
86    case Instruction::DIV_DOUBLE_2ADDR:
87    case Instruction::DIV_DOUBLE:
88      op = kThumb2Vdivd;
89      break;
90    case Instruction::MUL_DOUBLE_2ADDR:
91    case Instruction::MUL_DOUBLE:
92      op = kThumb2Vmuld;
93      break;
94    case Instruction::REM_DOUBLE_2ADDR:
95    case Instruction::REM_DOUBLE:
96      FlushAllRegs();   // Send everything to home location
97      CallRuntimeHelperRegLocationRegLocation(kQuickFmod, rl_src1, rl_src2, false);
98      rl_result = GetReturnWide(kFPReg);
99      StoreValueWide(rl_dest, rl_result);
100      return;
101    case Instruction::NEG_DOUBLE:
102      GenNegDouble(rl_dest, rl_src1);
103      return;
104    default:
105      LOG(FATAL) << "Unexpected opcode: " << opcode;
106  }
107
108  rl_src1 = LoadValueWide(rl_src1, kFPReg);
109  DCHECK(rl_src1.wide);
110  rl_src2 = LoadValueWide(rl_src2, kFPReg);
111  DCHECK(rl_src2.wide);
112  rl_result = EvalLoc(rl_dest, kFPReg, true);
113  DCHECK(rl_dest.wide);
114  DCHECK(rl_result.wide);
115  NewLIR3(op, rl_result.reg.GetReg(), rl_src1.reg.GetReg(), rl_src2.reg.GetReg());
116  StoreValueWide(rl_dest, rl_result);
117}
118
119void ArmMir2Lir::GenMultiplyByConstantFloat(RegLocation rl_dest, RegLocation rl_src1,
120                                            int32_t constant) {
121  RegLocation rl_result;
122  RegStorage r_tmp = AllocTempSingle();
123  LoadConstantNoClobber(r_tmp, constant);
124  rl_src1 = LoadValue(rl_src1, kFPReg);
125  rl_result = EvalLoc(rl_dest, kFPReg, true);
126  NewLIR3(kThumb2Vmuls, rl_result.reg.GetReg(), rl_src1.reg.GetReg(), r_tmp.GetReg());
127  StoreValue(rl_dest, rl_result);
128}
129
130void ArmMir2Lir::GenMultiplyByConstantDouble(RegLocation rl_dest, RegLocation rl_src1,
131                                             int64_t constant) {
132  RegLocation rl_result;
133  RegStorage r_tmp = AllocTempDouble();
134  DCHECK(r_tmp.IsDouble());
135  LoadConstantWide(r_tmp, constant);
136  rl_src1 = LoadValueWide(rl_src1, kFPReg);
137  DCHECK(rl_src1.wide);
138  rl_result = EvalLocWide(rl_dest, kFPReg, true);
139  DCHECK(rl_dest.wide);
140  DCHECK(rl_result.wide);
141  NewLIR3(kThumb2Vmuld, rl_result.reg.GetReg(), rl_src1.reg.GetReg(), r_tmp.GetReg());
142  StoreValueWide(rl_dest, rl_result);
143}
144
145void ArmMir2Lir::GenConversion(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src) {
146  int op = kThumbBkpt;
147  int src_reg;
148  RegLocation rl_result;
149
150  switch (opcode) {
151    case Instruction::INT_TO_FLOAT:
152      op = kThumb2VcvtIF;
153      break;
154    case Instruction::FLOAT_TO_INT:
155      op = kThumb2VcvtFI;
156      break;
157    case Instruction::DOUBLE_TO_FLOAT:
158      op = kThumb2VcvtDF;
159      break;
160    case Instruction::FLOAT_TO_DOUBLE:
161      op = kThumb2VcvtFd;
162      break;
163    case Instruction::INT_TO_DOUBLE:
164      op = kThumb2VcvtF64S32;
165      break;
166    case Instruction::DOUBLE_TO_INT:
167      op = kThumb2VcvtDI;
168      break;
169    case Instruction::LONG_TO_DOUBLE: {
170      rl_src = LoadValueWide(rl_src, kFPReg);
171      RegisterInfo* info = GetRegInfo(rl_src.reg);
172      RegStorage src_low = info->FindMatchingView(RegisterInfo::kLowSingleStorageMask)->GetReg();
173      DCHECK(src_low.Valid());
174      RegStorage src_high = info->FindMatchingView(RegisterInfo::kHighSingleStorageMask)->GetReg();
175      DCHECK(src_high.Valid());
176      rl_result = EvalLoc(rl_dest, kFPReg, true);
177      RegStorage tmp1 = AllocTempDouble();
178      RegStorage tmp2 = AllocTempDouble();
179
180      NewLIR2(kThumb2VcvtF64S32, tmp1.GetReg(), src_high.GetReg());
181      NewLIR2(kThumb2VcvtF64U32, rl_result.reg.GetReg(), src_low.GetReg());
182      LoadConstantWide(tmp2, 0x41f0000000000000LL);
183      NewLIR3(kThumb2VmlaF64, rl_result.reg.GetReg(), tmp1.GetReg(), tmp2.GetReg());
184      FreeTemp(tmp1);
185      FreeTemp(tmp2);
186      StoreValueWide(rl_dest, rl_result);
187      return;
188    }
189    case Instruction::FLOAT_TO_LONG:
190      CheckEntrypointTypes<kQuickF2l, int64_t, float>();  // int64_t -> kCoreReg
191      GenConversionCall(kQuickF2l, rl_dest, rl_src, kCoreReg);
192      return;
193    case Instruction::LONG_TO_FLOAT: {
194      rl_src = LoadValueWide(rl_src, kFPReg);
195      RegisterInfo* info = GetRegInfo(rl_src.reg);
196      RegStorage src_low = info->FindMatchingView(RegisterInfo::kLowSingleStorageMask)->GetReg();
197      DCHECK(src_low.Valid());
198      RegStorage src_high = info->FindMatchingView(RegisterInfo::kHighSingleStorageMask)->GetReg();
199      DCHECK(src_high.Valid());
200      rl_result = EvalLoc(rl_dest, kFPReg, true);
201      // Allocate temp registers.
202      RegStorage high_val = AllocTempDouble();
203      RegStorage low_val = AllocTempDouble();
204      RegStorage const_val = AllocTempDouble();
205      // Long to double.
206      NewLIR2(kThumb2VcvtF64S32, high_val.GetReg(), src_high.GetReg());
207      NewLIR2(kThumb2VcvtF64U32, low_val.GetReg(), src_low.GetReg());
208      LoadConstantWide(const_val, INT64_C(0x41f0000000000000));
209      NewLIR3(kThumb2VmlaF64, low_val.GetReg(), high_val.GetReg(), const_val.GetReg());
210      // Double to float.
211      NewLIR2(kThumb2VcvtDF, rl_result.reg.GetReg(), low_val.GetReg());
212      // Free temp registers.
213      FreeTemp(high_val);
214      FreeTemp(low_val);
215      FreeTemp(const_val);
216      // Store result.
217      StoreValue(rl_dest, rl_result);
218      return;
219    }
220    case Instruction::DOUBLE_TO_LONG:
221      CheckEntrypointTypes<kQuickD2l, int64_t, double>();  // int64_t -> kCoreReg
222      GenConversionCall(kQuickD2l, rl_dest, rl_src, kCoreReg);
223      return;
224    default:
225      LOG(FATAL) << "Unexpected opcode: " << opcode;
226  }
227  if (rl_src.wide) {
228    rl_src = LoadValueWide(rl_src, kFPReg);
229    src_reg = rl_src.reg.GetReg();
230  } else {
231    rl_src = LoadValue(rl_src, kFPReg);
232    src_reg = rl_src.reg.GetReg();
233  }
234  if (rl_dest.wide) {
235    rl_result = EvalLoc(rl_dest, kFPReg, true);
236    NewLIR2(op, rl_result.reg.GetReg(), src_reg);
237    StoreValueWide(rl_dest, rl_result);
238  } else {
239    rl_result = EvalLoc(rl_dest, kFPReg, true);
240    NewLIR2(op, rl_result.reg.GetReg(), src_reg);
241    StoreValue(rl_dest, rl_result);
242  }
243}
244
245void ArmMir2Lir::GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias,
246                                     bool is_double) {
247  LIR* target = &block_label_list_[bb->taken];
248  RegLocation rl_src1;
249  RegLocation rl_src2;
250  if (is_double) {
251    rl_src1 = mir_graph_->GetSrcWide(mir, 0);
252    rl_src2 = mir_graph_->GetSrcWide(mir, 2);
253    rl_src1 = LoadValueWide(rl_src1, kFPReg);
254    rl_src2 = LoadValueWide(rl_src2, kFPReg);
255    NewLIR2(kThumb2Vcmpd, rl_src1.reg.GetReg(), rl_src2.reg.GetReg());
256  } else {
257    rl_src1 = mir_graph_->GetSrc(mir, 0);
258    rl_src2 = mir_graph_->GetSrc(mir, 1);
259    rl_src1 = LoadValue(rl_src1, kFPReg);
260    rl_src2 = LoadValue(rl_src2, kFPReg);
261    NewLIR2(kThumb2Vcmps, rl_src1.reg.GetReg(), rl_src2.reg.GetReg());
262  }
263  NewLIR0(kThumb2Fmstat);
264  ConditionCode ccode = mir->meta.ccode;
265  switch (ccode) {
266    case kCondEq:
267    case kCondNe:
268      break;
269    case kCondLt:
270      if (gt_bias) {
271        ccode = kCondMi;
272      }
273      break;
274    case kCondLe:
275      if (gt_bias) {
276        ccode = kCondLs;
277      }
278      break;
279    case kCondGt:
280      if (gt_bias) {
281        ccode = kCondHi;
282      }
283      break;
284    case kCondGe:
285      if (gt_bias) {
286        ccode = kCondUge;
287      }
288      break;
289    default:
290      LOG(FATAL) << "Unexpected ccode: " << ccode;
291  }
292  OpCondBranch(ccode, target);
293}
294
295
296void ArmMir2Lir::GenCmpFP(Instruction::Code opcode, RegLocation rl_dest,
297                          RegLocation rl_src1, RegLocation rl_src2) {
298  bool is_double = false;
299  int default_result = -1;
300  RegLocation rl_result;
301
302  switch (opcode) {
303    case Instruction::CMPL_FLOAT:
304      is_double = false;
305      default_result = -1;
306      break;
307    case Instruction::CMPG_FLOAT:
308      is_double = false;
309      default_result = 1;
310      break;
311    case Instruction::CMPL_DOUBLE:
312      is_double = true;
313      default_result = -1;
314      break;
315    case Instruction::CMPG_DOUBLE:
316      is_double = true;
317      default_result = 1;
318      break;
319    default:
320      LOG(FATAL) << "Unexpected opcode: " << opcode;
321  }
322  if (is_double) {
323    rl_src1 = LoadValueWide(rl_src1, kFPReg);
324    rl_src2 = LoadValueWide(rl_src2, kFPReg);
325    // In case result vreg is also a src vreg, break association to avoid useless copy by EvalLoc()
326    ClobberSReg(rl_dest.s_reg_low);
327    rl_result = EvalLoc(rl_dest, kCoreReg, true);
328    LoadConstant(rl_result.reg, default_result);
329    NewLIR2(kThumb2Vcmpd, rl_src1.reg.GetReg(), rl_src2.reg.GetReg());
330  } else {
331    rl_src1 = LoadValue(rl_src1, kFPReg);
332    rl_src2 = LoadValue(rl_src2, kFPReg);
333    // In case result vreg is also a srcvreg, break association to avoid useless copy by EvalLoc()
334    ClobberSReg(rl_dest.s_reg_low);
335    rl_result = EvalLoc(rl_dest, kCoreReg, true);
336    LoadConstant(rl_result.reg, default_result);
337    NewLIR2(kThumb2Vcmps, rl_src1.reg.GetReg(), rl_src2.reg.GetReg());
338  }
339  DCHECK(!rl_result.reg.IsFloat());
340  NewLIR0(kThumb2Fmstat);
341
342  LIR* it = OpIT((default_result == -1) ? kCondGt : kCondMi, "");
343  NewLIR2(kThumb2MovI8M, rl_result.reg.GetReg(),
344          ModifiedImmediate(-default_result));  // Must not alter ccodes
345  OpEndIT(it);
346
347  it = OpIT(kCondEq, "");
348  LoadConstant(rl_result.reg, 0);
349  OpEndIT(it);
350
351  StoreValue(rl_dest, rl_result);
352}
353
354void ArmMir2Lir::GenNegFloat(RegLocation rl_dest, RegLocation rl_src) {
355  RegLocation rl_result;
356  rl_src = LoadValue(rl_src, kFPReg);
357  rl_result = EvalLoc(rl_dest, kFPReg, true);
358  NewLIR2(kThumb2Vnegs, rl_result.reg.GetReg(), rl_src.reg.GetReg());
359  StoreValue(rl_dest, rl_result);
360}
361
362void ArmMir2Lir::GenNegDouble(RegLocation rl_dest, RegLocation rl_src) {
363  RegLocation rl_result;
364  rl_src = LoadValueWide(rl_src, kFPReg);
365  rl_result = EvalLoc(rl_dest, kFPReg, true);
366  NewLIR2(kThumb2Vnegd, rl_result.reg.GetReg(), rl_src.reg.GetReg());
367  StoreValueWide(rl_dest, rl_result);
368}
369
370static RegisterClass RegClassForAbsFP(RegLocation rl_src, RegLocation rl_dest) {
371  // If src is in a core reg or, unlikely, dest has been promoted to a core reg, use core reg.
372  if ((rl_src.location == kLocPhysReg && !rl_src.reg.IsFloat()) ||
373      (rl_dest.location == kLocPhysReg && !rl_dest.reg.IsFloat())) {
374    return kCoreReg;
375  }
376  // If src is in an fp reg or dest has been promoted to an fp reg, use fp reg.
377  if (rl_src.location == kLocPhysReg || rl_dest.location == kLocPhysReg) {
378    return kFPReg;
379  }
380  // With both src and dest in the stack frame we have to perform load+abs+store. Whether this
381  // is faster using a core reg or fp reg depends on the particular CPU. Without further
382  // investigation and testing we prefer core register. (If the result is subsequently used in
383  // another fp operation, the dalvik reg will probably get promoted and that should be handled
384  // by the cases above.)
385  return kCoreReg;
386}
387
388bool ArmMir2Lir::GenInlinedAbsFloat(CallInfo* info) {
389  if (info->result.location == kLocInvalid) {
390    return true;  // Result is unused: inlining successful, no code generated.
391  }
392  RegLocation rl_dest = info->result;
393  RegLocation rl_src = UpdateLoc(info->args[0]);
394  RegisterClass reg_class = RegClassForAbsFP(rl_src, rl_dest);
395  rl_src = LoadValue(rl_src, reg_class);
396  RegLocation rl_result = EvalLoc(rl_dest, reg_class, true);
397  if (reg_class == kFPReg) {
398    NewLIR2(kThumb2Vabss, rl_result.reg.GetReg(), rl_src.reg.GetReg());
399  } else {
400    OpRegRegImm(kOpAnd, rl_result.reg, rl_src.reg, 0x7fffffff);
401  }
402  StoreValue(rl_dest, rl_result);
403  return true;
404}
405
406bool ArmMir2Lir::GenInlinedAbsDouble(CallInfo* info) {
407  if (info->result.location == kLocInvalid) {
408    return true;  // Result is unused: inlining successful, no code generated.
409  }
410  RegLocation rl_dest = info->result;
411  RegLocation rl_src = UpdateLocWide(info->args[0]);
412  RegisterClass reg_class = RegClassForAbsFP(rl_src, rl_dest);
413  rl_src = LoadValueWide(rl_src, reg_class);
414  RegLocation rl_result = EvalLoc(rl_dest, reg_class, true);
415  if (reg_class == kFPReg) {
416    NewLIR2(kThumb2Vabsd, rl_result.reg.GetReg(), rl_src.reg.GetReg());
417  } else if (rl_result.reg.GetLow().GetReg() != rl_src.reg.GetHigh().GetReg()) {
418    // No inconvenient overlap.
419    OpRegCopy(rl_result.reg.GetLow(), rl_src.reg.GetLow());
420    OpRegRegImm(kOpAnd, rl_result.reg.GetHigh(), rl_src.reg.GetHigh(), 0x7fffffff);
421  } else {
422    // Inconvenient overlap, use a temp register to preserve the high word of the source.
423    RegStorage rs_tmp = AllocTemp();
424    OpRegCopy(rs_tmp, rl_src.reg.GetHigh());
425    OpRegCopy(rl_result.reg.GetLow(), rl_src.reg.GetLow());
426    OpRegRegImm(kOpAnd, rl_result.reg.GetHigh(), rs_tmp, 0x7fffffff);
427    FreeTemp(rs_tmp);
428  }
429  StoreValueWide(rl_dest, rl_result);
430  return true;
431}
432
433bool ArmMir2Lir::GenInlinedSqrt(CallInfo* info) {
434  DCHECK_EQ(cu_->instruction_set, kThumb2);
435  RegLocation rl_src = info->args[0];
436  RegLocation rl_dest = InlineTargetWide(info);  // double place for result
437  rl_src = LoadValueWide(rl_src, kFPReg);
438  RegLocation rl_result = EvalLoc(rl_dest, kFPReg, true);
439  NewLIR2(kThumb2Vsqrtd, rl_result.reg.GetReg(), rl_src.reg.GetReg());
440  StoreValueWide(rl_dest, rl_result);
441  return true;
442}
443
444
445}  // namespace art
446