blob: 96842836aa24b0cb83e464a6d85aebe5472a8e16 [file] [log] [blame]
Matteo Franchin43ec8732014-03-31 15:00:14 +01001/*
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 "arm64_lir.h"
18#include "codegen_arm64.h"
19#include "dex/quick/mir_to_lir-inl.h"
20
21namespace art {
22
23void Arm64Mir2Lir::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(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 Arm64Mir2Lir::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(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, rl_result.reg.GetReg(), rl_src1.reg.GetReg(), rl_src2.reg.GetReg());
115 StoreValueWide(rl_dest, rl_result);
116}
117
118void Arm64Mir2Lir::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 RegStorage src_low = rl_src.reg.DoubleToLowSingle();
145 RegStorage src_high = rl_src.reg.DoubleToHighSingle();
146 rl_result = EvalLoc(rl_dest, kFPReg, true);
147 RegStorage tmp1 = AllocTempDouble();
148 RegStorage tmp2 = AllocTempDouble();
149
150 NewLIR2(kThumb2VcvtF64S32, tmp1.GetReg(), src_high.GetReg());
151 NewLIR2(kThumb2VcvtF64U32, rl_result.reg.GetReg(), src_low.GetReg());
152 LoadConstantWide(tmp2, 0x41f0000000000000LL);
153 NewLIR3(kThumb2VmlaF64, rl_result.reg.GetReg(), tmp1.GetReg(), tmp2.GetReg());
154 FreeTemp(tmp1);
155 FreeTemp(tmp2);
156 StoreValueWide(rl_dest, rl_result);
157 return;
158 }
159 case Instruction::FLOAT_TO_LONG:
160 GenConversionCall(QUICK_ENTRYPOINT_OFFSET(4, pF2l), rl_dest, rl_src);
161 return;
162 case Instruction::LONG_TO_FLOAT: {
163 rl_src = LoadValueWide(rl_src, kFPReg);
164 RegStorage src_low = rl_src.reg.DoubleToLowSingle();
165 RegStorage src_high = rl_src.reg.DoubleToHighSingle();
166 rl_result = EvalLoc(rl_dest, kFPReg, true);
167 // Allocate temp registers.
168 RegStorage high_val = AllocTempDouble();
169 RegStorage low_val = AllocTempDouble();
170 RegStorage const_val = AllocTempDouble();
171 // Long to double.
172 NewLIR2(kThumb2VcvtF64S32, high_val.GetReg(), src_high.GetReg());
173 NewLIR2(kThumb2VcvtF64U32, low_val.GetReg(), src_low.GetReg());
174 LoadConstantWide(const_val, INT64_C(0x41f0000000000000));
175 NewLIR3(kThumb2VmlaF64, low_val.GetReg(), high_val.GetReg(), const_val.GetReg());
176 // Double to float.
177 NewLIR2(kThumb2VcvtDF, rl_result.reg.GetReg(), low_val.GetReg());
178 // Free temp registers.
179 FreeTemp(high_val);
180 FreeTemp(low_val);
181 FreeTemp(const_val);
182 // Store result.
183 StoreValue(rl_dest, rl_result);
184 return;
185 }
186 case Instruction::DOUBLE_TO_LONG:
187 GenConversionCall(QUICK_ENTRYPOINT_OFFSET(4, pD2l), rl_dest, rl_src);
188 return;
189 default:
190 LOG(FATAL) << "Unexpected opcode: " << opcode;
191 }
192 if (rl_src.wide) {
193 rl_src = LoadValueWide(rl_src, kFPReg);
194 src_reg = rl_src.reg.GetReg();
195 } else {
196 rl_src = LoadValue(rl_src, kFPReg);
197 src_reg = rl_src.reg.GetReg();
198 }
199 if (rl_dest.wide) {
200 rl_result = EvalLoc(rl_dest, kFPReg, true);
201 NewLIR2(op, rl_result.reg.GetReg(), src_reg);
202 StoreValueWide(rl_dest, rl_result);
203 } else {
204 rl_result = EvalLoc(rl_dest, kFPReg, true);
205 NewLIR2(op, rl_result.reg.GetReg(), src_reg);
206 StoreValue(rl_dest, rl_result);
207 }
208}
209
210void Arm64Mir2Lir::GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias,
211 bool is_double) {
212 LIR* target = &block_label_list_[bb->taken];
213 RegLocation rl_src1;
214 RegLocation rl_src2;
215 if (is_double) {
216 rl_src1 = mir_graph_->GetSrcWide(mir, 0);
217 rl_src2 = mir_graph_->GetSrcWide(mir, 2);
218 rl_src1 = LoadValueWide(rl_src1, kFPReg);
219 rl_src2 = LoadValueWide(rl_src2, kFPReg);
220 NewLIR2(kThumb2Vcmpd, rl_src1.reg.GetReg(), rl_src2.reg.GetReg());
221 } else {
222 rl_src1 = mir_graph_->GetSrc(mir, 0);
223 rl_src2 = mir_graph_->GetSrc(mir, 1);
224 rl_src1 = LoadValue(rl_src1, kFPReg);
225 rl_src2 = LoadValue(rl_src2, kFPReg);
226 NewLIR2(kThumb2Vcmps, rl_src1.reg.GetReg(), rl_src2.reg.GetReg());
227 }
228 NewLIR0(kThumb2Fmstat);
229 ConditionCode ccode = mir->meta.ccode;
230 switch (ccode) {
231 case kCondEq:
232 case kCondNe:
233 break;
234 case kCondLt:
235 if (gt_bias) {
236 ccode = kCondMi;
237 }
238 break;
239 case kCondLe:
240 if (gt_bias) {
241 ccode = kCondLs;
242 }
243 break;
244 case kCondGt:
245 if (gt_bias) {
246 ccode = kCondHi;
247 }
248 break;
249 case kCondGe:
250 if (gt_bias) {
251 ccode = kCondUge;
252 }
253 break;
254 default:
255 LOG(FATAL) << "Unexpected ccode: " << ccode;
256 }
257 OpCondBranch(ccode, target);
258}
259
260
261void Arm64Mir2Lir::GenCmpFP(Instruction::Code opcode, RegLocation rl_dest,
262 RegLocation rl_src1, RegLocation rl_src2) {
263 bool is_double = false;
264 int default_result = -1;
265 RegLocation rl_result;
266
267 switch (opcode) {
268 case Instruction::CMPL_FLOAT:
269 is_double = false;
270 default_result = -1;
271 break;
272 case Instruction::CMPG_FLOAT:
273 is_double = false;
274 default_result = 1;
275 break;
276 case Instruction::CMPL_DOUBLE:
277 is_double = true;
278 default_result = -1;
279 break;
280 case Instruction::CMPG_DOUBLE:
281 is_double = true;
282 default_result = 1;
283 break;
284 default:
285 LOG(FATAL) << "Unexpected opcode: " << opcode;
286 }
287 if (is_double) {
288 rl_src1 = LoadValueWide(rl_src1, kFPReg);
289 rl_src2 = LoadValueWide(rl_src2, kFPReg);
290 // In case result vreg is also a src vreg, break association to avoid useless copy by EvalLoc()
291 ClobberSReg(rl_dest.s_reg_low);
292 rl_result = EvalLoc(rl_dest, kCoreReg, true);
293 LoadConstant(rl_result.reg, default_result);
294 NewLIR2(kThumb2Vcmpd, rl_src1.reg.GetReg(), rl_src2.reg.GetReg());
295 } else {
296 rl_src1 = LoadValue(rl_src1, kFPReg);
297 rl_src2 = LoadValue(rl_src2, kFPReg);
298 // In case result vreg is also a srcvreg, break association to avoid useless copy by EvalLoc()
299 ClobberSReg(rl_dest.s_reg_low);
300 rl_result = EvalLoc(rl_dest, kCoreReg, true);
301 LoadConstant(rl_result.reg, default_result);
302 NewLIR2(kThumb2Vcmps, rl_src1.reg.GetReg(), rl_src2.reg.GetReg());
303 }
304 DCHECK(!rl_result.reg.IsFloat());
305 NewLIR0(kThumb2Fmstat);
306
307 LIR* it = OpIT((default_result == -1) ? kCondGt : kCondMi, "");
308 NewLIR2(kThumb2MovI8M, rl_result.reg.GetReg(),
309 ModifiedImmediate(-default_result)); // Must not alter ccodes
310 OpEndIT(it);
311
312 it = OpIT(kCondEq, "");
313 LoadConstant(rl_result.reg, 0);
314 OpEndIT(it);
315
316 StoreValue(rl_dest, rl_result);
317}
318
319void Arm64Mir2Lir::GenNegFloat(RegLocation rl_dest, RegLocation rl_src) {
320 RegLocation rl_result;
321 rl_src = LoadValue(rl_src, kFPReg);
322 rl_result = EvalLoc(rl_dest, kFPReg, true);
323 NewLIR2(kThumb2Vnegs, rl_result.reg.GetReg(), rl_src.reg.GetReg());
324 StoreValue(rl_dest, rl_result);
325}
326
327void Arm64Mir2Lir::GenNegDouble(RegLocation rl_dest, RegLocation rl_src) {
328 RegLocation rl_result;
329 rl_src = LoadValueWide(rl_src, kFPReg);
330 rl_result = EvalLoc(rl_dest, kFPReg, true);
331 NewLIR2(kThumb2Vnegd, rl_result.reg.GetReg(), rl_src.reg.GetReg());
332 StoreValueWide(rl_dest, rl_result);
333}
334
335bool Arm64Mir2Lir::GenInlinedSqrt(CallInfo* info) {
336 DCHECK_EQ(cu_->instruction_set, kThumb2);
337 LIR *branch;
338 RegLocation rl_src = info->args[0];
339 RegLocation rl_dest = InlineTargetWide(info); // double place for result
340 rl_src = LoadValueWide(rl_src, kFPReg);
341 RegLocation rl_result = EvalLoc(rl_dest, kFPReg, true);
342 NewLIR2(kThumb2Vsqrtd, rl_result.reg.GetReg(), rl_src.reg.GetReg());
343 NewLIR2(kThumb2Vcmpd, rl_result.reg.GetReg(), rl_result.reg.GetReg());
344 NewLIR0(kThumb2Fmstat);
345 branch = NewLIR2(kThumbBCond, 0, kArmCondEq);
346 ClobberCallerSave();
347 LockCallTemps(); // Using fixed registers
348 RegStorage r_tgt = LoadHelper(QUICK_ENTRYPOINT_OFFSET(4, pSqrt));
349 NewLIR3(kThumb2Fmrrd, rs_r0.GetReg(), rs_r1.GetReg(), rl_src.reg.GetReg());
350 NewLIR1(kThumbBlxR, r_tgt.GetReg());
351 NewLIR3(kThumb2Fmdrr, rl_result.reg.GetReg(), rs_r0.GetReg(), rs_r1.GetReg());
352 branch->target = NewLIR0(kPseudoTargetLabel);
353 StoreValueWide(rl_dest, rl_result);
354 return true;
355}
356
357
358} // namespace art