blob: 6dc4a7ab5139e81e9f3a3402a5da611792652664 [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/* This file contains codegen for the Thumb2 ISA. */
18
19#include "arm64_lir.h"
20#include "codegen_arm64.h"
21#include "dex/quick/mir_to_lir-inl.h"
buzbeeb5860fb2014-06-21 15:31:01 -070022#include "dex/reg_storage_eq.h"
Matteo Franchin43ec8732014-03-31 15:00:14 +010023#include "entrypoints/quick/quick_entrypoints.h"
24#include "mirror/array.h"
25
26namespace art {
27
28LIR* Arm64Mir2Lir::OpCmpBranch(ConditionCode cond, RegStorage src1, RegStorage src2, LIR* target) {
29 OpRegReg(kOpCmp, src1, src2);
30 return OpCondBranch(cond, target);
31}
32
Matteo Franchin43ec8732014-03-31 15:00:14 +010033LIR* Arm64Mir2Lir::OpIT(ConditionCode ccode, const char* guide) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +010034 LOG(FATAL) << "Unexpected use of OpIT for Arm64";
35 return NULL;
Matteo Franchin43ec8732014-03-31 15:00:14 +010036}
37
38void Arm64Mir2Lir::OpEndIT(LIR* it) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +010039 LOG(FATAL) << "Unexpected use of OpEndIT for Arm64";
Matteo Franchin43ec8732014-03-31 15:00:14 +010040}
41
42/*
43 * 64-bit 3way compare function.
Matteo Franchine45fb9e2014-05-06 10:10:30 +010044 * cmp xA, xB
Zheng Xu511c8a62014-06-03 16:22:23 +080045 * csinc wC, wzr, wzr, eq // wC = (xA == xB) ? 0 : 1
46 * csneg wC, wC, wC, ge // wC = (xA >= xB) ? wC : -wC
Matteo Franchin43ec8732014-03-31 15:00:14 +010047 */
Matteo Franchine45fb9e2014-05-06 10:10:30 +010048void Arm64Mir2Lir::GenCmpLong(RegLocation rl_dest, RegLocation rl_src1,
49 RegLocation rl_src2) {
50 RegLocation rl_result;
Matteo Franchin43ec8732014-03-31 15:00:14 +010051 rl_src1 = LoadValueWide(rl_src1, kCoreReg);
52 rl_src2 = LoadValueWide(rl_src2, kCoreReg);
Matteo Franchine45fb9e2014-05-06 10:10:30 +010053 rl_result = EvalLoc(rl_dest, kCoreReg, true);
Matteo Franchin43ec8732014-03-31 15:00:14 +010054
Matteo Franchine45fb9e2014-05-06 10:10:30 +010055 OpRegReg(kOpCmp, rl_src1.reg, rl_src2.reg);
Zheng Xu511c8a62014-06-03 16:22:23 +080056 NewLIR4(kA64Csinc4rrrc, rl_result.reg.GetReg(), rwzr, rwzr, kArmCondEq);
57 NewLIR4(kA64Csneg4rrrc, rl_result.reg.GetReg(), rl_result.reg.GetReg(),
58 rl_result.reg.GetReg(), kArmCondGe);
59 StoreValue(rl_dest, rl_result);
Serban Constantinescued65c5e2014-05-22 15:10:18 +010060}
61
62void Arm64Mir2Lir::GenShiftOpLong(Instruction::Code opcode, RegLocation rl_dest,
63 RegLocation rl_src1, RegLocation rl_shift) {
64 OpKind op = kOpBkpt;
65 switch (opcode) {
66 case Instruction::SHL_LONG:
67 case Instruction::SHL_LONG_2ADDR:
68 op = kOpLsl;
69 break;
70 case Instruction::SHR_LONG:
71 case Instruction::SHR_LONG_2ADDR:
72 op = kOpAsr;
73 break;
74 case Instruction::USHR_LONG:
75 case Instruction::USHR_LONG_2ADDR:
76 op = kOpLsr;
77 break;
78 default:
79 LOG(FATAL) << "Unexpected case: " << opcode;
80 }
Zheng Xue2eb29e2014-06-12 10:22:33 +080081 rl_shift = LoadValue(rl_shift, kCoreReg);
Serban Constantinescued65c5e2014-05-22 15:10:18 +010082 rl_src1 = LoadValueWide(rl_src1, kCoreReg);
83 RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true);
Zheng Xue2eb29e2014-06-12 10:22:33 +080084 OpRegRegReg(op, rl_result.reg, rl_src1.reg, As64BitReg(rl_shift.reg));
Serban Constantinescued65c5e2014-05-22 15:10:18 +010085 StoreValueWide(rl_dest, rl_result);
Matteo Franchin43ec8732014-03-31 15:00:14 +010086}
87
Matteo Franchin43ec8732014-03-31 15:00:14 +010088void Arm64Mir2Lir::GenSelect(BasicBlock* bb, MIR* mir) {
89 RegLocation rl_result;
90 RegLocation rl_src = mir_graph_->GetSrc(mir, 0);
91 RegLocation rl_dest = mir_graph_->GetDest(mir);
buzbeea0cd2d72014-06-01 09:33:49 -070092 RegisterClass src_reg_class = rl_src.ref ? kRefReg : kCoreReg;
93 RegisterClass result_reg_class = rl_dest.ref ? kRefReg : kCoreReg;
Andreas Gampe381f8ac2014-07-10 03:23:41 -070094
buzbeea0cd2d72014-06-01 09:33:49 -070095 rl_src = LoadValue(rl_src, src_reg_class);
Andreas Gampe381f8ac2014-07-10 03:23:41 -070096 // rl_src may be aliased with rl_result/rl_dest, so do compare early.
97 OpRegImm(kOpCmp, rl_src.reg, 0);
98
Serban Constantinescu05e27ff2014-05-28 13:21:45 +010099 ArmConditionCode code = ArmConditionEncoding(mir->meta.ccode);
100
Andreas Gampe381f8ac2014-07-10 03:23:41 -0700101 // The kMirOpSelect has two variants, one for constants and one for moves.
102 bool is_wide = rl_dest.ref || rl_dest.wide;
103
104 if (mir->ssa_rep->num_uses == 1) {
105 uint32_t true_val = mir->dalvikInsn.vB;
106 uint32_t false_val = mir->dalvikInsn.vC;
107
108 int opcode; // The opcode.
109 int left_op, right_op; // The operands.
110 bool rl_result_evaled = false;
111
112 // Check some simple cases.
113 // TODO: Improve this.
114 int zero_reg = (is_wide ? rs_xzr : rs_wzr).GetReg();
115
116 if ((true_val == 0 && false_val == 1) || (true_val == 1 && false_val == 0)) {
117 // CSInc cheap based on wzr.
118 if (true_val == 1) {
119 // Negate.
120 code = ArmConditionEncoding(NegateComparison(mir->meta.ccode));
121 }
122
123 left_op = right_op = zero_reg;
124 opcode = is_wide ? WIDE(kA64Csinc4rrrc) : kA64Csinc4rrrc;
125 } else if ((true_val == 0 && false_val == 0xFFFFFFFF) ||
126 (true_val == 0xFFFFFFFF && false_val == 0)) {
127 // CSneg cheap based on wzr.
128 if (true_val == 0xFFFFFFFF) {
129 // Negate.
130 code = ArmConditionEncoding(NegateComparison(mir->meta.ccode));
131 }
132
133 left_op = right_op = zero_reg;
Stuart Monteith873c3712014-07-11 16:31:28 +0100134 opcode = is_wide ? WIDE(kA64Csinv4rrrc) : kA64Csinv4rrrc;
Andreas Gampe381f8ac2014-07-10 03:23:41 -0700135 } else if (true_val == 0 || false_val == 0) {
136 // Csel half cheap based on wzr.
137 rl_result = EvalLoc(rl_dest, result_reg_class, true);
138 rl_result_evaled = true;
139 if (false_val == 0) {
140 // Negate.
141 code = ArmConditionEncoding(NegateComparison(mir->meta.ccode));
142 }
143 LoadConstantNoClobber(rl_result.reg, true_val == 0 ? false_val : true_val);
144 left_op = zero_reg;
145 right_op = rl_result.reg.GetReg();
146 opcode = is_wide ? WIDE(kA64Csel4rrrc) : kA64Csel4rrrc;
147 } else if (true_val == 1 || false_val == 1) {
148 // CSInc half cheap based on wzr.
149 rl_result = EvalLoc(rl_dest, result_reg_class, true);
150 rl_result_evaled = true;
151 if (true_val == 1) {
152 // Negate.
153 code = ArmConditionEncoding(NegateComparison(mir->meta.ccode));
154 }
155 LoadConstantNoClobber(rl_result.reg, true_val == 1 ? false_val : true_val);
156 left_op = rl_result.reg.GetReg();
157 right_op = zero_reg;
158 opcode = is_wide ? WIDE(kA64Csinc4rrrc) : kA64Csinc4rrrc;
159 } else if (true_val == 0xFFFFFFFF || false_val == 0xFFFFFFFF) {
160 // CSneg half cheap based on wzr.
161 rl_result = EvalLoc(rl_dest, result_reg_class, true);
162 rl_result_evaled = true;
163 if (true_val == 0xFFFFFFFF) {
164 // Negate.
165 code = ArmConditionEncoding(NegateComparison(mir->meta.ccode));
166 }
167 LoadConstantNoClobber(rl_result.reg, true_val == 0xFFFFFFFF ? false_val : true_val);
168 left_op = rl_result.reg.GetReg();
169 right_op = zero_reg;
Stuart Monteith873c3712014-07-11 16:31:28 +0100170 opcode = is_wide ? WIDE(kA64Csinv4rrrc) : kA64Csinv4rrrc;
Andreas Gampe39c8a992014-07-12 04:26:03 -0700171 } else if ((true_val + 1 == false_val) || (false_val + 1 == true_val)) {
172 // Load a constant and use CSinc. Use rl_result.
173 if (false_val + 1 == true_val) {
174 // Negate.
175 code = ArmConditionEncoding(NegateComparison(mir->meta.ccode));
176 true_val = false_val;
177 }
178
179 rl_result = EvalLoc(rl_dest, result_reg_class, true);
180 rl_result_evaled = true;
181 LoadConstantNoClobber(rl_result.reg, true_val);
182 left_op = right_op = rl_result.reg.GetReg();
183 opcode = is_wide ? WIDE(kA64Csinc4rrrc) : kA64Csinc4rrrc;
Andreas Gampe381f8ac2014-07-10 03:23:41 -0700184 } else {
185 // Csel. The rest. Use rl_result and a temp.
186 // TODO: To minimize the constants being loaded, check whether one can be inexpensively
187 // loaded as n - 1 or ~n.
188 rl_result = EvalLoc(rl_dest, result_reg_class, true);
189 rl_result_evaled = true;
190 LoadConstantNoClobber(rl_result.reg, true_val);
191 RegStorage t_reg2 = AllocTypedTemp(false, result_reg_class);
192 if (rl_dest.wide) {
193 if (t_reg2.Is32Bit()) {
194 t_reg2 = As64BitReg(t_reg2);
195 }
196 }
197 LoadConstantNoClobber(t_reg2, false_val);
198
199 // Use csel.
200 left_op = rl_result.reg.GetReg();
201 right_op = t_reg2.GetReg();
202 opcode = is_wide ? WIDE(kA64Csel4rrrc) : kA64Csel4rrrc;
203 }
204
205 if (!rl_result_evaled) {
206 rl_result = EvalLoc(rl_dest, result_reg_class, true);
207 }
208
209 NewLIR4(opcode, rl_result.reg.GetReg(), left_op, right_op, code);
210 } else {
211 RegLocation rl_true = mir_graph_->reg_location_[mir->ssa_rep->uses[1]];
212 RegLocation rl_false = mir_graph_->reg_location_[mir->ssa_rep->uses[2]];
213
214 rl_true = LoadValue(rl_true, result_reg_class);
215 rl_false = LoadValue(rl_false, result_reg_class);
216 rl_result = EvalLoc(rl_dest, result_reg_class, true);
217
218 int opcode = is_wide ? WIDE(kA64Csel4rrrc) : kA64Csel4rrrc;
219 NewLIR4(opcode, rl_result.reg.GetReg(),
220 rl_true.reg.GetReg(), rl_false.reg.GetReg(), code);
221 }
Matteo Franchin43ec8732014-03-31 15:00:14 +0100222 StoreValue(rl_dest, rl_result);
223}
224
225void Arm64Mir2Lir::GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir) {
226 RegLocation rl_src1 = mir_graph_->GetSrcWide(mir, 0);
227 RegLocation rl_src2 = mir_graph_->GetSrcWide(mir, 2);
Serban Constantinescu05e27ff2014-05-28 13:21:45 +0100228 LIR* taken = &block_label_list_[bb->taken];
229 LIR* not_taken = &block_label_list_[bb->fall_through];
Matteo Franchin43ec8732014-03-31 15:00:14 +0100230 // Normalize such that if either operand is constant, src2 will be constant.
231 ConditionCode ccode = mir->meta.ccode;
232 if (rl_src1.is_const) {
233 std::swap(rl_src1, rl_src2);
234 ccode = FlipComparisonOrder(ccode);
235 }
Serban Constantinescu05e27ff2014-05-28 13:21:45 +0100236
Andreas Gampe381f8ac2014-07-10 03:23:41 -0700237 rl_src1 = LoadValueWide(rl_src1, kCoreReg);
238
Matteo Franchin43ec8732014-03-31 15:00:14 +0100239 if (rl_src2.is_const) {
Andreas Gampe381f8ac2014-07-10 03:23:41 -0700240 // TODO: Optimize for rl_src1.is_const? (Does happen in the boot image at the moment.)
241
Matteo Franchin43ec8732014-03-31 15:00:14 +0100242 int64_t val = mir_graph_->ConstantValueWide(rl_src2);
Serban Constantinescu05e27ff2014-05-28 13:21:45 +0100243 // Special handling using cbz & cbnz.
244 if (val == 0 && (ccode == kCondEq || ccode == kCondNe)) {
245 OpCmpImmBranch(ccode, rl_src1.reg, 0, taken);
246 OpCmpImmBranch(NegateComparison(ccode), rl_src1.reg, 0, not_taken);
247 return;
Andreas Gampe381f8ac2014-07-10 03:23:41 -0700248 }
249
Serban Constantinescu05e27ff2014-05-28 13:21:45 +0100250 // Only handle Imm if src2 is not already in a register.
Andreas Gampe381f8ac2014-07-10 03:23:41 -0700251 rl_src2 = UpdateLocWide(rl_src2);
252 if (rl_src2.location != kLocPhysReg) {
Serban Constantinescu05e27ff2014-05-28 13:21:45 +0100253 OpRegImm64(kOpCmp, rl_src1.reg, val);
254 OpCondBranch(ccode, taken);
255 OpCondBranch(NegateComparison(ccode), not_taken);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100256 return;
257 }
258 }
Serban Constantinescu05e27ff2014-05-28 13:21:45 +0100259
Matteo Franchin43ec8732014-03-31 15:00:14 +0100260 rl_src2 = LoadValueWide(rl_src2, kCoreReg);
Serban Constantinescu05e27ff2014-05-28 13:21:45 +0100261 OpRegReg(kOpCmp, rl_src1.reg, rl_src2.reg);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100262 OpCondBranch(ccode, taken);
Serban Constantinescu05e27ff2014-05-28 13:21:45 +0100263 OpCondBranch(NegateComparison(ccode), not_taken);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100264}
265
266/*
267 * Generate a register comparison to an immediate and branch. Caller
268 * is responsible for setting branch target field.
269 */
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100270LIR* Arm64Mir2Lir::OpCmpImmBranch(ConditionCode cond, RegStorage reg, int check_value,
271 LIR* target) {
Andreas Gampe9522af92014-07-14 20:16:59 -0700272 LIR* branch = nullptr;
Matteo Franchin43ec8732014-03-31 15:00:14 +0100273 ArmConditionCode arm_cond = ArmConditionEncoding(cond);
Andreas Gampe9522af92014-07-14 20:16:59 -0700274 if (check_value == 0) {
275 if (arm_cond == kArmCondEq || arm_cond == kArmCondNe) {
276 ArmOpcode opcode = (arm_cond == kArmCondEq) ? kA64Cbz2rt : kA64Cbnz2rt;
277 ArmOpcode wide = reg.Is64Bit() ? WIDE(0) : UNWIDE(0);
278 branch = NewLIR2(opcode | wide, reg.GetReg(), 0);
279 } else if (arm_cond == kArmCondLs) {
280 // kArmCondLs is an unsigned less or equal. A comparison r <= 0 is then the same as cbz.
281 // This case happens for a bounds check of array[0].
282 ArmOpcode opcode = kA64Cbz2rt;
283 ArmOpcode wide = reg.Is64Bit() ? WIDE(0) : UNWIDE(0);
284 branch = NewLIR2(opcode | wide, reg.GetReg(), 0);
285 }
286 }
287
288 if (branch == nullptr) {
Matteo Franchin43ec8732014-03-31 15:00:14 +0100289 OpRegImm(kOpCmp, reg, check_value);
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100290 branch = NewLIR2(kA64B2ct, arm_cond, 0);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100291 }
Andreas Gampe9522af92014-07-14 20:16:59 -0700292
Matteo Franchin43ec8732014-03-31 15:00:14 +0100293 branch->target = target;
294 return branch;
295}
296
Zheng Xu7c1c2632014-06-17 18:17:31 +0800297LIR* Arm64Mir2Lir::OpCmpMemImmBranch(ConditionCode cond, RegStorage temp_reg,
298 RegStorage base_reg, int offset, int check_value,
Nicolas Geoffray0025a862014-07-11 08:26:40 +0000299 LIR* target) {
Zheng Xu7c1c2632014-06-17 18:17:31 +0800300 // It is possible that temp register is 64-bit. (ArgReg or RefReg)
301 // Always compare 32-bit value no matter what temp_reg is.
302 if (temp_reg.Is64Bit()) {
303 temp_reg = As32BitReg(temp_reg);
304 }
305 Load32Disp(base_reg, offset, temp_reg);
306 LIR* branch = OpCmpImmBranch(cond, temp_reg, check_value, target);
307 return branch;
308}
309
Matteo Franchin43ec8732014-03-31 15:00:14 +0100310LIR* Arm64Mir2Lir::OpRegCopyNoInsert(RegStorage r_dest, RegStorage r_src) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100311 bool dest_is_fp = r_dest.IsFloat();
312 bool src_is_fp = r_src.IsFloat();
313 ArmOpcode opcode = kA64Brk1d;
Matteo Franchin43ec8732014-03-31 15:00:14 +0100314 LIR* res;
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100315
316 if (LIKELY(dest_is_fp == src_is_fp)) {
317 if (LIKELY(!dest_is_fp)) {
Andreas Gampe4b537a82014-06-30 22:24:53 -0700318 DCHECK_EQ(r_dest.Is64Bit(), r_src.Is64Bit());
319
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100320 // Core/core copy.
321 // Copies involving the sp register require a different instruction.
322 opcode = UNLIKELY(A64_REG_IS_SP(r_dest.GetReg())) ? kA64Add4RRdT : kA64Mov2rr;
323
324 // TODO(Arm64): kA64Add4RRdT formally has 4 args, but is used as a 2 args instruction.
325 // This currently works because the other arguments are set to 0 by default. We should
326 // rather introduce an alias kA64Mov2RR.
327
328 // core/core copy. Do a x/x copy only if both registers are x.
329 if (r_dest.Is64Bit() && r_src.Is64Bit()) {
330 opcode = WIDE(opcode);
331 }
332 } else {
333 // Float/float copy.
334 bool dest_is_double = r_dest.IsDouble();
335 bool src_is_double = r_src.IsDouble();
336
337 // We do not do float/double or double/float casts here.
338 DCHECK_EQ(dest_is_double, src_is_double);
339
340 // Homogeneous float/float copy.
341 opcode = (dest_is_double) ? FWIDE(kA64Fmov2ff) : kA64Fmov2ff;
342 }
343 } else {
344 // Inhomogeneous register copy.
345 if (dest_is_fp) {
346 if (r_dest.IsDouble()) {
347 opcode = kA64Fmov2Sx;
348 } else {
Andreas Gampe4b537a82014-06-30 22:24:53 -0700349 r_src = Check32BitReg(r_src);
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100350 opcode = kA64Fmov2sw;
351 }
352 } else {
353 if (r_src.IsDouble()) {
354 opcode = kA64Fmov2xS;
355 } else {
Andreas Gampe4b537a82014-06-30 22:24:53 -0700356 r_dest = Check32BitReg(r_dest);
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100357 opcode = kA64Fmov2ws;
358 }
359 }
Matteo Franchin43ec8732014-03-31 15:00:14 +0100360 }
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100361
Matteo Franchin43ec8732014-03-31 15:00:14 +0100362 res = RawLIR(current_dalvik_offset_, opcode, r_dest.GetReg(), r_src.GetReg());
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100363
Matteo Franchin43ec8732014-03-31 15:00:14 +0100364 if (!(cu_->disable_opt & (1 << kSafeOptimizations)) && r_dest == r_src) {
365 res->flags.is_nop = true;
366 }
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100367
Matteo Franchin43ec8732014-03-31 15:00:14 +0100368 return res;
369}
370
371void Arm64Mir2Lir::OpRegCopy(RegStorage r_dest, RegStorage r_src) {
372 if (r_dest != r_src) {
373 LIR* res = OpRegCopyNoInsert(r_dest, r_src);
374 AppendLIR(res);
375 }
376}
377
378void Arm64Mir2Lir::OpRegCopyWide(RegStorage r_dest, RegStorage r_src) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100379 OpRegCopy(r_dest, r_src);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100380}
381
382// Table of magic divisors
383struct MagicTable {
Matteo Franchin7c6c2ac2014-07-01 18:03:08 +0100384 int magic64_base;
385 int magic64_eor;
386 uint64_t magic64;
387 uint32_t magic32;
Matteo Franchin43ec8732014-03-31 15:00:14 +0100388 uint32_t shift;
389 DividePattern pattern;
390};
391
392static const MagicTable magic_table[] = {
Matteo Franchin7c6c2ac2014-07-01 18:03:08 +0100393 { 0, 0, 0, 0, 0, DivideNone}, // 0
394 { 0, 0, 0, 0, 0, DivideNone}, // 1
395 { 0, 0, 0, 0, 0, DivideNone}, // 2
396 {0x3c, -1, 0x5555555555555556, 0x55555556, 0, Divide3}, // 3
397 { 0, 0, 0, 0, 0, DivideNone}, // 4
398 {0xf9, -1, 0x6666666666666667, 0x66666667, 1, Divide5}, // 5
399 {0x7c, 0x1041, 0x2AAAAAAAAAAAAAAB, 0x2AAAAAAB, 0, Divide3}, // 6
400 { -1, -1, 0x924924924924924A, 0x92492493, 2, Divide7}, // 7
401 { 0, 0, 0, 0, 0, DivideNone}, // 8
402 { -1, -1, 0x38E38E38E38E38E4, 0x38E38E39, 1, Divide5}, // 9
403 {0xf9, -1, 0x6666666666666667, 0x66666667, 2, Divide5}, // 10
404 { -1, -1, 0x2E8BA2E8BA2E8BA3, 0x2E8BA2E9, 1, Divide5}, // 11
405 {0x7c, 0x1041, 0x2AAAAAAAAAAAAAAB, 0x2AAAAAAB, 1, Divide5}, // 12
406 { -1, -1, 0x4EC4EC4EC4EC4EC5, 0x4EC4EC4F, 2, Divide5}, // 13
407 { -1, -1, 0x924924924924924A, 0x92492493, 3, Divide7}, // 14
408 {0x78, -1, 0x8888888888888889, 0x88888889, 3, Divide7}, // 15
Matteo Franchin43ec8732014-03-31 15:00:14 +0100409};
410
411// Integer division by constant via reciprocal multiply (Hacker's Delight, 10-4)
412bool Arm64Mir2Lir::SmallLiteralDivRem(Instruction::Code dalvik_opcode, bool is_div,
Matteo Franchinc61b3c92014-06-18 11:52:47 +0100413 RegLocation rl_src, RegLocation rl_dest, int lit) {
414 if ((lit < 0) || (lit >= static_cast<int>(arraysize(magic_table)))) {
Matteo Franchin43ec8732014-03-31 15:00:14 +0100415 return false;
416 }
417 DividePattern pattern = magic_table[lit].pattern;
418 if (pattern == DivideNone) {
419 return false;
420 }
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100421 // Tuning: add rem patterns
422 if (!is_div) {
423 return false;
424 }
Matteo Franchin43ec8732014-03-31 15:00:14 +0100425
426 RegStorage r_magic = AllocTemp();
Matteo Franchin7c6c2ac2014-07-01 18:03:08 +0100427 LoadConstant(r_magic, magic_table[lit].magic32);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100428 rl_src = LoadValue(rl_src, kCoreReg);
429 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
Matteo Franchinc61b3c92014-06-18 11:52:47 +0100430 RegStorage r_long_mul = AllocTemp();
431 NewLIR4(kA64Smaddl4xwwx, As64BitReg(r_long_mul).GetReg(),
432 r_magic.GetReg(), rl_src.reg.GetReg(), rxzr);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100433 switch (pattern) {
434 case Divide3:
Matteo Franchinc61b3c92014-06-18 11:52:47 +0100435 OpRegRegImm(kOpLsr, As64BitReg(r_long_mul), As64BitReg(r_long_mul), 32);
436 OpRegRegRegShift(kOpSub, rl_result.reg, r_long_mul, rl_src.reg, EncodeShift(kA64Asr, 31));
Matteo Franchin43ec8732014-03-31 15:00:14 +0100437 break;
438 case Divide5:
Matteo Franchinc61b3c92014-06-18 11:52:47 +0100439 OpRegRegImm(kOpAsr, As64BitReg(r_long_mul), As64BitReg(r_long_mul),
440 32 + magic_table[lit].shift);
441 OpRegRegRegShift(kOpSub, rl_result.reg, r_long_mul, rl_src.reg, EncodeShift(kA64Asr, 31));
Matteo Franchin43ec8732014-03-31 15:00:14 +0100442 break;
443 case Divide7:
Matteo Franchinc61b3c92014-06-18 11:52:47 +0100444 OpRegRegRegShift(kOpAdd, As64BitReg(r_long_mul), As64BitReg(rl_src.reg),
445 As64BitReg(r_long_mul), EncodeShift(kA64Lsr, 32));
446 OpRegRegImm(kOpAsr, r_long_mul, r_long_mul, magic_table[lit].shift);
447 OpRegRegRegShift(kOpSub, rl_result.reg, r_long_mul, rl_src.reg, EncodeShift(kA64Asr, 31));
Matteo Franchin43ec8732014-03-31 15:00:14 +0100448 break;
449 default:
450 LOG(FATAL) << "Unexpected pattern: " << pattern;
451 }
Matteo Franchin43ec8732014-03-31 15:00:14 +0100452 StoreValue(rl_dest, rl_result);
453 return true;
454}
455
Matteo Franchin7c6c2ac2014-07-01 18:03:08 +0100456bool Arm64Mir2Lir::SmallLiteralDivRem64(Instruction::Code dalvik_opcode, bool is_div,
457 RegLocation rl_src, RegLocation rl_dest, int64_t lit) {
458 if ((lit < 0) || (lit >= static_cast<int>(arraysize(magic_table)))) {
459 return false;
460 }
461 DividePattern pattern = magic_table[lit].pattern;
462 if (pattern == DivideNone) {
463 return false;
464 }
465 // Tuning: add rem patterns
466 if (!is_div) {
467 return false;
468 }
469
470 RegStorage r_magic = AllocTempWide();
471 rl_src = LoadValueWide(rl_src, kCoreReg);
472 RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true);
473 RegStorage r_long_mul = AllocTempWide();
474
475 if (magic_table[lit].magic64_base >= 0) {
476 // Check that the entry in the table is correct.
477 if (kIsDebugBuild) {
478 uint64_t reconstructed_imm;
479 uint64_t base = DecodeLogicalImmediate(/*is_wide*/true, magic_table[lit].magic64_base);
480 if (magic_table[lit].magic64_eor >= 0) {
481 uint64_t eor = DecodeLogicalImmediate(/*is_wide*/true, magic_table[lit].magic64_eor);
482 reconstructed_imm = base ^ eor;
483 } else {
484 reconstructed_imm = base + 1;
485 }
486 DCHECK_EQ(reconstructed_imm, magic_table[lit].magic64) << " for literal " << lit;
487 }
488
489 // Load the magic constant in two instructions.
490 NewLIR3(WIDE(kA64Orr3Rrl), r_magic.GetReg(), rxzr, magic_table[lit].magic64_base);
491 if (magic_table[lit].magic64_eor >= 0) {
492 NewLIR3(WIDE(kA64Eor3Rrl), r_magic.GetReg(), r_magic.GetReg(),
493 magic_table[lit].magic64_eor);
494 } else {
495 NewLIR4(WIDE(kA64Add4RRdT), r_magic.GetReg(), r_magic.GetReg(), 1, 0);
496 }
497 } else {
498 LoadConstantWide(r_magic, magic_table[lit].magic64);
499 }
500
501 NewLIR3(kA64Smulh3xxx, r_long_mul.GetReg(), r_magic.GetReg(), rl_src.reg.GetReg());
502 switch (pattern) {
503 case Divide3:
504 OpRegRegRegShift(kOpSub, rl_result.reg, r_long_mul, rl_src.reg, EncodeShift(kA64Asr, 63));
505 break;
506 case Divide5:
507 OpRegRegImm(kOpAsr, r_long_mul, r_long_mul, magic_table[lit].shift);
508 OpRegRegRegShift(kOpSub, rl_result.reg, r_long_mul, rl_src.reg, EncodeShift(kA64Asr, 63));
509 break;
510 case Divide7:
511 OpRegRegReg(kOpAdd, r_long_mul, rl_src.reg, r_long_mul);
512 OpRegRegImm(kOpAsr, r_long_mul, r_long_mul, magic_table[lit].shift);
513 OpRegRegRegShift(kOpSub, rl_result.reg, r_long_mul, rl_src.reg, EncodeShift(kA64Asr, 63));
514 break;
515 default:
516 LOG(FATAL) << "Unexpected pattern: " << pattern;
517 }
518 StoreValueWide(rl_dest, rl_result);
519 return true;
520}
521
Matteo Franchinc61b3c92014-06-18 11:52:47 +0100522// Returns true if it added instructions to 'cu' to divide 'rl_src' by 'lit'
523// and store the result in 'rl_dest'.
524bool Arm64Mir2Lir::HandleEasyDivRem(Instruction::Code dalvik_opcode, bool is_div,
525 RegLocation rl_src, RegLocation rl_dest, int lit) {
Matteo Franchin7c6c2ac2014-07-01 18:03:08 +0100526 return HandleEasyDivRem64(dalvik_opcode, is_div, rl_src, rl_dest, static_cast<int>(lit));
527}
528
529// Returns true if it added instructions to 'cu' to divide 'rl_src' by 'lit'
530// and store the result in 'rl_dest'.
531bool Arm64Mir2Lir::HandleEasyDivRem64(Instruction::Code dalvik_opcode, bool is_div,
532 RegLocation rl_src, RegLocation rl_dest, int64_t lit) {
533 const bool is_64bit = rl_dest.wide;
534 const int nbits = (is_64bit) ? 64 : 32;
535
Matteo Franchinc61b3c92014-06-18 11:52:47 +0100536 if (lit < 2) {
537 return false;
538 }
539 if (!IsPowerOfTwo(lit)) {
Matteo Franchin7c6c2ac2014-07-01 18:03:08 +0100540 if (is_64bit) {
541 return SmallLiteralDivRem64(dalvik_opcode, is_div, rl_src, rl_dest, lit);
542 } else {
543 return SmallLiteralDivRem(dalvik_opcode, is_div, rl_src, rl_dest, static_cast<int32_t>(lit));
544 }
Matteo Franchinc61b3c92014-06-18 11:52:47 +0100545 }
546 int k = LowestSetBit(lit);
Matteo Franchin7c6c2ac2014-07-01 18:03:08 +0100547 if (k >= nbits - 2) {
Matteo Franchinc61b3c92014-06-18 11:52:47 +0100548 // Avoid special cases.
549 return false;
550 }
Matteo Franchin7c6c2ac2014-07-01 18:03:08 +0100551
552 RegLocation rl_result;
553 RegStorage t_reg;
554 if (is_64bit) {
555 rl_src = LoadValueWide(rl_src, kCoreReg);
556 rl_result = EvalLocWide(rl_dest, kCoreReg, true);
557 t_reg = AllocTempWide();
558 } else {
559 rl_src = LoadValue(rl_src, kCoreReg);
560 rl_result = EvalLoc(rl_dest, kCoreReg, true);
561 t_reg = AllocTemp();
562 }
563
564 int shift = EncodeShift(kA64Lsr, nbits - k);
Matteo Franchinc61b3c92014-06-18 11:52:47 +0100565 if (is_div) {
Matteo Franchinc61b3c92014-06-18 11:52:47 +0100566 if (lit == 2) {
567 // Division by 2 is by far the most common division by constant.
Matteo Franchin7c6c2ac2014-07-01 18:03:08 +0100568 OpRegRegRegShift(kOpAdd, t_reg, rl_src.reg, rl_src.reg, shift);
Matteo Franchinc61b3c92014-06-18 11:52:47 +0100569 OpRegRegImm(kOpAsr, rl_result.reg, t_reg, k);
570 } else {
Matteo Franchin7c6c2ac2014-07-01 18:03:08 +0100571 OpRegRegImm(kOpAsr, t_reg, rl_src.reg, nbits - 1);
572 OpRegRegRegShift(kOpAdd, t_reg, rl_src.reg, t_reg, shift);
Matteo Franchinc61b3c92014-06-18 11:52:47 +0100573 OpRegRegImm(kOpAsr, rl_result.reg, t_reg, k);
574 }
575 } else {
Matteo Franchinc61b3c92014-06-18 11:52:47 +0100576 if (lit == 2) {
Matteo Franchin7c6c2ac2014-07-01 18:03:08 +0100577 OpRegRegRegShift(kOpAdd, t_reg, rl_src.reg, rl_src.reg, shift);
578 OpRegRegImm64(kOpAnd, t_reg, t_reg, lit - 1);
579 OpRegRegRegShift(kOpSub, rl_result.reg, t_reg, rl_src.reg, shift);
Matteo Franchinc61b3c92014-06-18 11:52:47 +0100580 } else {
Matteo Franchin7c6c2ac2014-07-01 18:03:08 +0100581 RegStorage t_reg2 = (is_64bit) ? AllocTempWide() : AllocTemp();
582 OpRegRegImm(kOpAsr, t_reg, rl_src.reg, nbits - 1);
583 OpRegRegRegShift(kOpAdd, t_reg2, rl_src.reg, t_reg, shift);
584 OpRegRegImm64(kOpAnd, t_reg2, t_reg2, lit - 1);
585 OpRegRegRegShift(kOpSub, rl_result.reg, t_reg2, t_reg, shift);
Matteo Franchinc61b3c92014-06-18 11:52:47 +0100586 }
587 }
Matteo Franchin7c6c2ac2014-07-01 18:03:08 +0100588
589 if (is_64bit) {
590 StoreValueWide(rl_dest, rl_result);
591 } else {
592 StoreValue(rl_dest, rl_result);
593 }
Matteo Franchinc61b3c92014-06-18 11:52:47 +0100594 return true;
595}
596
Matteo Franchin43ec8732014-03-31 15:00:14 +0100597bool Arm64Mir2Lir::EasyMultiply(RegLocation rl_src, RegLocation rl_dest, int lit) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100598 LOG(FATAL) << "Unexpected use of EasyMultiply for Arm64";
599 return false;
Matteo Franchin43ec8732014-03-31 15:00:14 +0100600}
601
Matteo Franchin43ec8732014-03-31 15:00:14 +0100602RegLocation Arm64Mir2Lir::GenDivRemLit(RegLocation rl_dest, RegLocation rl_src1, int lit, bool is_div) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100603 LOG(FATAL) << "Unexpected use of GenDivRemLit for Arm64";
Matteo Franchin43ec8732014-03-31 15:00:14 +0100604 return rl_dest;
605}
606
607RegLocation Arm64Mir2Lir::GenDivRemLit(RegLocation rl_dest, RegStorage reg1, int lit, bool is_div) {
608 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
609
610 // Put the literal in a temp.
611 RegStorage lit_temp = AllocTemp();
612 LoadConstant(lit_temp, lit);
613 // Use the generic case for div/rem with arg2 in a register.
614 // TODO: The literal temp can be freed earlier during a modulus to reduce reg pressure.
615 rl_result = GenDivRem(rl_result, reg1, lit_temp, is_div);
616 FreeTemp(lit_temp);
617
618 return rl_result;
619}
620
Matteo Franchin7c6c2ac2014-07-01 18:03:08 +0100621RegLocation Arm64Mir2Lir::GenDivRem(RegLocation rl_dest, RegLocation rl_src1,
622 RegLocation rl_src2, bool is_div, bool check_zero) {
623 LOG(FATAL) << "Unexpected use of GenDivRem for Arm64";
624 return rl_dest;
625}
626
Serban Constantinescued65c5e2014-05-22 15:10:18 +0100627RegLocation Arm64Mir2Lir::GenDivRem(RegLocation rl_dest, RegStorage r_src1, RegStorage r_src2,
Matteo Franchin7c6c2ac2014-07-01 18:03:08 +0100628 bool is_div) {
Serban Constantinescued65c5e2014-05-22 15:10:18 +0100629 CHECK_EQ(r_src1.Is64Bit(), r_src2.Is64Bit());
630
Matteo Franchin43ec8732014-03-31 15:00:14 +0100631 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
632 if (is_div) {
Serban Constantinescued65c5e2014-05-22 15:10:18 +0100633 OpRegRegReg(kOpDiv, rl_result.reg, r_src1, r_src2);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100634 } else {
Serban Constantinescued65c5e2014-05-22 15:10:18 +0100635 // temp = r_src1 / r_src2
636 // dest = r_src1 - temp * r_src2
637 RegStorage temp;
638 ArmOpcode wide;
639 if (rl_result.reg.Is64Bit()) {
640 temp = AllocTempWide();
641 wide = WIDE(0);
642 } else {
643 temp = AllocTemp();
644 wide = UNWIDE(0);
645 }
646 OpRegRegReg(kOpDiv, temp, r_src1, r_src2);
647 NewLIR4(kA64Msub4rrrr | wide, rl_result.reg.GetReg(), temp.GetReg(),
648 r_src1.GetReg(), r_src2.GetReg());
Matteo Franchin43ec8732014-03-31 15:00:14 +0100649 FreeTemp(temp);
650 }
Matteo Franchin43ec8732014-03-31 15:00:14 +0100651 return rl_result;
652}
653
Serban Constantinescu169489b2014-06-11 16:43:35 +0100654bool Arm64Mir2Lir::GenInlinedAbsLong(CallInfo* info) {
655 RegLocation rl_src = info->args[0];
656 rl_src = LoadValueWide(rl_src, kCoreReg);
657 RegLocation rl_dest = InlineTargetWide(info);
658 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
659 RegStorage sign_reg = AllocTempWide();
660 // abs(x) = y<=x>>63, (x+y)^y.
661 OpRegRegImm(kOpAsr, sign_reg, rl_src.reg, 63);
662 OpRegRegReg(kOpAdd, rl_result.reg, rl_src.reg, sign_reg);
663 OpRegReg(kOpXor, rl_result.reg, sign_reg);
664 StoreValueWide(rl_dest, rl_result);
665 return true;
666}
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100667
Serban Constantinescu23abec92014-07-02 16:13:38 +0100668bool Arm64Mir2Lir::GenInlinedMinMax(CallInfo* info, bool is_min, bool is_long) {
Serban Constantinescu169489b2014-06-11 16:43:35 +0100669 DCHECK_EQ(cu_->instruction_set, kArm64);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100670 RegLocation rl_src1 = info->args[0];
Serban Constantinescu23abec92014-07-02 16:13:38 +0100671 RegLocation rl_src2 = (is_long) ? info->args[2] : info->args[1];
672 rl_src1 = (is_long) ? LoadValueWide(rl_src1, kCoreReg) : LoadValue(rl_src1, kCoreReg);
673 rl_src2 = (is_long) ? LoadValueWide(rl_src2, kCoreReg) : LoadValue(rl_src2, kCoreReg);
674 RegLocation rl_dest = (is_long) ? InlineTargetWide(info) : InlineTarget(info);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100675 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
676 OpRegReg(kOpCmp, rl_src1.reg, rl_src2.reg);
Serban Constantinescu23abec92014-07-02 16:13:38 +0100677 NewLIR4((is_long) ? WIDE(kA64Csel4rrrc) : kA64Csel4rrrc, rl_result.reg.GetReg(),
678 rl_src1.reg.GetReg(), rl_src2.reg.GetReg(), (is_min) ? kArmCondLt : kArmCondGt);
679 (is_long) ? StoreValueWide(rl_dest, rl_result) :StoreValue(rl_dest, rl_result);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100680 return true;
681}
682
683bool Arm64Mir2Lir::GenInlinedPeek(CallInfo* info, OpSize size) {
684 RegLocation rl_src_address = info->args[0]; // long address
Serban Constantinescu63fe93d2014-06-30 17:10:28 +0100685 RegLocation rl_dest = (size == k64) ? InlineTargetWide(info) : InlineTarget(info);
686 RegLocation rl_address = LoadValueWide(rl_src_address, kCoreReg);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100687 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
Serban Constantinescu169489b2014-06-11 16:43:35 +0100688
Andreas Gampe3c12c512014-06-24 18:46:29 +0000689 LoadBaseDisp(rl_address.reg, 0, rl_result.reg, size, kNotVolatile);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100690 if (size == k64) {
Matteo Franchin43ec8732014-03-31 15:00:14 +0100691 StoreValueWide(rl_dest, rl_result);
692 } else {
693 DCHECK(size == kSignedByte || size == kSignedHalf || size == k32);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100694 StoreValue(rl_dest, rl_result);
695 }
696 return true;
697}
698
699bool Arm64Mir2Lir::GenInlinedPoke(CallInfo* info, OpSize size) {
700 RegLocation rl_src_address = info->args[0]; // long address
Matteo Franchin43ec8732014-03-31 15:00:14 +0100701 RegLocation rl_src_value = info->args[2]; // [size] value
Serban Constantinescu63fe93d2014-06-30 17:10:28 +0100702 RegLocation rl_address = LoadValueWide(rl_src_address, kCoreReg);
Serban Constantinescu169489b2014-06-11 16:43:35 +0100703
704 RegLocation rl_value;
Matteo Franchin43ec8732014-03-31 15:00:14 +0100705 if (size == k64) {
Serban Constantinescu169489b2014-06-11 16:43:35 +0100706 rl_value = LoadValueWide(rl_src_value, kCoreReg);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100707 } else {
708 DCHECK(size == kSignedByte || size == kSignedHalf || size == k32);
Serban Constantinescu169489b2014-06-11 16:43:35 +0100709 rl_value = LoadValue(rl_src_value, kCoreReg);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100710 }
Andreas Gampe3c12c512014-06-24 18:46:29 +0000711 StoreBaseDisp(rl_address.reg, 0, rl_value.reg, size, kNotVolatile);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100712 return true;
713}
714
715void Arm64Mir2Lir::OpLea(RegStorage r_base, RegStorage reg1, RegStorage reg2, int scale, int offset) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100716 LOG(FATAL) << "Unexpected use of OpLea for Arm64";
Matteo Franchin43ec8732014-03-31 15:00:14 +0100717}
718
Andreas Gampe2f244e92014-05-08 03:35:25 -0700719void Arm64Mir2Lir::OpTlsCmp(ThreadOffset<4> offset, int val) {
720 UNIMPLEMENTED(FATAL) << "Should not be used.";
721}
722
723void Arm64Mir2Lir::OpTlsCmp(ThreadOffset<8> offset, int val) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100724 LOG(FATAL) << "Unexpected use of OpTlsCmp for Arm64";
Matteo Franchin43ec8732014-03-31 15:00:14 +0100725}
726
727bool Arm64Mir2Lir::GenInlinedCas(CallInfo* info, bool is_long, bool is_object) {
Serban Constantinescu169489b2014-06-11 16:43:35 +0100728 DCHECK_EQ(cu_->instruction_set, kArm64);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100729 // Unused - RegLocation rl_src_unsafe = info->args[0];
730 RegLocation rl_src_obj = info->args[1]; // Object - known non-null
731 RegLocation rl_src_offset = info->args[2]; // long low
Matteo Franchin43ec8732014-03-31 15:00:14 +0100732 RegLocation rl_src_expected = info->args[4]; // int, long or Object
733 // If is_long, high half is in info->args[5]
734 RegLocation rl_src_new_value = info->args[is_long ? 6 : 5]; // int, long or Object
735 // If is_long, high half is in info->args[7]
736 RegLocation rl_dest = InlineTarget(info); // boolean place for result
737
Serban Constantinescu169489b2014-06-11 16:43:35 +0100738 // Load Object and offset
buzbeea0cd2d72014-06-01 09:33:49 -0700739 RegLocation rl_object = LoadValue(rl_src_obj, kRefReg);
Serban Constantinescu63fe93d2014-06-30 17:10:28 +0100740 RegLocation rl_offset = LoadValueWide(rl_src_offset, kCoreReg);
Serban Constantinescu169489b2014-06-11 16:43:35 +0100741
Matteo Franchin43ec8732014-03-31 15:00:14 +0100742 RegLocation rl_new_value;
Serban Constantinescu169489b2014-06-11 16:43:35 +0100743 RegLocation rl_expected;
744 if (is_long) {
Matteo Franchin43ec8732014-03-31 15:00:14 +0100745 rl_new_value = LoadValueWide(rl_src_new_value, kCoreReg);
Serban Constantinescu169489b2014-06-11 16:43:35 +0100746 rl_expected = LoadValueWide(rl_src_expected, kCoreReg);
747 } else {
748 rl_new_value = LoadValue(rl_src_new_value, is_object ? kRefReg : kCoreReg);
749 rl_expected = LoadValue(rl_src_expected, is_object ? kRefReg : kCoreReg);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100750 }
751
752 if (is_object && !mir_graph_->IsConstantNullRef(rl_new_value)) {
753 // Mark card for object assuming new value is stored.
754 MarkGCCard(rl_new_value.reg, rl_object.reg);
755 }
756
Serban Constantinescu169489b2014-06-11 16:43:35 +0100757 RegStorage r_ptr = AllocTempRef();
Matteo Franchin43ec8732014-03-31 15:00:14 +0100758 OpRegRegReg(kOpAdd, r_ptr, rl_object.reg, rl_offset.reg);
759
760 // Free now unneeded rl_object and rl_offset to give more temps.
761 ClobberSReg(rl_object.s_reg_low);
762 FreeTemp(rl_object.reg);
763 ClobberSReg(rl_offset.s_reg_low);
764 FreeTemp(rl_offset.reg);
765
Matteo Franchin43ec8732014-03-31 15:00:14 +0100766 // do {
767 // tmp = [r_ptr] - expected;
768 // } while (tmp == 0 && failure([r_ptr] <- r_new_value));
769 // result = tmp != 0;
770
Serban Constantinescu169489b2014-06-11 16:43:35 +0100771 RegStorage r_tmp;
Serban Constantinescu63fe93d2014-06-30 17:10:28 +0100772 RegStorage r_tmp_stored;
773 RegStorage rl_new_value_stored = rl_new_value.reg;
774 ArmOpcode wide = UNWIDE(0);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100775 if (is_long) {
Serban Constantinescu63fe93d2014-06-30 17:10:28 +0100776 r_tmp_stored = r_tmp = AllocTempWide();
777 wide = WIDE(0);
Serban Constantinescu169489b2014-06-11 16:43:35 +0100778 } else if (is_object) {
Serban Constantinescu63fe93d2014-06-30 17:10:28 +0100779 // References use 64-bit registers, but are stored as compressed 32-bit values.
780 // This means r_tmp_stored != r_tmp.
Serban Constantinescu169489b2014-06-11 16:43:35 +0100781 r_tmp = AllocTempRef();
Serban Constantinescu63fe93d2014-06-30 17:10:28 +0100782 r_tmp_stored = As32BitReg(r_tmp);
783 rl_new_value_stored = As32BitReg(rl_new_value_stored);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100784 } else {
Serban Constantinescu63fe93d2014-06-30 17:10:28 +0100785 r_tmp_stored = r_tmp = AllocTemp();
Matteo Franchin43ec8732014-03-31 15:00:14 +0100786 }
787
Serban Constantinescu63fe93d2014-06-30 17:10:28 +0100788 RegStorage r_tmp32 = (r_tmp.Is32Bit()) ? r_tmp : As32BitReg(r_tmp);
Serban Constantinescu169489b2014-06-11 16:43:35 +0100789 LIR* loop = NewLIR0(kPseudoTargetLabel);
Serban Constantinescu63fe93d2014-06-30 17:10:28 +0100790 NewLIR2(kA64Ldaxr2rX | wide, r_tmp_stored.GetReg(), r_ptr.GetReg());
Serban Constantinescu169489b2014-06-11 16:43:35 +0100791 OpRegReg(kOpCmp, r_tmp, rl_expected.reg);
Vladimir Marko8dea81c2014-06-06 14:50:36 +0100792 DCHECK(last_lir_insn_->u.m.def_mask->HasBit(ResourceMask::kCCode));
Serban Constantinescu169489b2014-06-11 16:43:35 +0100793 LIR* early_exit = OpCondBranch(kCondNe, NULL);
Serban Constantinescu63fe93d2014-06-30 17:10:28 +0100794 NewLIR3(kA64Stlxr3wrX | wide, r_tmp32.GetReg(), rl_new_value_stored.GetReg(), r_ptr.GetReg());
795 NewLIR3(kA64Cmp3RdT, r_tmp32.GetReg(), 0, ENCODE_NO_SHIFT);
Serban Constantinescu169489b2014-06-11 16:43:35 +0100796 DCHECK(last_lir_insn_->u.m.def_mask->HasBit(ResourceMask::kCCode));
797 OpCondBranch(kCondNe, loop);
798
Serban Constantinescu63fe93d2014-06-30 17:10:28 +0100799 LIR* exit_loop = NewLIR0(kPseudoTargetLabel);
800 early_exit->target = exit_loop;
801
Serban Constantinescu169489b2014-06-11 16:43:35 +0100802 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
Serban Constantinescu63fe93d2014-06-30 17:10:28 +0100803 NewLIR4(kA64Csinc4rrrc, rl_result.reg.GetReg(), rwzr, rwzr, kArmCondNe);
Serban Constantinescu169489b2014-06-11 16:43:35 +0100804
Matteo Franchin43ec8732014-03-31 15:00:14 +0100805 FreeTemp(r_tmp); // Now unneeded.
Serban Constantinescu169489b2014-06-11 16:43:35 +0100806 FreeTemp(r_ptr); // Now unneeded.
Matteo Franchin43ec8732014-03-31 15:00:14 +0100807
808 StoreValue(rl_dest, rl_result);
809
Matteo Franchin43ec8732014-03-31 15:00:14 +0100810 return true;
811}
812
813LIR* Arm64Mir2Lir::OpPcRelLoad(RegStorage reg, LIR* target) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100814 return RawLIR(current_dalvik_offset_, WIDE(kA64Ldr2rp), reg.GetReg(), 0, 0, 0, 0, target);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100815}
816
817LIR* Arm64Mir2Lir::OpVldm(RegStorage r_base, int count) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100818 LOG(FATAL) << "Unexpected use of OpVldm for Arm64";
819 return NULL;
Matteo Franchin43ec8732014-03-31 15:00:14 +0100820}
821
822LIR* Arm64Mir2Lir::OpVstm(RegStorage r_base, int count) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100823 LOG(FATAL) << "Unexpected use of OpVstm for Arm64";
824 return NULL;
Matteo Franchin43ec8732014-03-31 15:00:14 +0100825}
826
827void Arm64Mir2Lir::GenMultiplyByTwoBitMultiplier(RegLocation rl_src,
828 RegLocation rl_result, int lit,
829 int first_bit, int second_bit) {
Serban Constantinescued65c5e2014-05-22 15:10:18 +0100830 OpRegRegRegShift(kOpAdd, rl_result.reg, rl_src.reg, rl_src.reg, EncodeShift(kA64Lsl, second_bit - first_bit));
Matteo Franchin43ec8732014-03-31 15:00:14 +0100831 if (first_bit != 0) {
832 OpRegRegImm(kOpLsl, rl_result.reg, rl_result.reg, first_bit);
833 }
834}
835
836void Arm64Mir2Lir::GenDivZeroCheckWide(RegStorage reg) {
Serban Constantinescued65c5e2014-05-22 15:10:18 +0100837 LOG(FATAL) << "Unexpected use of GenDivZero for Arm64";
Matteo Franchin43ec8732014-03-31 15:00:14 +0100838}
839
840// Test suspend flag, return target of taken suspend branch
841LIR* Arm64Mir2Lir::OpTestSuspend(LIR* target) {
Zheng Xubaa7c882014-06-30 14:26:50 +0800842 NewLIR3(kA64Subs3rRd, rwSUSPEND, rwSUSPEND, 1);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100843 return OpCondBranch((target == NULL) ? kCondEq : kCondNe, target);
844}
845
846// Decrement register and branch on condition
847LIR* Arm64Mir2Lir::OpDecAndBranch(ConditionCode c_code, RegStorage reg, LIR* target) {
buzbee33ae5582014-06-12 14:56:32 -0700848 // Combine sub & test using sub setflags encoding here. We need to make sure a
849 // subtract form that sets carry is used, so generate explicitly.
850 // TODO: might be best to add a new op, kOpSubs, and handle it generically.
851 ArmOpcode opcode = reg.Is64Bit() ? WIDE(kA64Subs3rRd) : UNWIDE(kA64Subs3rRd);
852 NewLIR3(opcode, reg.GetReg(), reg.GetReg(), 1); // For value == 1, this should set flags.
Vladimir Marko8dea81c2014-06-06 14:50:36 +0100853 DCHECK(last_lir_insn_->u.m.def_mask->HasBit(ResourceMask::kCCode));
Matteo Franchin43ec8732014-03-31 15:00:14 +0100854 return OpCondBranch(c_code, target);
855}
856
Andreas Gampeb14329f2014-05-15 11:16:06 -0700857bool Arm64Mir2Lir::GenMemBarrier(MemBarrierKind barrier_kind) {
Matteo Franchin43ec8732014-03-31 15:00:14 +0100858#if ANDROID_SMP != 0
859 // Start off with using the last LIR as the barrier. If it is not enough, then we will generate one.
860 LIR* barrier = last_lir_insn_;
861
862 int dmb_flavor;
863 // TODO: revisit Arm barrier kinds
864 switch (barrier_kind) {
Hans Boehm48f5c472014-06-27 14:50:10 -0700865 case kAnyStore: dmb_flavor = kISH; break;
866 case kLoadAny: dmb_flavor = kISH; break;
867 // We conjecture that kISHLD is insufficient. It is documented
868 // to provide LoadLoad | StoreStore ordering. But if this were used
869 // to implement volatile loads, we suspect that the lack of store
870 // atomicity on ARM would cause us to allow incorrect results for
871 // the canonical IRIW example. But we're not sure.
872 // We should be using acquire loads instead.
Matteo Franchin43ec8732014-03-31 15:00:14 +0100873 case kStoreStore: dmb_flavor = kISHST; break;
Hans Boehm48f5c472014-06-27 14:50:10 -0700874 case kAnyAny: dmb_flavor = kISH; break;
Matteo Franchin43ec8732014-03-31 15:00:14 +0100875 default:
876 LOG(FATAL) << "Unexpected MemBarrierKind: " << barrier_kind;
877 dmb_flavor = kSY; // quiet gcc.
878 break;
879 }
880
Andreas Gampeb14329f2014-05-15 11:16:06 -0700881 bool ret = false;
882
Matteo Franchin43ec8732014-03-31 15:00:14 +0100883 // If the same barrier already exists, don't generate another.
884 if (barrier == nullptr
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100885 || (barrier->opcode != kA64Dmb1B || barrier->operands[0] != dmb_flavor)) {
886 barrier = NewLIR1(kA64Dmb1B, dmb_flavor);
Andreas Gampeb14329f2014-05-15 11:16:06 -0700887 ret = true;
Matteo Franchin43ec8732014-03-31 15:00:14 +0100888 }
889
890 // At this point we must have a memory barrier. Mark it as a scheduling barrier as well.
891 DCHECK(!barrier->flags.use_def_invalid);
Vladimir Marko8dea81c2014-06-06 14:50:36 +0100892 barrier->u.m.def_mask = &kEncodeAll;
Andreas Gampeb14329f2014-05-15 11:16:06 -0700893 return ret;
894#else
895 return false;
Matteo Franchin43ec8732014-03-31 15:00:14 +0100896#endif
897}
898
Serban Constantinescued65c5e2014-05-22 15:10:18 +0100899void Arm64Mir2Lir::GenIntToLong(RegLocation rl_dest, RegLocation rl_src) {
900 RegLocation rl_result;
901
902 rl_src = LoadValue(rl_src, kCoreReg);
903 rl_result = EvalLocWide(rl_dest, kCoreReg, true);
Andreas Gampe4b537a82014-06-30 22:24:53 -0700904 NewLIR4(WIDE(kA64Sbfm4rrdd), rl_result.reg.GetReg(), As64BitReg(rl_src.reg).GetReg(), 0, 31);
Serban Constantinescued65c5e2014-05-22 15:10:18 +0100905 StoreValueWide(rl_dest, rl_result);
906}
907
908void Arm64Mir2Lir::GenDivRemLong(Instruction::Code opcode, RegLocation rl_dest,
909 RegLocation rl_src1, RegLocation rl_src2, bool is_div) {
Matteo Franchin7c6c2ac2014-07-01 18:03:08 +0100910 if (rl_src2.is_const) {
911 DCHECK(rl_src2.wide);
912 int64_t lit = mir_graph_->ConstantValueWide(rl_src2);
913 if (HandleEasyDivRem64(opcode, is_div, rl_src1, rl_dest, lit)) {
914 return;
915 }
916 }
917
Serban Constantinescued65c5e2014-05-22 15:10:18 +0100918 RegLocation rl_result;
919 rl_src1 = LoadValueWide(rl_src1, kCoreReg);
920 rl_src2 = LoadValueWide(rl_src2, kCoreReg);
921 GenDivZeroCheck(rl_src2.reg);
922 rl_result = GenDivRem(rl_dest, rl_src1.reg, rl_src2.reg, is_div);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100923 StoreValueWide(rl_dest, rl_result);
924}
925
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100926void Arm64Mir2Lir::GenLongOp(OpKind op, RegLocation rl_dest, RegLocation rl_src1,
927 RegLocation rl_src2) {
928 RegLocation rl_result;
Serban Constantinescued65c5e2014-05-22 15:10:18 +0100929
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100930 rl_src1 = LoadValueWide(rl_src1, kCoreReg);
931 rl_src2 = LoadValueWide(rl_src2, kCoreReg);
932 rl_result = EvalLocWide(rl_dest, kCoreReg, true);
Serban Constantinescued65c5e2014-05-22 15:10:18 +0100933 OpRegRegRegShift(op, rl_result.reg, rl_src1.reg, rl_src2.reg, ENCODE_NO_SHIFT);
934 StoreValueWide(rl_dest, rl_result);
935}
936
937void Arm64Mir2Lir::GenNegLong(RegLocation rl_dest, RegLocation rl_src) {
938 RegLocation rl_result;
939
940 rl_src = LoadValueWide(rl_src, kCoreReg);
941 rl_result = EvalLocWide(rl_dest, kCoreReg, true);
942 OpRegRegShift(kOpNeg, rl_result.reg, rl_src.reg, ENCODE_NO_SHIFT);
943 StoreValueWide(rl_dest, rl_result);
944}
945
946void Arm64Mir2Lir::GenNotLong(RegLocation rl_dest, RegLocation rl_src) {
947 RegLocation rl_result;
948
949 rl_src = LoadValueWide(rl_src, kCoreReg);
950 rl_result = EvalLocWide(rl_dest, kCoreReg, true);
951 OpRegRegShift(kOpMvn, rl_result.reg, rl_src.reg, ENCODE_NO_SHIFT);
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100952 StoreValueWide(rl_dest, rl_result);
953}
954
Matteo Franchin43ec8732014-03-31 15:00:14 +0100955void Arm64Mir2Lir::GenMulLong(Instruction::Code opcode, RegLocation rl_dest,
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100956 RegLocation rl_src1, RegLocation rl_src2) {
957 GenLongOp(kOpMul, rl_dest, rl_src1, rl_src2);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100958}
959
960void Arm64Mir2Lir::GenAddLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1,
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100961 RegLocation rl_src2) {
962 GenLongOp(kOpAdd, rl_dest, rl_src1, rl_src2);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100963}
964
965void Arm64Mir2Lir::GenSubLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1,
966 RegLocation rl_src2) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100967 GenLongOp(kOpSub, rl_dest, rl_src1, rl_src2);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100968}
969
970void Arm64Mir2Lir::GenAndLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1,
971 RegLocation rl_src2) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100972 GenLongOp(kOpAnd, rl_dest, rl_src1, rl_src2);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100973}
974
975void Arm64Mir2Lir::GenOrLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1,
976 RegLocation rl_src2) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100977 GenLongOp(kOpOr, rl_dest, rl_src1, rl_src2);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100978}
979
980void Arm64Mir2Lir::GenXorLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1,
981 RegLocation rl_src2) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100982 GenLongOp(kOpXor, rl_dest, rl_src1, rl_src2);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100983}
984
985/*
986 * Generate array load
987 */
988void Arm64Mir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array,
989 RegLocation rl_index, RegLocation rl_dest, int scale) {
990 RegisterClass reg_class = RegClassBySize(size);
991 int len_offset = mirror::Array::LengthOffset().Int32Value();
992 int data_offset;
993 RegLocation rl_result;
994 bool constant_index = rl_index.is_const;
buzbeea0cd2d72014-06-01 09:33:49 -0700995 rl_array = LoadValue(rl_array, kRefReg);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100996 if (!constant_index) {
997 rl_index = LoadValue(rl_index, kCoreReg);
998 }
999
1000 if (rl_dest.wide) {
1001 data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Int32Value();
1002 } else {
1003 data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Int32Value();
1004 }
1005
1006 // If index is constant, just fold it into the data offset
1007 if (constant_index) {
1008 data_offset += mir_graph_->ConstantValue(rl_index) << scale;
1009 }
1010
1011 /* null object? */
1012 GenNullCheck(rl_array.reg, opt_flags);
1013
1014 bool needs_range_check = (!(opt_flags & MIR_IGNORE_RANGE_CHECK));
1015 RegStorage reg_len;
1016 if (needs_range_check) {
1017 reg_len = AllocTemp();
1018 /* Get len */
1019 Load32Disp(rl_array.reg, len_offset, reg_len);
1020 MarkPossibleNullPointerException(opt_flags);
1021 } else {
1022 ForceImplicitNullCheck(rl_array.reg, opt_flags);
1023 }
1024 if (rl_dest.wide || rl_dest.fp || constant_index) {
1025 RegStorage reg_ptr;
1026 if (constant_index) {
1027 reg_ptr = rl_array.reg; // NOTE: must not alter reg_ptr in constant case.
1028 } else {
1029 // No special indexed operation, lea + load w/ displacement
buzbeea0cd2d72014-06-01 09:33:49 -07001030 reg_ptr = AllocTempRef();
buzbee33ae5582014-06-12 14:56:32 -07001031 OpRegRegRegShift(kOpAdd, reg_ptr, rl_array.reg, As64BitReg(rl_index.reg),
1032 EncodeShift(kA64Lsl, scale));
Matteo Franchin43ec8732014-03-31 15:00:14 +01001033 FreeTemp(rl_index.reg);
1034 }
1035 rl_result = EvalLoc(rl_dest, reg_class, true);
1036
1037 if (needs_range_check) {
1038 if (constant_index) {
1039 GenArrayBoundsCheck(mir_graph_->ConstantValue(rl_index), reg_len);
1040 } else {
1041 GenArrayBoundsCheck(rl_index.reg, reg_len);
1042 }
1043 FreeTemp(reg_len);
1044 }
Andreas Gampe3c12c512014-06-24 18:46:29 +00001045 if (rl_result.ref) {
1046 LoadRefDisp(reg_ptr, data_offset, rl_result.reg, kNotVolatile);
1047 } else {
1048 LoadBaseDisp(reg_ptr, data_offset, rl_result.reg, size, kNotVolatile);
1049 }
Vladimir Marko455759b2014-05-06 20:49:36 +01001050 MarkPossibleNullPointerException(opt_flags);
1051 if (!constant_index) {
1052 FreeTemp(reg_ptr);
1053 }
Matteo Franchin43ec8732014-03-31 15:00:14 +01001054 if (rl_dest.wide) {
Matteo Franchin43ec8732014-03-31 15:00:14 +01001055 StoreValueWide(rl_dest, rl_result);
1056 } else {
Matteo Franchin43ec8732014-03-31 15:00:14 +01001057 StoreValue(rl_dest, rl_result);
1058 }
1059 } else {
1060 // Offset base, then use indexed load
buzbeea0cd2d72014-06-01 09:33:49 -07001061 RegStorage reg_ptr = AllocTempRef();
Matteo Franchin43ec8732014-03-31 15:00:14 +01001062 OpRegRegImm(kOpAdd, reg_ptr, rl_array.reg, data_offset);
1063 FreeTemp(rl_array.reg);
1064 rl_result = EvalLoc(rl_dest, reg_class, true);
1065
1066 if (needs_range_check) {
1067 GenArrayBoundsCheck(rl_index.reg, reg_len);
1068 FreeTemp(reg_len);
1069 }
Andreas Gampe3c12c512014-06-24 18:46:29 +00001070 if (rl_result.ref) {
Matteo Franchin255e0142014-07-04 13:50:41 +01001071 LoadRefIndexed(reg_ptr, As64BitReg(rl_index.reg), rl_result.reg, scale);
Andreas Gampe3c12c512014-06-24 18:46:29 +00001072 } else {
1073 LoadBaseIndexed(reg_ptr, As64BitReg(rl_index.reg), rl_result.reg, scale, size);
1074 }
Matteo Franchin43ec8732014-03-31 15:00:14 +01001075 MarkPossibleNullPointerException(opt_flags);
1076 FreeTemp(reg_ptr);
1077 StoreValue(rl_dest, rl_result);
1078 }
1079}
1080
1081/*
1082 * Generate array store
1083 *
1084 */
1085void Arm64Mir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array,
1086 RegLocation rl_index, RegLocation rl_src, int scale, bool card_mark) {
1087 RegisterClass reg_class = RegClassBySize(size);
1088 int len_offset = mirror::Array::LengthOffset().Int32Value();
1089 bool constant_index = rl_index.is_const;
1090
1091 int data_offset;
1092 if (size == k64 || size == kDouble) {
1093 data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Int32Value();
1094 } else {
1095 data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Int32Value();
1096 }
1097
1098 // If index is constant, just fold it into the data offset.
1099 if (constant_index) {
1100 data_offset += mir_graph_->ConstantValue(rl_index) << scale;
1101 }
1102
buzbeea0cd2d72014-06-01 09:33:49 -07001103 rl_array = LoadValue(rl_array, kRefReg);
Matteo Franchin43ec8732014-03-31 15:00:14 +01001104 if (!constant_index) {
1105 rl_index = LoadValue(rl_index, kCoreReg);
1106 }
1107
1108 RegStorage reg_ptr;
1109 bool allocated_reg_ptr_temp = false;
1110 if (constant_index) {
1111 reg_ptr = rl_array.reg;
1112 } else if (IsTemp(rl_array.reg) && !card_mark) {
1113 Clobber(rl_array.reg);
1114 reg_ptr = rl_array.reg;
1115 } else {
1116 allocated_reg_ptr_temp = true;
buzbeea0cd2d72014-06-01 09:33:49 -07001117 reg_ptr = AllocTempRef();
Matteo Franchin43ec8732014-03-31 15:00:14 +01001118 }
1119
1120 /* null object? */
1121 GenNullCheck(rl_array.reg, opt_flags);
1122
1123 bool needs_range_check = (!(opt_flags & MIR_IGNORE_RANGE_CHECK));
1124 RegStorage reg_len;
1125 if (needs_range_check) {
1126 reg_len = AllocTemp();
1127 // NOTE: max live temps(4) here.
1128 /* Get len */
1129 Load32Disp(rl_array.reg, len_offset, reg_len);
1130 MarkPossibleNullPointerException(opt_flags);
1131 } else {
1132 ForceImplicitNullCheck(rl_array.reg, opt_flags);
1133 }
1134 /* at this point, reg_ptr points to array, 2 live temps */
1135 if (rl_src.wide || rl_src.fp || constant_index) {
1136 if (rl_src.wide) {
1137 rl_src = LoadValueWide(rl_src, reg_class);
1138 } else {
1139 rl_src = LoadValue(rl_src, reg_class);
1140 }
1141 if (!constant_index) {
buzbee33ae5582014-06-12 14:56:32 -07001142 OpRegRegRegShift(kOpAdd, reg_ptr, rl_array.reg, As64BitReg(rl_index.reg),
1143 EncodeShift(kA64Lsl, scale));
Matteo Franchin43ec8732014-03-31 15:00:14 +01001144 }
1145 if (needs_range_check) {
1146 if (constant_index) {
1147 GenArrayBoundsCheck(mir_graph_->ConstantValue(rl_index), reg_len);
1148 } else {
1149 GenArrayBoundsCheck(rl_index.reg, reg_len);
1150 }
1151 FreeTemp(reg_len);
1152 }
Andreas Gampe3c12c512014-06-24 18:46:29 +00001153 if (rl_src.ref) {
1154 StoreRefDisp(reg_ptr, data_offset, rl_src.reg, kNotVolatile);
1155 } else {
1156 StoreBaseDisp(reg_ptr, data_offset, rl_src.reg, size, kNotVolatile);
1157 }
Matteo Franchin43ec8732014-03-31 15:00:14 +01001158 MarkPossibleNullPointerException(opt_flags);
1159 } else {
1160 /* reg_ptr -> array data */
1161 OpRegRegImm(kOpAdd, reg_ptr, rl_array.reg, data_offset);
1162 rl_src = LoadValue(rl_src, reg_class);
1163 if (needs_range_check) {
1164 GenArrayBoundsCheck(rl_index.reg, reg_len);
1165 FreeTemp(reg_len);
1166 }
Andreas Gampe3c12c512014-06-24 18:46:29 +00001167 if (rl_src.ref) {
Matteo Franchin255e0142014-07-04 13:50:41 +01001168 StoreRefIndexed(reg_ptr, As64BitReg(rl_index.reg), rl_src.reg, scale);
Andreas Gampe3c12c512014-06-24 18:46:29 +00001169 } else {
1170 StoreBaseIndexed(reg_ptr, As64BitReg(rl_index.reg), rl_src.reg, scale, size);
1171 }
Matteo Franchin43ec8732014-03-31 15:00:14 +01001172 MarkPossibleNullPointerException(opt_flags);
1173 }
1174 if (allocated_reg_ptr_temp) {
1175 FreeTemp(reg_ptr);
1176 }
1177 if (card_mark) {
1178 MarkGCCard(rl_src.reg, rl_array.reg);
1179 }
1180}
1181
Matteo Franchin43ec8732014-03-31 15:00:14 +01001182void Arm64Mir2Lir::GenShiftImmOpLong(Instruction::Code opcode,
Matteo Franchin7c6c2ac2014-07-01 18:03:08 +01001183 RegLocation rl_dest, RegLocation rl_src, RegLocation rl_shift) {
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001184 OpKind op = kOpBkpt;
Matteo Franchin43ec8732014-03-31 15:00:14 +01001185 // Per spec, we only care about low 6 bits of shift amount.
1186 int shift_amount = mir_graph_->ConstantValue(rl_shift) & 0x3f;
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001187 rl_src = LoadValueWide(rl_src, kCoreReg);
Matteo Franchin43ec8732014-03-31 15:00:14 +01001188 if (shift_amount == 0) {
1189 StoreValueWide(rl_dest, rl_src);
1190 return;
1191 }
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001192
1193 RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true);
Matteo Franchin43ec8732014-03-31 15:00:14 +01001194 switch (opcode) {
1195 case Instruction::SHL_LONG:
1196 case Instruction::SHL_LONG_2ADDR:
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001197 op = kOpLsl;
Matteo Franchin43ec8732014-03-31 15:00:14 +01001198 break;
1199 case Instruction::SHR_LONG:
1200 case Instruction::SHR_LONG_2ADDR:
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001201 op = kOpAsr;
Matteo Franchin43ec8732014-03-31 15:00:14 +01001202 break;
1203 case Instruction::USHR_LONG:
1204 case Instruction::USHR_LONG_2ADDR:
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001205 op = kOpLsr;
Matteo Franchin43ec8732014-03-31 15:00:14 +01001206 break;
1207 default:
1208 LOG(FATAL) << "Unexpected case";
1209 }
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001210 OpRegRegImm(op, rl_result.reg, rl_src.reg, shift_amount);
Matteo Franchin43ec8732014-03-31 15:00:14 +01001211 StoreValueWide(rl_dest, rl_result);
1212}
1213
Matteo Franchine45fb9e2014-05-06 10:10:30 +01001214void Arm64Mir2Lir::GenArithImmOpLong(Instruction::Code opcode, RegLocation rl_dest,
1215 RegLocation rl_src1, RegLocation rl_src2) {
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001216 if ((opcode == Instruction::SUB_LONG) || (opcode == Instruction::SUB_LONG_2ADDR)) {
Matteo Franchin43ec8732014-03-31 15:00:14 +01001217 if (!rl_src2.is_const) {
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001218 return GenArithOpLong(opcode, rl_dest, rl_src1, rl_src2);
Matteo Franchin43ec8732014-03-31 15:00:14 +01001219 }
1220 } else {
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001221 // Associativity.
Matteo Franchin43ec8732014-03-31 15:00:14 +01001222 if (!rl_src2.is_const) {
1223 DCHECK(rl_src1.is_const);
1224 std::swap(rl_src1, rl_src2);
1225 }
1226 }
Matteo Franchin43ec8732014-03-31 15:00:14 +01001227 DCHECK(rl_src2.is_const);
Matteo Franchin43ec8732014-03-31 15:00:14 +01001228
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001229 OpKind op = kOpBkpt;
1230 int64_t val = mir_graph_->ConstantValueWide(rl_src2);
1231
Matteo Franchin43ec8732014-03-31 15:00:14 +01001232 switch (opcode) {
1233 case Instruction::ADD_LONG:
1234 case Instruction::ADD_LONG_2ADDR:
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001235 op = kOpAdd;
1236 break;
Matteo Franchin43ec8732014-03-31 15:00:14 +01001237 case Instruction::SUB_LONG:
1238 case Instruction::SUB_LONG_2ADDR:
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001239 op = kOpSub;
Matteo Franchin43ec8732014-03-31 15:00:14 +01001240 break;
1241 case Instruction::AND_LONG:
1242 case Instruction::AND_LONG_2ADDR:
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001243 op = kOpAnd;
Matteo Franchin43ec8732014-03-31 15:00:14 +01001244 break;
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001245 case Instruction::OR_LONG:
1246 case Instruction::OR_LONG_2ADDR:
1247 op = kOpOr;
Matteo Franchin43ec8732014-03-31 15:00:14 +01001248 break;
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001249 case Instruction::XOR_LONG:
1250 case Instruction::XOR_LONG_2ADDR:
1251 op = kOpXor;
1252 break;
Matteo Franchin43ec8732014-03-31 15:00:14 +01001253 default:
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001254 LOG(FATAL) << "Unexpected opcode";
Matteo Franchin43ec8732014-03-31 15:00:14 +01001255 }
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001256
1257 rl_src1 = LoadValueWide(rl_src1, kCoreReg);
1258 RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true);
Zheng Xue2eb29e2014-06-12 10:22:33 +08001259 OpRegRegImm64(op, rl_result.reg, rl_src1.reg, val);
Matteo Franchin43ec8732014-03-31 15:00:14 +01001260 StoreValueWide(rl_dest, rl_result);
1261}
1262
Matteo Franchine45fb9e2014-05-06 10:10:30 +01001263/**
1264 * @brief Split a register list in pairs or registers.
1265 *
1266 * Given a list of registers in @p reg_mask, split the list in pairs. Use as follows:
1267 * @code
1268 * int reg1 = -1, reg2 = -1;
1269 * while (reg_mask) {
1270 * reg_mask = GenPairWise(reg_mask, & reg1, & reg2);
1271 * if (UNLIKELY(reg2 < 0)) {
1272 * // Single register in reg1.
1273 * } else {
1274 * // Pair in reg1, reg2.
1275 * }
1276 * }
1277 * @endcode
1278 */
1279uint32_t Arm64Mir2Lir::GenPairWise(uint32_t reg_mask, int* reg1, int* reg2) {
1280 // Find first register.
1281 int first_bit_set = __builtin_ctz(reg_mask) + 1;
1282 int reg = *reg1 + first_bit_set;
1283 reg_mask >>= first_bit_set;
1284
1285 if (LIKELY(reg_mask)) {
1286 // Save the first register, find the second and use the pair opcode.
1287 int second_bit_set = __builtin_ctz(reg_mask) + 1;
1288 *reg2 = reg;
1289 reg_mask >>= second_bit_set;
1290 *reg1 = reg + second_bit_set;
1291 return reg_mask;
1292 }
1293
1294 // Use the single opcode, as we just have one register.
1295 *reg1 = reg;
1296 *reg2 = -1;
1297 return reg_mask;
1298}
1299
1300void Arm64Mir2Lir::UnSpillCoreRegs(RegStorage base, int offset, uint32_t reg_mask) {
1301 int reg1 = -1, reg2 = -1;
Matteo Franchinbc6d1972014-05-13 12:33:28 +01001302 const int reg_log2_size = 3;
Matteo Franchine45fb9e2014-05-06 10:10:30 +01001303
Matteo Franchinbc6d1972014-05-13 12:33:28 +01001304 for (offset = (offset >> reg_log2_size); reg_mask; offset += 2) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +01001305 reg_mask = GenPairWise(reg_mask, & reg1, & reg2);
1306 if (UNLIKELY(reg2 < 0)) {
Matteo Franchinbc6d1972014-05-13 12:33:28 +01001307 NewLIR3(WIDE(kA64Ldr3rXD), RegStorage::Solo64(reg1).GetReg(), base.GetReg(), offset);
Matteo Franchine45fb9e2014-05-06 10:10:30 +01001308 } else {
buzbeeb5860fb2014-06-21 15:31:01 -07001309 DCHECK_LE(offset, 63);
Matteo Franchinbc6d1972014-05-13 12:33:28 +01001310 NewLIR4(WIDE(kA64Ldp4rrXD), RegStorage::Solo64(reg2).GetReg(),
1311 RegStorage::Solo64(reg1).GetReg(), base.GetReg(), offset);
Matteo Franchine45fb9e2014-05-06 10:10:30 +01001312 }
1313 }
1314}
1315
1316void Arm64Mir2Lir::SpillCoreRegs(RegStorage base, int offset, uint32_t reg_mask) {
1317 int reg1 = -1, reg2 = -1;
Matteo Franchinbc6d1972014-05-13 12:33:28 +01001318 const int reg_log2_size = 3;
Matteo Franchine45fb9e2014-05-06 10:10:30 +01001319
Matteo Franchinbc6d1972014-05-13 12:33:28 +01001320 for (offset = (offset >> reg_log2_size); reg_mask; offset += 2) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +01001321 reg_mask = GenPairWise(reg_mask, & reg1, & reg2);
1322 if (UNLIKELY(reg2 < 0)) {
Matteo Franchinbc6d1972014-05-13 12:33:28 +01001323 NewLIR3(WIDE(kA64Str3rXD), RegStorage::Solo64(reg1).GetReg(), base.GetReg(), offset);
Matteo Franchine45fb9e2014-05-06 10:10:30 +01001324 } else {
Matteo Franchinbc6d1972014-05-13 12:33:28 +01001325 NewLIR4(WIDE(kA64Stp4rrXD), RegStorage::Solo64(reg2).GetReg(),
1326 RegStorage::Solo64(reg1).GetReg(), base.GetReg(), offset);
1327 }
1328 }
1329}
1330
1331void Arm64Mir2Lir::UnSpillFPRegs(RegStorage base, int offset, uint32_t reg_mask) {
1332 int reg1 = -1, reg2 = -1;
1333 const int reg_log2_size = 3;
1334
1335 for (offset = (offset >> reg_log2_size); reg_mask; offset += 2) {
1336 reg_mask = GenPairWise(reg_mask, & reg1, & reg2);
1337 if (UNLIKELY(reg2 < 0)) {
1338 NewLIR3(FWIDE(kA64Ldr3fXD), RegStorage::FloatSolo64(reg1).GetReg(), base.GetReg(), offset);
1339 } else {
1340 NewLIR4(WIDE(kA64Ldp4ffXD), RegStorage::FloatSolo64(reg2).GetReg(),
1341 RegStorage::FloatSolo64(reg1).GetReg(), base.GetReg(), offset);
1342 }
1343 }
1344}
1345
1346// TODO(Arm64): consider using ld1 and st1?
1347void Arm64Mir2Lir::SpillFPRegs(RegStorage base, int offset, uint32_t reg_mask) {
1348 int reg1 = -1, reg2 = -1;
1349 const int reg_log2_size = 3;
1350
1351 for (offset = (offset >> reg_log2_size); reg_mask; offset += 2) {
1352 reg_mask = GenPairWise(reg_mask, & reg1, & reg2);
1353 if (UNLIKELY(reg2 < 0)) {
1354 NewLIR3(FWIDE(kA64Str3fXD), RegStorage::FloatSolo64(reg1).GetReg(), base.GetReg(), offset);
1355 } else {
1356 NewLIR4(WIDE(kA64Stp4ffXD), RegStorage::FloatSolo64(reg2).GetReg(),
1357 RegStorage::FloatSolo64(reg1).GetReg(), base.GetReg(), offset);
Matteo Franchine45fb9e2014-05-06 10:10:30 +01001358 }
1359 }
1360}
1361
Serban Constantinescu23abec92014-07-02 16:13:38 +01001362bool Arm64Mir2Lir::GenInlinedReverseBits(CallInfo* info, OpSize size) {
1363 ArmOpcode wide = (size == k64) ? WIDE(0) : UNWIDE(0);
1364 RegLocation rl_src_i = info->args[0];
1365 RegLocation rl_dest = (size == k64) ? InlineTargetWide(info) : InlineTarget(info); // result reg
1366 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
1367 RegLocation rl_i = (size == k64) ? LoadValueWide(rl_src_i, kCoreReg) : LoadValue(rl_src_i, kCoreReg);
1368 NewLIR2(kA64Rbit2rr | wide, rl_result.reg.GetReg(), rl_i.reg.GetReg());
1369 (size == k64) ? StoreValueWide(rl_dest, rl_result) : StoreValue(rl_dest, rl_result);
1370 return true;
1371}
1372
Matteo Franchin43ec8732014-03-31 15:00:14 +01001373} // namespace art