blob: e8ee28d90274063a679e197804a32f6dc1ff5e10 [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
Elliott Hughes8366ca02014-11-17 12:02:05 -080019#include "arch/instruction_set_features.h"
Matteo Franchin43ec8732014-03-31 15:00:14 +010020#include "arm64_lir.h"
21#include "codegen_arm64.h"
22#include "dex/quick/mir_to_lir-inl.h"
buzbeeb5860fb2014-06-21 15:31:01 -070023#include "dex/reg_storage_eq.h"
Matteo Franchin43ec8732014-03-31 15:00:14 +010024#include "entrypoints/quick/quick_entrypoints.h"
Ian Rogers7e70b002014-10-08 11:47:24 -070025#include "mirror/array-inl.h"
Andreas Gampef29ecd62014-07-29 00:35:00 -070026#include "utils.h"
Matteo Franchin43ec8732014-03-31 15:00:14 +010027
28namespace art {
29
30LIR* Arm64Mir2Lir::OpCmpBranch(ConditionCode cond, RegStorage src1, RegStorage src2, LIR* target) {
31 OpRegReg(kOpCmp, src1, src2);
32 return OpCondBranch(cond, target);
33}
34
Matteo Franchin43ec8732014-03-31 15:00:14 +010035LIR* Arm64Mir2Lir::OpIT(ConditionCode ccode, const char* guide) {
Ian Rogers6a3c1fc2014-10-31 00:33:20 -070036 UNUSED(ccode, guide);
Matteo Franchine45fb9e2014-05-06 10:10:30 +010037 LOG(FATAL) << "Unexpected use of OpIT for Arm64";
Ian Rogers6a3c1fc2014-10-31 00:33:20 -070038 UNREACHABLE();
Matteo Franchin43ec8732014-03-31 15:00:14 +010039}
40
41void Arm64Mir2Lir::OpEndIT(LIR* it) {
Ian Rogers6a3c1fc2014-10-31 00:33:20 -070042 UNUSED(it);
Matteo Franchine45fb9e2014-05-06 10:10:30 +010043 LOG(FATAL) << "Unexpected use of OpEndIT for Arm64";
Matteo Franchin43ec8732014-03-31 15:00:14 +010044}
45
46/*
47 * 64-bit 3way compare function.
Matteo Franchine45fb9e2014-05-06 10:10:30 +010048 * cmp xA, xB
Zheng Xu511c8a62014-06-03 16:22:23 +080049 * csinc wC, wzr, wzr, eq // wC = (xA == xB) ? 0 : 1
50 * csneg wC, wC, wC, ge // wC = (xA >= xB) ? wC : -wC
Matteo Franchin43ec8732014-03-31 15:00:14 +010051 */
Matteo Franchine45fb9e2014-05-06 10:10:30 +010052void Arm64Mir2Lir::GenCmpLong(RegLocation rl_dest, RegLocation rl_src1,
53 RegLocation rl_src2) {
54 RegLocation rl_result;
Matteo Franchin43ec8732014-03-31 15:00:14 +010055 rl_src1 = LoadValueWide(rl_src1, kCoreReg);
56 rl_src2 = LoadValueWide(rl_src2, kCoreReg);
Matteo Franchine45fb9e2014-05-06 10:10:30 +010057 rl_result = EvalLoc(rl_dest, kCoreReg, true);
Matteo Franchin43ec8732014-03-31 15:00:14 +010058
Matteo Franchine45fb9e2014-05-06 10:10:30 +010059 OpRegReg(kOpCmp, rl_src1.reg, rl_src2.reg);
Zheng Xu511c8a62014-06-03 16:22:23 +080060 NewLIR4(kA64Csinc4rrrc, rl_result.reg.GetReg(), rwzr, rwzr, kArmCondEq);
61 NewLIR4(kA64Csneg4rrrc, rl_result.reg.GetReg(), rl_result.reg.GetReg(),
62 rl_result.reg.GetReg(), kArmCondGe);
63 StoreValue(rl_dest, rl_result);
Serban Constantinescued65c5e2014-05-22 15:10:18 +010064}
65
66void Arm64Mir2Lir::GenShiftOpLong(Instruction::Code opcode, RegLocation rl_dest,
67 RegLocation rl_src1, RegLocation rl_shift) {
68 OpKind op = kOpBkpt;
69 switch (opcode) {
70 case Instruction::SHL_LONG:
71 case Instruction::SHL_LONG_2ADDR:
72 op = kOpLsl;
73 break;
74 case Instruction::SHR_LONG:
75 case Instruction::SHR_LONG_2ADDR:
76 op = kOpAsr;
77 break;
78 case Instruction::USHR_LONG:
79 case Instruction::USHR_LONG_2ADDR:
80 op = kOpLsr;
81 break;
82 default:
83 LOG(FATAL) << "Unexpected case: " << opcode;
84 }
Zheng Xue2eb29e2014-06-12 10:22:33 +080085 rl_shift = LoadValue(rl_shift, kCoreReg);
Serban Constantinescued65c5e2014-05-22 15:10:18 +010086 rl_src1 = LoadValueWide(rl_src1, kCoreReg);
87 RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true);
Zheng Xue2eb29e2014-06-12 10:22:33 +080088 OpRegRegReg(op, rl_result.reg, rl_src1.reg, As64BitReg(rl_shift.reg));
Serban Constantinescued65c5e2014-05-22 15:10:18 +010089 StoreValueWide(rl_dest, rl_result);
Matteo Franchin43ec8732014-03-31 15:00:14 +010090}
91
Andreas Gampe90969af2014-07-15 23:02:11 -070092static constexpr bool kUseDeltaEncodingInGenSelect = false;
Andreas Gampe381f8ac2014-07-10 03:23:41 -070093
Andreas Gampe90969af2014-07-15 23:02:11 -070094void Arm64Mir2Lir::GenSelect(int32_t true_val, int32_t false_val, ConditionCode ccode,
95 RegStorage rs_dest, int result_reg_class) {
96 if (false_val == 0 || // 0 is better as first operand.
97 true_val == 1 || // Potentially Csinc.
98 true_val == -1 || // Potentially Csinv.
99 true_val == false_val + 1) { // Potentially Csinc.
100 ccode = NegateComparison(ccode);
101 std::swap(true_val, false_val);
102 }
103
104 ArmConditionCode code = ArmConditionEncoding(ccode);
105
106 int opcode; // The opcode.
107 RegStorage left_op = RegStorage::InvalidReg(); // The operands.
108 RegStorage right_op = RegStorage::InvalidReg(); // The operands.
109
110 bool is_wide = rs_dest.Is64Bit();
111
112 RegStorage zero_reg = is_wide ? rs_xzr : rs_wzr;
113
114 if (true_val == 0) {
115 left_op = zero_reg;
116 } else {
117 left_op = rs_dest;
118 LoadConstantNoClobber(rs_dest, true_val);
119 }
120 if (false_val == 1) {
121 right_op = zero_reg;
122 opcode = kA64Csinc4rrrc;
123 } else if (false_val == -1) {
124 right_op = zero_reg;
125 opcode = kA64Csinv4rrrc;
126 } else if (false_val == true_val + 1) {
127 right_op = left_op;
128 opcode = kA64Csinc4rrrc;
129 } else if (false_val == -true_val) {
130 right_op = left_op;
131 opcode = kA64Csneg4rrrc;
132 } else if (false_val == ~true_val) {
133 right_op = left_op;
134 opcode = kA64Csinv4rrrc;
135 } else if (true_val == 0) {
136 // left_op is zero_reg.
137 right_op = rs_dest;
138 LoadConstantNoClobber(rs_dest, false_val);
139 opcode = kA64Csel4rrrc;
140 } else {
141 // Generic case.
142 RegStorage t_reg2 = AllocTypedTemp(false, result_reg_class);
143 if (is_wide) {
144 if (t_reg2.Is32Bit()) {
145 t_reg2 = As64BitReg(t_reg2);
146 }
147 } else {
148 if (t_reg2.Is64Bit()) {
149 t_reg2 = As32BitReg(t_reg2);
150 }
151 }
152
153 if (kUseDeltaEncodingInGenSelect) {
154 int32_t delta = false_val - true_val;
155 uint32_t abs_val = delta < 0 ? -delta : delta;
156
157 if (abs_val < 0x1000) { // TODO: Replace with InexpensiveConstant with opcode.
158 // Can encode as immediate to an add.
159 right_op = t_reg2;
160 OpRegRegImm(kOpAdd, t_reg2, left_op, delta);
161 }
162 }
163
164 // Load as constant.
165 if (!right_op.Valid()) {
166 LoadConstantNoClobber(t_reg2, false_val);
167 right_op = t_reg2;
168 }
169
170 opcode = kA64Csel4rrrc;
171 }
172
173 DCHECK(left_op.Valid() && right_op.Valid());
174 NewLIR4(is_wide ? WIDE(opcode) : opcode, rs_dest.GetReg(), left_op.GetReg(), right_op.GetReg(),
175 code);
176}
177
178void Arm64Mir2Lir::GenSelectConst32(RegStorage left_op, RegStorage right_op, ConditionCode code,
179 int32_t true_val, int32_t false_val, RegStorage rs_dest,
Ian Rogers6a3c1fc2014-10-31 00:33:20 -0700180 RegisterClass dest_reg_class) {
Andreas Gampe90969af2014-07-15 23:02:11 -0700181 DCHECK(rs_dest.Valid());
182 OpRegReg(kOpCmp, left_op, right_op);
183 GenSelect(true_val, false_val, code, rs_dest, dest_reg_class);
184}
185
186void Arm64Mir2Lir::GenSelect(BasicBlock* bb, MIR* mir) {
Ian Rogers6a3c1fc2014-10-31 00:33:20 -0700187 UNUSED(bb);
Andreas Gampe90969af2014-07-15 23:02:11 -0700188 RegLocation rl_src = mir_graph_->GetSrc(mir, 0);
189 rl_src = LoadValue(rl_src, rl_src.ref ? kRefReg : kCoreReg);
Andreas Gampe381f8ac2014-07-10 03:23:41 -0700190 // rl_src may be aliased with rl_result/rl_dest, so do compare early.
191 OpRegImm(kOpCmp, rl_src.reg, 0);
192
Andreas Gampe90969af2014-07-15 23:02:11 -0700193 RegLocation rl_dest = mir_graph_->GetDest(mir);
Serban Constantinescu05e27ff2014-05-28 13:21:45 +0100194
Andreas Gampe381f8ac2014-07-10 03:23:41 -0700195 // The kMirOpSelect has two variants, one for constants and one for moves.
Andreas Gampe381f8ac2014-07-10 03:23:41 -0700196 if (mir->ssa_rep->num_uses == 1) {
Andreas Gampe90969af2014-07-15 23:02:11 -0700197 RegLocation rl_result = EvalLoc(rl_dest, rl_dest.ref ? kRefReg : kCoreReg, true);
198 GenSelect(mir->dalvikInsn.vB, mir->dalvikInsn.vC, mir->meta.ccode, rl_result.reg,
199 rl_dest.ref ? kRefReg : kCoreReg);
200 StoreValue(rl_dest, rl_result);
Andreas Gampe381f8ac2014-07-10 03:23:41 -0700201 } else {
202 RegLocation rl_true = mir_graph_->reg_location_[mir->ssa_rep->uses[1]];
203 RegLocation rl_false = mir_graph_->reg_location_[mir->ssa_rep->uses[2]];
204
Andreas Gampe90969af2014-07-15 23:02:11 -0700205 RegisterClass result_reg_class = rl_dest.ref ? kRefReg : kCoreReg;
Andreas Gampe381f8ac2014-07-10 03:23:41 -0700206 rl_true = LoadValue(rl_true, result_reg_class);
207 rl_false = LoadValue(rl_false, result_reg_class);
Andreas Gampe90969af2014-07-15 23:02:11 -0700208 RegLocation rl_result = EvalLoc(rl_dest, result_reg_class, true);
Andreas Gampe381f8ac2014-07-10 03:23:41 -0700209
Andreas Gampe90969af2014-07-15 23:02:11 -0700210 bool is_wide = rl_dest.ref || rl_dest.wide;
Andreas Gampe381f8ac2014-07-10 03:23:41 -0700211 int opcode = is_wide ? WIDE(kA64Csel4rrrc) : kA64Csel4rrrc;
212 NewLIR4(opcode, rl_result.reg.GetReg(),
Andreas Gampe90969af2014-07-15 23:02:11 -0700213 rl_true.reg.GetReg(), rl_false.reg.GetReg(), ArmConditionEncoding(mir->meta.ccode));
214 StoreValue(rl_dest, rl_result);
Andreas Gampe381f8ac2014-07-10 03:23:41 -0700215 }
Matteo Franchin43ec8732014-03-31 15:00:14 +0100216}
217
218void Arm64Mir2Lir::GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir) {
219 RegLocation rl_src1 = mir_graph_->GetSrcWide(mir, 0);
220 RegLocation rl_src2 = mir_graph_->GetSrcWide(mir, 2);
Serban Constantinescu05e27ff2014-05-28 13:21:45 +0100221 LIR* taken = &block_label_list_[bb->taken];
222 LIR* not_taken = &block_label_list_[bb->fall_through];
Matteo Franchin43ec8732014-03-31 15:00:14 +0100223 // Normalize such that if either operand is constant, src2 will be constant.
224 ConditionCode ccode = mir->meta.ccode;
225 if (rl_src1.is_const) {
226 std::swap(rl_src1, rl_src2);
227 ccode = FlipComparisonOrder(ccode);
228 }
Serban Constantinescu05e27ff2014-05-28 13:21:45 +0100229
Andreas Gampe381f8ac2014-07-10 03:23:41 -0700230 rl_src1 = LoadValueWide(rl_src1, kCoreReg);
231
Matteo Franchin43ec8732014-03-31 15:00:14 +0100232 if (rl_src2.is_const) {
Andreas Gampe381f8ac2014-07-10 03:23:41 -0700233 // TODO: Optimize for rl_src1.is_const? (Does happen in the boot image at the moment.)
234
Matteo Franchin43ec8732014-03-31 15:00:14 +0100235 int64_t val = mir_graph_->ConstantValueWide(rl_src2);
Serban Constantinescu05e27ff2014-05-28 13:21:45 +0100236 // Special handling using cbz & cbnz.
237 if (val == 0 && (ccode == kCondEq || ccode == kCondNe)) {
238 OpCmpImmBranch(ccode, rl_src1.reg, 0, taken);
239 OpCmpImmBranch(NegateComparison(ccode), rl_src1.reg, 0, not_taken);
240 return;
Andreas Gampe381f8ac2014-07-10 03:23:41 -0700241 }
242
Serban Constantinescu05e27ff2014-05-28 13:21:45 +0100243 // Only handle Imm if src2 is not already in a register.
Andreas Gampe381f8ac2014-07-10 03:23:41 -0700244 rl_src2 = UpdateLocWide(rl_src2);
245 if (rl_src2.location != kLocPhysReg) {
Serban Constantinescu05e27ff2014-05-28 13:21:45 +0100246 OpRegImm64(kOpCmp, rl_src1.reg, val);
247 OpCondBranch(ccode, taken);
248 OpCondBranch(NegateComparison(ccode), not_taken);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100249 return;
250 }
251 }
Serban Constantinescu05e27ff2014-05-28 13:21:45 +0100252
Matteo Franchin43ec8732014-03-31 15:00:14 +0100253 rl_src2 = LoadValueWide(rl_src2, kCoreReg);
Serban Constantinescu05e27ff2014-05-28 13:21:45 +0100254 OpRegReg(kOpCmp, rl_src1.reg, rl_src2.reg);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100255 OpCondBranch(ccode, taken);
Serban Constantinescu05e27ff2014-05-28 13:21:45 +0100256 OpCondBranch(NegateComparison(ccode), not_taken);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100257}
258
259/*
260 * Generate a register comparison to an immediate and branch. Caller
261 * is responsible for setting branch target field.
262 */
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100263LIR* Arm64Mir2Lir::OpCmpImmBranch(ConditionCode cond, RegStorage reg, int check_value,
264 LIR* target) {
Andreas Gampe9522af92014-07-14 20:16:59 -0700265 LIR* branch = nullptr;
Matteo Franchin43ec8732014-03-31 15:00:14 +0100266 ArmConditionCode arm_cond = ArmConditionEncoding(cond);
Andreas Gampe9522af92014-07-14 20:16:59 -0700267 if (check_value == 0) {
268 if (arm_cond == kArmCondEq || arm_cond == kArmCondNe) {
Matteo Franchin4163c532014-07-15 15:20:27 +0100269 A64Opcode opcode = (arm_cond == kArmCondEq) ? kA64Cbz2rt : kA64Cbnz2rt;
270 A64Opcode wide = reg.Is64Bit() ? WIDE(0) : UNWIDE(0);
Andreas Gampe9522af92014-07-14 20:16:59 -0700271 branch = NewLIR2(opcode | wide, reg.GetReg(), 0);
272 } else if (arm_cond == kArmCondLs) {
273 // kArmCondLs is an unsigned less or equal. A comparison r <= 0 is then the same as cbz.
274 // This case happens for a bounds check of array[0].
Matteo Franchin4163c532014-07-15 15:20:27 +0100275 A64Opcode opcode = kA64Cbz2rt;
276 A64Opcode wide = reg.Is64Bit() ? WIDE(0) : UNWIDE(0);
Andreas Gampe9522af92014-07-14 20:16:59 -0700277 branch = NewLIR2(opcode | wide, reg.GetReg(), 0);
Zheng Xu5d7cdec2014-08-18 17:28:22 +0800278 } else if (arm_cond == kArmCondLt || arm_cond == kArmCondGe) {
Matteo Franchin4163c532014-07-15 15:20:27 +0100279 A64Opcode opcode = (arm_cond == kArmCondLt) ? kA64Tbnz3rht : kA64Tbz3rht;
280 A64Opcode wide = reg.Is64Bit() ? WIDE(0) : UNWIDE(0);
Zheng Xu5d7cdec2014-08-18 17:28:22 +0800281 int value = reg.Is64Bit() ? 63 : 31;
282 branch = NewLIR3(opcode | wide, reg.GetReg(), value, 0);
Andreas Gampe9522af92014-07-14 20:16:59 -0700283 }
284 }
285
286 if (branch == nullptr) {
Matteo Franchin43ec8732014-03-31 15:00:14 +0100287 OpRegImm(kOpCmp, reg, check_value);
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100288 branch = NewLIR2(kA64B2ct, arm_cond, 0);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100289 }
Andreas Gampe9522af92014-07-14 20:16:59 -0700290
Matteo Franchin43ec8732014-03-31 15:00:14 +0100291 branch->target = target;
292 return branch;
293}
294
Zheng Xu7c1c2632014-06-17 18:17:31 +0800295LIR* Arm64Mir2Lir::OpCmpMemImmBranch(ConditionCode cond, RegStorage temp_reg,
296 RegStorage base_reg, int offset, int check_value,
Dave Allison69dfe512014-07-11 17:11:58 +0000297 LIR* target, LIR** compare) {
298 DCHECK(compare == nullptr);
Zheng Xu7c1c2632014-06-17 18:17:31 +0800299 // It is possible that temp register is 64-bit. (ArgReg or RefReg)
300 // Always compare 32-bit value no matter what temp_reg is.
301 if (temp_reg.Is64Bit()) {
302 temp_reg = As32BitReg(temp_reg);
303 }
304 Load32Disp(base_reg, offset, temp_reg);
305 LIR* branch = OpCmpImmBranch(cond, temp_reg, check_value, target);
306 return branch;
307}
308
Matteo Franchin43ec8732014-03-31 15:00:14 +0100309LIR* Arm64Mir2Lir::OpRegCopyNoInsert(RegStorage r_dest, RegStorage r_src) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100310 bool dest_is_fp = r_dest.IsFloat();
311 bool src_is_fp = r_src.IsFloat();
Matteo Franchin4163c532014-07-15 15:20:27 +0100312 A64Opcode opcode = kA64Brk1d;
Matteo Franchin43ec8732014-03-31 15:00:14 +0100313 LIR* res;
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100314
315 if (LIKELY(dest_is_fp == src_is_fp)) {
316 if (LIKELY(!dest_is_fp)) {
Andreas Gampe4b537a82014-06-30 22:24:53 -0700317 DCHECK_EQ(r_dest.Is64Bit(), r_src.Is64Bit());
318
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100319 // Core/core copy.
320 // Copies involving the sp register require a different instruction.
321 opcode = UNLIKELY(A64_REG_IS_SP(r_dest.GetReg())) ? kA64Add4RRdT : kA64Mov2rr;
322
323 // TODO(Arm64): kA64Add4RRdT formally has 4 args, but is used as a 2 args instruction.
324 // This currently works because the other arguments are set to 0 by default. We should
325 // rather introduce an alias kA64Mov2RR.
326
327 // core/core copy. Do a x/x copy only if both registers are x.
328 if (r_dest.Is64Bit() && r_src.Is64Bit()) {
329 opcode = WIDE(opcode);
330 }
331 } else {
332 // Float/float copy.
333 bool dest_is_double = r_dest.IsDouble();
334 bool src_is_double = r_src.IsDouble();
335
336 // We do not do float/double or double/float casts here.
337 DCHECK_EQ(dest_is_double, src_is_double);
338
339 // Homogeneous float/float copy.
Matteo Franchin4163c532014-07-15 15:20:27 +0100340 opcode = (dest_is_double) ? WIDE(kA64Fmov2ff) : kA64Fmov2ff;
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100341 }
342 } else {
343 // Inhomogeneous register copy.
344 if (dest_is_fp) {
345 if (r_dest.IsDouble()) {
346 opcode = kA64Fmov2Sx;
347 } else {
Andreas Gampe4b537a82014-06-30 22:24:53 -0700348 r_src = Check32BitReg(r_src);
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100349 opcode = kA64Fmov2sw;
350 }
351 } else {
352 if (r_src.IsDouble()) {
353 opcode = kA64Fmov2xS;
354 } else {
Andreas Gampe4b537a82014-06-30 22:24:53 -0700355 r_dest = Check32BitReg(r_dest);
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100356 opcode = kA64Fmov2ws;
357 }
358 }
Matteo Franchin43ec8732014-03-31 15:00:14 +0100359 }
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100360
Matteo Franchin43ec8732014-03-31 15:00:14 +0100361 res = RawLIR(current_dalvik_offset_, opcode, r_dest.GetReg(), r_src.GetReg());
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100362
Matteo Franchin43ec8732014-03-31 15:00:14 +0100363 if (!(cu_->disable_opt & (1 << kSafeOptimizations)) && r_dest == r_src) {
364 res->flags.is_nop = true;
365 }
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100366
Matteo Franchin43ec8732014-03-31 15:00:14 +0100367 return res;
368}
369
370void Arm64Mir2Lir::OpRegCopy(RegStorage r_dest, RegStorage r_src) {
371 if (r_dest != r_src) {
372 LIR* res = OpRegCopyNoInsert(r_dest, r_src);
373 AppendLIR(res);
374 }
375}
376
377void Arm64Mir2Lir::OpRegCopyWide(RegStorage r_dest, RegStorage r_src) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100378 OpRegCopy(r_dest, r_src);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100379}
380
381// Table of magic divisors
382struct MagicTable {
Matteo Franchin7c6c2ac2014-07-01 18:03:08 +0100383 int magic64_base;
384 int magic64_eor;
385 uint64_t magic64;
386 uint32_t magic32;
Matteo Franchin43ec8732014-03-31 15:00:14 +0100387 uint32_t shift;
388 DividePattern pattern;
389};
390
391static const MagicTable magic_table[] = {
Matteo Franchin7c6c2ac2014-07-01 18:03:08 +0100392 { 0, 0, 0, 0, 0, DivideNone}, // 0
393 { 0, 0, 0, 0, 0, DivideNone}, // 1
394 { 0, 0, 0, 0, 0, DivideNone}, // 2
395 {0x3c, -1, 0x5555555555555556, 0x55555556, 0, Divide3}, // 3
396 { 0, 0, 0, 0, 0, DivideNone}, // 4
397 {0xf9, -1, 0x6666666666666667, 0x66666667, 1, Divide5}, // 5
398 {0x7c, 0x1041, 0x2AAAAAAAAAAAAAAB, 0x2AAAAAAB, 0, Divide3}, // 6
399 { -1, -1, 0x924924924924924A, 0x92492493, 2, Divide7}, // 7
400 { 0, 0, 0, 0, 0, DivideNone}, // 8
401 { -1, -1, 0x38E38E38E38E38E4, 0x38E38E39, 1, Divide5}, // 9
402 {0xf9, -1, 0x6666666666666667, 0x66666667, 2, Divide5}, // 10
403 { -1, -1, 0x2E8BA2E8BA2E8BA3, 0x2E8BA2E9, 1, Divide5}, // 11
404 {0x7c, 0x1041, 0x2AAAAAAAAAAAAAAB, 0x2AAAAAAB, 1, Divide5}, // 12
405 { -1, -1, 0x4EC4EC4EC4EC4EC5, 0x4EC4EC4F, 2, Divide5}, // 13
406 { -1, -1, 0x924924924924924A, 0x92492493, 3, Divide7}, // 14
407 {0x78, -1, 0x8888888888888889, 0x88888889, 3, Divide7}, // 15
Matteo Franchin43ec8732014-03-31 15:00:14 +0100408};
409
410// Integer division by constant via reciprocal multiply (Hacker's Delight, 10-4)
411bool Arm64Mir2Lir::SmallLiteralDivRem(Instruction::Code dalvik_opcode, bool is_div,
Matteo Franchinc61b3c92014-06-18 11:52:47 +0100412 RegLocation rl_src, RegLocation rl_dest, int lit) {
Ian Rogers6a3c1fc2014-10-31 00:33:20 -0700413 UNUSED(dalvik_opcode);
Matteo Franchinc61b3c92014-06-18 11:52:47 +0100414 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();
Matteo Franchin65420b22014-10-27 13:29:30 +0000431 NewLIR3(kA64Smull3xww, As64BitReg(r_long_mul).GetReg(), r_magic.GetReg(), rl_src.reg.GetReg());
Matteo Franchin43ec8732014-03-31 15:00:14 +0100432 switch (pattern) {
433 case Divide3:
Matteo Franchinc61b3c92014-06-18 11:52:47 +0100434 OpRegRegImm(kOpLsr, As64BitReg(r_long_mul), As64BitReg(r_long_mul), 32);
435 OpRegRegRegShift(kOpSub, rl_result.reg, r_long_mul, rl_src.reg, EncodeShift(kA64Asr, 31));
Matteo Franchin43ec8732014-03-31 15:00:14 +0100436 break;
437 case Divide5:
Matteo Franchinc61b3c92014-06-18 11:52:47 +0100438 OpRegRegImm(kOpAsr, As64BitReg(r_long_mul), As64BitReg(r_long_mul),
439 32 + magic_table[lit].shift);
440 OpRegRegRegShift(kOpSub, rl_result.reg, r_long_mul, rl_src.reg, EncodeShift(kA64Asr, 31));
Matteo Franchin43ec8732014-03-31 15:00:14 +0100441 break;
442 case Divide7:
Matteo Franchinc61b3c92014-06-18 11:52:47 +0100443 OpRegRegRegShift(kOpAdd, As64BitReg(r_long_mul), As64BitReg(rl_src.reg),
444 As64BitReg(r_long_mul), EncodeShift(kA64Lsr, 32));
445 OpRegRegImm(kOpAsr, r_long_mul, r_long_mul, magic_table[lit].shift);
446 OpRegRegRegShift(kOpSub, rl_result.reg, r_long_mul, rl_src.reg, EncodeShift(kA64Asr, 31));
Matteo Franchin43ec8732014-03-31 15:00:14 +0100447 break;
448 default:
449 LOG(FATAL) << "Unexpected pattern: " << pattern;
450 }
Matteo Franchin43ec8732014-03-31 15:00:14 +0100451 StoreValue(rl_dest, rl_result);
452 return true;
453}
454
Matteo Franchin7c6c2ac2014-07-01 18:03:08 +0100455bool Arm64Mir2Lir::SmallLiteralDivRem64(Instruction::Code dalvik_opcode, bool is_div,
456 RegLocation rl_src, RegLocation rl_dest, int64_t lit) {
Ian Rogers6a3c1fc2014-10-31 00:33:20 -0700457 UNUSED(dalvik_opcode);
Matteo Franchin7c6c2ac2014-07-01 18:03:08 +0100458 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 }
Andreas Gampece410622014-11-24 14:23:53 -0800486 DCHECK_EQ(reconstructed_imm, magic_table[lit].magic64) << " for literal " << lit;
Matteo Franchin7c6c2ac2014-07-01 18:03:08 +0100487 }
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) {
Ian Rogers6a3c1fc2014-10-31 00:33:20 -0700598 UNUSED(rl_src, rl_dest, lit);
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100599 LOG(FATAL) << "Unexpected use of EasyMultiply for Arm64";
Ian Rogers6a3c1fc2014-10-31 00:33:20 -0700600 UNREACHABLE();
Matteo Franchin43ec8732014-03-31 15:00:14 +0100601}
602
Ian Rogers6a3c1fc2014-10-31 00:33:20 -0700603RegLocation Arm64Mir2Lir::GenDivRemLit(RegLocation rl_dest, RegLocation rl_src1, int lit,
604 bool is_div) {
605 UNUSED(rl_dest, rl_src1, lit, is_div);
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100606 LOG(FATAL) << "Unexpected use of GenDivRemLit for Arm64";
Ian Rogers6a3c1fc2014-10-31 00:33:20 -0700607 UNREACHABLE();
Matteo Franchin43ec8732014-03-31 15:00:14 +0100608}
609
610RegLocation Arm64Mir2Lir::GenDivRemLit(RegLocation rl_dest, RegStorage reg1, int lit, bool is_div) {
611 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
612
613 // Put the literal in a temp.
614 RegStorage lit_temp = AllocTemp();
615 LoadConstant(lit_temp, lit);
616 // Use the generic case for div/rem with arg2 in a register.
617 // TODO: The literal temp can be freed earlier during a modulus to reduce reg pressure.
618 rl_result = GenDivRem(rl_result, reg1, lit_temp, is_div);
619 FreeTemp(lit_temp);
620
621 return rl_result;
622}
623
Matteo Franchin7c6c2ac2014-07-01 18:03:08 +0100624RegLocation Arm64Mir2Lir::GenDivRem(RegLocation rl_dest, RegLocation rl_src1,
Razvan A Lupusoru5c5676b2014-09-29 16:42:11 -0700625 RegLocation rl_src2, bool is_div, int flags) {
Ian Rogers6a3c1fc2014-10-31 00:33:20 -0700626 UNUSED(rl_dest, rl_src1, rl_src2, is_div, flags);
Matteo Franchin7c6c2ac2014-07-01 18:03:08 +0100627 LOG(FATAL) << "Unexpected use of GenDivRem for Arm64";
Ian Rogers6a3c1fc2014-10-31 00:33:20 -0700628 UNREACHABLE();
Matteo Franchin7c6c2ac2014-07-01 18:03:08 +0100629}
630
Serban Constantinescued65c5e2014-05-22 15:10:18 +0100631RegLocation Arm64Mir2Lir::GenDivRem(RegLocation rl_dest, RegStorage r_src1, RegStorage r_src2,
Matteo Franchin7c6c2ac2014-07-01 18:03:08 +0100632 bool is_div) {
Serban Constantinescued65c5e2014-05-22 15:10:18 +0100633 CHECK_EQ(r_src1.Is64Bit(), r_src2.Is64Bit());
634
Matteo Franchin43ec8732014-03-31 15:00:14 +0100635 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
636 if (is_div) {
Serban Constantinescued65c5e2014-05-22 15:10:18 +0100637 OpRegRegReg(kOpDiv, rl_result.reg, r_src1, r_src2);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100638 } else {
Serban Constantinescued65c5e2014-05-22 15:10:18 +0100639 // temp = r_src1 / r_src2
640 // dest = r_src1 - temp * r_src2
641 RegStorage temp;
Matteo Franchin4163c532014-07-15 15:20:27 +0100642 A64Opcode wide;
Serban Constantinescued65c5e2014-05-22 15:10:18 +0100643 if (rl_result.reg.Is64Bit()) {
644 temp = AllocTempWide();
645 wide = WIDE(0);
646 } else {
647 temp = AllocTemp();
648 wide = UNWIDE(0);
649 }
650 OpRegRegReg(kOpDiv, temp, r_src1, r_src2);
651 NewLIR4(kA64Msub4rrrr | wide, rl_result.reg.GetReg(), temp.GetReg(),
Matteo Franchin65420b22014-10-27 13:29:30 +0000652 r_src2.GetReg(), r_src1.GetReg());
Matteo Franchin43ec8732014-03-31 15:00:14 +0100653 FreeTemp(temp);
654 }
Matteo Franchin43ec8732014-03-31 15:00:14 +0100655 return rl_result;
656}
657
Martyn Capewell9a8a5062014-08-07 11:31:48 +0100658bool Arm64Mir2Lir::GenInlinedAbsInt(CallInfo* info) {
659 RegLocation rl_src = info->args[0];
660 rl_src = LoadValue(rl_src, kCoreReg);
661 RegLocation rl_dest = InlineTarget(info);
662 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
663
664 // Compare the source value with zero. Write the negated value to the result if
665 // negative, otherwise write the original value.
666 OpRegImm(kOpCmp, rl_src.reg, 0);
667 NewLIR4(kA64Csneg4rrrc, rl_result.reg.GetReg(), rl_src.reg.GetReg(), rl_src.reg.GetReg(),
668 kArmCondPl);
669 StoreValue(rl_dest, rl_result);
670 return true;
671}
672
Serban Constantinescu169489b2014-06-11 16:43:35 +0100673bool Arm64Mir2Lir::GenInlinedAbsLong(CallInfo* info) {
674 RegLocation rl_src = info->args[0];
675 rl_src = LoadValueWide(rl_src, kCoreReg);
676 RegLocation rl_dest = InlineTargetWide(info);
677 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
Martyn Capewell9a8a5062014-08-07 11:31:48 +0100678
679 // Compare the source value with zero. Write the negated value to the result if
680 // negative, otherwise write the original value.
681 OpRegImm(kOpCmp, rl_src.reg, 0);
682 NewLIR4(WIDE(kA64Csneg4rrrc), rl_result.reg.GetReg(), rl_src.reg.GetReg(),
683 rl_src.reg.GetReg(), kArmCondPl);
Serban Constantinescu169489b2014-06-11 16:43:35 +0100684 StoreValueWide(rl_dest, rl_result);
685 return true;
686}
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100687
Serban Constantinescu23abec92014-07-02 16:13:38 +0100688bool Arm64Mir2Lir::GenInlinedMinMax(CallInfo* info, bool is_min, bool is_long) {
Serban Constantinescu169489b2014-06-11 16:43:35 +0100689 DCHECK_EQ(cu_->instruction_set, kArm64);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100690 RegLocation rl_src1 = info->args[0];
Serban Constantinescu23abec92014-07-02 16:13:38 +0100691 RegLocation rl_src2 = (is_long) ? info->args[2] : info->args[1];
692 rl_src1 = (is_long) ? LoadValueWide(rl_src1, kCoreReg) : LoadValue(rl_src1, kCoreReg);
693 rl_src2 = (is_long) ? LoadValueWide(rl_src2, kCoreReg) : LoadValue(rl_src2, kCoreReg);
694 RegLocation rl_dest = (is_long) ? InlineTargetWide(info) : InlineTarget(info);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100695 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
696 OpRegReg(kOpCmp, rl_src1.reg, rl_src2.reg);
Serban Constantinescu23abec92014-07-02 16:13:38 +0100697 NewLIR4((is_long) ? WIDE(kA64Csel4rrrc) : kA64Csel4rrrc, rl_result.reg.GetReg(),
698 rl_src1.reg.GetReg(), rl_src2.reg.GetReg(), (is_min) ? kArmCondLt : kArmCondGt);
699 (is_long) ? StoreValueWide(rl_dest, rl_result) :StoreValue(rl_dest, rl_result);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100700 return true;
701}
702
703bool Arm64Mir2Lir::GenInlinedPeek(CallInfo* info, OpSize size) {
704 RegLocation rl_src_address = info->args[0]; // long address
Serban Constantinescu63fe93d2014-06-30 17:10:28 +0100705 RegLocation rl_dest = (size == k64) ? InlineTargetWide(info) : InlineTarget(info);
706 RegLocation rl_address = LoadValueWide(rl_src_address, kCoreReg);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100707 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
Serban Constantinescu169489b2014-06-11 16:43:35 +0100708
Andreas Gampe3c12c512014-06-24 18:46:29 +0000709 LoadBaseDisp(rl_address.reg, 0, rl_result.reg, size, kNotVolatile);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100710 if (size == k64) {
Matteo Franchin43ec8732014-03-31 15:00:14 +0100711 StoreValueWide(rl_dest, rl_result);
712 } else {
713 DCHECK(size == kSignedByte || size == kSignedHalf || size == k32);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100714 StoreValue(rl_dest, rl_result);
715 }
716 return true;
717}
718
719bool Arm64Mir2Lir::GenInlinedPoke(CallInfo* info, OpSize size) {
720 RegLocation rl_src_address = info->args[0]; // long address
Matteo Franchin43ec8732014-03-31 15:00:14 +0100721 RegLocation rl_src_value = info->args[2]; // [size] value
Serban Constantinescu63fe93d2014-06-30 17:10:28 +0100722 RegLocation rl_address = LoadValueWide(rl_src_address, kCoreReg);
Serban Constantinescu169489b2014-06-11 16:43:35 +0100723
724 RegLocation rl_value;
Matteo Franchin43ec8732014-03-31 15:00:14 +0100725 if (size == k64) {
Serban Constantinescu169489b2014-06-11 16:43:35 +0100726 rl_value = LoadValueWide(rl_src_value, kCoreReg);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100727 } else {
728 DCHECK(size == kSignedByte || size == kSignedHalf || size == k32);
Serban Constantinescu169489b2014-06-11 16:43:35 +0100729 rl_value = LoadValue(rl_src_value, kCoreReg);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100730 }
Andreas Gampe3c12c512014-06-24 18:46:29 +0000731 StoreBaseDisp(rl_address.reg, 0, rl_value.reg, size, kNotVolatile);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100732 return true;
733}
734
Matteo Franchin43ec8732014-03-31 15:00:14 +0100735bool Arm64Mir2Lir::GenInlinedCas(CallInfo* info, bool is_long, bool is_object) {
Serban Constantinescu169489b2014-06-11 16:43:35 +0100736 DCHECK_EQ(cu_->instruction_set, kArm64);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100737 // Unused - RegLocation rl_src_unsafe = info->args[0];
738 RegLocation rl_src_obj = info->args[1]; // Object - known non-null
739 RegLocation rl_src_offset = info->args[2]; // long low
Matteo Franchin43ec8732014-03-31 15:00:14 +0100740 RegLocation rl_src_expected = info->args[4]; // int, long or Object
741 // If is_long, high half is in info->args[5]
742 RegLocation rl_src_new_value = info->args[is_long ? 6 : 5]; // int, long or Object
743 // If is_long, high half is in info->args[7]
744 RegLocation rl_dest = InlineTarget(info); // boolean place for result
745
Serban Constantinescu169489b2014-06-11 16:43:35 +0100746 // Load Object and offset
buzbeea0cd2d72014-06-01 09:33:49 -0700747 RegLocation rl_object = LoadValue(rl_src_obj, kRefReg);
Serban Constantinescu63fe93d2014-06-30 17:10:28 +0100748 RegLocation rl_offset = LoadValueWide(rl_src_offset, kCoreReg);
Serban Constantinescu169489b2014-06-11 16:43:35 +0100749
Matteo Franchin43ec8732014-03-31 15:00:14 +0100750 RegLocation rl_new_value;
Serban Constantinescu169489b2014-06-11 16:43:35 +0100751 RegLocation rl_expected;
752 if (is_long) {
Matteo Franchin43ec8732014-03-31 15:00:14 +0100753 rl_new_value = LoadValueWide(rl_src_new_value, kCoreReg);
Serban Constantinescu169489b2014-06-11 16:43:35 +0100754 rl_expected = LoadValueWide(rl_src_expected, kCoreReg);
755 } else {
756 rl_new_value = LoadValue(rl_src_new_value, is_object ? kRefReg : kCoreReg);
757 rl_expected = LoadValue(rl_src_expected, is_object ? kRefReg : kCoreReg);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100758 }
759
760 if (is_object && !mir_graph_->IsConstantNullRef(rl_new_value)) {
761 // Mark card for object assuming new value is stored.
762 MarkGCCard(rl_new_value.reg, rl_object.reg);
763 }
764
Serban Constantinescu169489b2014-06-11 16:43:35 +0100765 RegStorage r_ptr = AllocTempRef();
Matteo Franchin43ec8732014-03-31 15:00:14 +0100766 OpRegRegReg(kOpAdd, r_ptr, rl_object.reg, rl_offset.reg);
767
768 // Free now unneeded rl_object and rl_offset to give more temps.
769 ClobberSReg(rl_object.s_reg_low);
770 FreeTemp(rl_object.reg);
771 ClobberSReg(rl_offset.s_reg_low);
772 FreeTemp(rl_offset.reg);
773
Matteo Franchin43ec8732014-03-31 15:00:14 +0100774 // do {
775 // tmp = [r_ptr] - expected;
776 // } while (tmp == 0 && failure([r_ptr] <- r_new_value));
777 // result = tmp != 0;
778
Serban Constantinescu169489b2014-06-11 16:43:35 +0100779 RegStorage r_tmp;
Serban Constantinescu63fe93d2014-06-30 17:10:28 +0100780 RegStorage r_tmp_stored;
781 RegStorage rl_new_value_stored = rl_new_value.reg;
Matteo Franchin4163c532014-07-15 15:20:27 +0100782 A64Opcode wide = UNWIDE(0);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100783 if (is_long) {
Serban Constantinescu63fe93d2014-06-30 17:10:28 +0100784 r_tmp_stored = r_tmp = AllocTempWide();
785 wide = WIDE(0);
Serban Constantinescu169489b2014-06-11 16:43:35 +0100786 } else if (is_object) {
Serban Constantinescu63fe93d2014-06-30 17:10:28 +0100787 // References use 64-bit registers, but are stored as compressed 32-bit values.
788 // This means r_tmp_stored != r_tmp.
Serban Constantinescu169489b2014-06-11 16:43:35 +0100789 r_tmp = AllocTempRef();
Serban Constantinescu63fe93d2014-06-30 17:10:28 +0100790 r_tmp_stored = As32BitReg(r_tmp);
791 rl_new_value_stored = As32BitReg(rl_new_value_stored);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100792 } else {
Serban Constantinescu63fe93d2014-06-30 17:10:28 +0100793 r_tmp_stored = r_tmp = AllocTemp();
Matteo Franchin43ec8732014-03-31 15:00:14 +0100794 }
795
Serban Constantinescu63fe93d2014-06-30 17:10:28 +0100796 RegStorage r_tmp32 = (r_tmp.Is32Bit()) ? r_tmp : As32BitReg(r_tmp);
Serban Constantinescu169489b2014-06-11 16:43:35 +0100797 LIR* loop = NewLIR0(kPseudoTargetLabel);
Serban Constantinescu63fe93d2014-06-30 17:10:28 +0100798 NewLIR2(kA64Ldaxr2rX | wide, r_tmp_stored.GetReg(), r_ptr.GetReg());
Serban Constantinescu169489b2014-06-11 16:43:35 +0100799 OpRegReg(kOpCmp, r_tmp, rl_expected.reg);
Vladimir Marko8dea81c2014-06-06 14:50:36 +0100800 DCHECK(last_lir_insn_->u.m.def_mask->HasBit(ResourceMask::kCCode));
Serban Constantinescu169489b2014-06-11 16:43:35 +0100801 LIR* early_exit = OpCondBranch(kCondNe, NULL);
Serban Constantinescu63fe93d2014-06-30 17:10:28 +0100802 NewLIR3(kA64Stlxr3wrX | wide, r_tmp32.GetReg(), rl_new_value_stored.GetReg(), r_ptr.GetReg());
803 NewLIR3(kA64Cmp3RdT, r_tmp32.GetReg(), 0, ENCODE_NO_SHIFT);
Serban Constantinescu169489b2014-06-11 16:43:35 +0100804 DCHECK(last_lir_insn_->u.m.def_mask->HasBit(ResourceMask::kCCode));
805 OpCondBranch(kCondNe, loop);
806
Serban Constantinescu63fe93d2014-06-30 17:10:28 +0100807 LIR* exit_loop = NewLIR0(kPseudoTargetLabel);
808 early_exit->target = exit_loop;
809
Serban Constantinescu169489b2014-06-11 16:43:35 +0100810 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
Serban Constantinescu63fe93d2014-06-30 17:10:28 +0100811 NewLIR4(kA64Csinc4rrrc, rl_result.reg.GetReg(), rwzr, rwzr, kArmCondNe);
Serban Constantinescu169489b2014-06-11 16:43:35 +0100812
Matteo Franchin43ec8732014-03-31 15:00:14 +0100813 FreeTemp(r_tmp); // Now unneeded.
Serban Constantinescu169489b2014-06-11 16:43:35 +0100814 FreeTemp(r_ptr); // Now unneeded.
Matteo Franchin43ec8732014-03-31 15:00:14 +0100815
816 StoreValue(rl_dest, rl_result);
817
Matteo Franchin43ec8732014-03-31 15:00:14 +0100818 return true;
819}
820
Zheng Xu947717a2014-08-07 14:05:23 +0800821bool Arm64Mir2Lir::GenInlinedArrayCopyCharArray(CallInfo* info) {
822 constexpr int kLargeArrayThreshold = 512;
823
824 RegLocation rl_src = info->args[0];
825 RegLocation rl_src_pos = info->args[1];
826 RegLocation rl_dst = info->args[2];
827 RegLocation rl_dst_pos = info->args[3];
828 RegLocation rl_length = info->args[4];
829 // Compile time check, handle exception by non-inline method to reduce related meta-data.
830 if ((rl_src_pos.is_const && (mir_graph_->ConstantValue(rl_src_pos) < 0)) ||
831 (rl_dst_pos.is_const && (mir_graph_->ConstantValue(rl_dst_pos) < 0)) ||
832 (rl_length.is_const && (mir_graph_->ConstantValue(rl_length) < 0))) {
833 return false;
834 }
835
836 ClobberCallerSave();
837 LockCallTemps(); // Prepare for explicit register usage.
838 RegStorage rs_src = rs_x0;
839 RegStorage rs_dst = rs_x1;
840 LoadValueDirectFixed(rl_src, rs_src);
841 LoadValueDirectFixed(rl_dst, rs_dst);
842
843 // Handle null pointer exception in slow-path.
844 LIR* src_check_branch = OpCmpImmBranch(kCondEq, rs_src, 0, nullptr);
845 LIR* dst_check_branch = OpCmpImmBranch(kCondEq, rs_dst, 0, nullptr);
846 // Handle potential overlapping in slow-path.
847 // TUNING: Support overlapping cases.
848 LIR* src_dst_same = OpCmpBranch(kCondEq, rs_src, rs_dst, nullptr);
849 // Handle exception or big length in slow-path.
850 RegStorage rs_length = rs_w2;
851 LoadValueDirectFixed(rl_length, rs_length);
852 LIR* len_neg_or_too_big = OpCmpImmBranch(kCondHi, rs_length, kLargeArrayThreshold, nullptr);
853 // Src bounds check.
854 RegStorage rs_src_pos = rs_w3;
855 RegStorage rs_arr_length = rs_w4;
856 LoadValueDirectFixed(rl_src_pos, rs_src_pos);
857 LIR* src_pos_negative = OpCmpImmBranch(kCondLt, rs_src_pos, 0, nullptr);
858 Load32Disp(rs_src, mirror::Array::LengthOffset().Int32Value(), rs_arr_length);
859 OpRegReg(kOpSub, rs_arr_length, rs_src_pos);
860 LIR* src_bad_len = OpCmpBranch(kCondLt, rs_arr_length, rs_length, nullptr);
861 // Dst bounds check.
862 RegStorage rs_dst_pos = rs_w5;
863 LoadValueDirectFixed(rl_dst_pos, rs_dst_pos);
864 LIR* dst_pos_negative = OpCmpImmBranch(kCondLt, rs_dst_pos, 0, nullptr);
865 Load32Disp(rs_dst, mirror::Array::LengthOffset().Int32Value(), rs_arr_length);
866 OpRegReg(kOpSub, rs_arr_length, rs_dst_pos);
867 LIR* dst_bad_len = OpCmpBranch(kCondLt, rs_arr_length, rs_length, nullptr);
868
869 // Everything is checked now.
870 // Set rs_src to the address of the first element to be copied.
871 rs_src_pos = As64BitReg(rs_src_pos);
872 OpRegImm(kOpAdd, rs_src, mirror::Array::DataOffset(2).Int32Value());
873 OpRegRegImm(kOpLsl, rs_src_pos, rs_src_pos, 1);
874 OpRegReg(kOpAdd, rs_src, rs_src_pos);
875 // Set rs_src to the address of the first element to be copied.
876 rs_dst_pos = As64BitReg(rs_dst_pos);
877 OpRegImm(kOpAdd, rs_dst, mirror::Array::DataOffset(2).Int32Value());
878 OpRegRegImm(kOpLsl, rs_dst_pos, rs_dst_pos, 1);
879 OpRegReg(kOpAdd, rs_dst, rs_dst_pos);
880
881 // rs_arr_length won't be not used anymore.
882 RegStorage rs_tmp = rs_arr_length;
883 // Use 64-bit view since rs_length will be used as index.
884 rs_length = As64BitReg(rs_length);
885 OpRegRegImm(kOpLsl, rs_length, rs_length, 1);
886
887 // Copy one element.
Zheng Xu5d7cdec2014-08-18 17:28:22 +0800888 LIR* jmp_to_copy_two = NewLIR3(WIDE(kA64Tbz3rht), rs_length.GetReg(), 1, 0);
Zheng Xu947717a2014-08-07 14:05:23 +0800889 OpRegImm(kOpSub, rs_length, 2);
890 LoadBaseIndexed(rs_src, rs_length, rs_tmp, 0, kSignedHalf);
891 StoreBaseIndexed(rs_dst, rs_length, rs_tmp, 0, kSignedHalf);
892
893 // Copy two elements.
894 LIR *copy_two = NewLIR0(kPseudoTargetLabel);
Zheng Xu5d7cdec2014-08-18 17:28:22 +0800895 LIR* jmp_to_copy_four = NewLIR3(WIDE(kA64Tbz3rht), rs_length.GetReg(), 2, 0);
Zheng Xu947717a2014-08-07 14:05:23 +0800896 OpRegImm(kOpSub, rs_length, 4);
897 LoadBaseIndexed(rs_src, rs_length, rs_tmp, 0, k32);
898 StoreBaseIndexed(rs_dst, rs_length, rs_tmp, 0, k32);
899
900 // Copy four elements.
901 LIR *copy_four = NewLIR0(kPseudoTargetLabel);
902 LIR* jmp_to_ret = OpCmpImmBranch(kCondEq, rs_length, 0, nullptr);
903 LIR *begin_loop = NewLIR0(kPseudoTargetLabel);
904 OpRegImm(kOpSub, rs_length, 8);
905 rs_tmp = As64BitReg(rs_tmp);
906 LoadBaseIndexed(rs_src, rs_length, rs_tmp, 0, k64);
907 StoreBaseIndexed(rs_dst, rs_length, rs_tmp, 0, k64);
908 LIR* jmp_to_loop = OpCmpImmBranch(kCondNe, rs_length, 0, nullptr);
909 LIR* loop_finished = OpUnconditionalBranch(nullptr);
910
911 LIR *check_failed = NewLIR0(kPseudoTargetLabel);
912 LIR* launchpad_branch = OpUnconditionalBranch(nullptr);
913 LIR* return_point = NewLIR0(kPseudoTargetLabel);
914
915 src_check_branch->target = check_failed;
916 dst_check_branch->target = check_failed;
917 src_dst_same->target = check_failed;
918 len_neg_or_too_big->target = check_failed;
919 src_pos_negative->target = check_failed;
920 src_bad_len->target = check_failed;
921 dst_pos_negative->target = check_failed;
922 dst_bad_len->target = check_failed;
923 jmp_to_copy_two->target = copy_two;
924 jmp_to_copy_four->target = copy_four;
925 jmp_to_ret->target = return_point;
926 jmp_to_loop->target = begin_loop;
927 loop_finished->target = return_point;
928
929 AddIntrinsicSlowPath(info, launchpad_branch, return_point);
Serguei Katkov9863daf2014-09-04 15:21:32 +0700930 ClobberCallerSave(); // We must clobber everything because slow path will return here
Zheng Xu947717a2014-08-07 14:05:23 +0800931
932 return true;
933}
934
Matteo Franchin43ec8732014-03-31 15:00:14 +0100935LIR* Arm64Mir2Lir::OpPcRelLoad(RegStorage reg, LIR* target) {
Serban Constantinescu63999682014-07-15 17:44:21 +0100936 ScopedMemRefType mem_ref_type(this, ResourceMask::kLiteral);
Matteo Franchin27cc0932014-09-08 18:29:24 +0100937 return RawLIR(current_dalvik_offset_, kA64Ldr2rp, As32BitReg(reg).GetReg(), 0, 0, 0, 0, target);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100938}
939
940LIR* Arm64Mir2Lir::OpVldm(RegStorage r_base, int count) {
Ian Rogers6a3c1fc2014-10-31 00:33:20 -0700941 UNUSED(r_base, count);
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100942 LOG(FATAL) << "Unexpected use of OpVldm for Arm64";
Ian Rogers6a3c1fc2014-10-31 00:33:20 -0700943 UNREACHABLE();
Matteo Franchin43ec8732014-03-31 15:00:14 +0100944}
945
946LIR* Arm64Mir2Lir::OpVstm(RegStorage r_base, int count) {
Ian Rogers6a3c1fc2014-10-31 00:33:20 -0700947 UNUSED(r_base, count);
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100948 LOG(FATAL) << "Unexpected use of OpVstm for Arm64";
Ian Rogers6a3c1fc2014-10-31 00:33:20 -0700949 UNREACHABLE();
Matteo Franchin43ec8732014-03-31 15:00:14 +0100950}
951
952void Arm64Mir2Lir::GenMultiplyByTwoBitMultiplier(RegLocation rl_src,
Ian Rogers6a3c1fc2014-10-31 00:33:20 -0700953 RegLocation rl_result, int lit ATTRIBUTE_UNUSED,
954 int first_bit, int second_bit) {
Serban Constantinescued65c5e2014-05-22 15:10:18 +0100955 OpRegRegRegShift(kOpAdd, rl_result.reg, rl_src.reg, rl_src.reg, EncodeShift(kA64Lsl, second_bit - first_bit));
Matteo Franchin43ec8732014-03-31 15:00:14 +0100956 if (first_bit != 0) {
957 OpRegRegImm(kOpLsl, rl_result.reg, rl_result.reg, first_bit);
958 }
959}
960
Ian Rogers6a3c1fc2014-10-31 00:33:20 -0700961void Arm64Mir2Lir::GenDivZeroCheckWide(RegStorage reg ATTRIBUTE_UNUSED) {
Serban Constantinescued65c5e2014-05-22 15:10:18 +0100962 LOG(FATAL) << "Unexpected use of GenDivZero for Arm64";
Matteo Franchin43ec8732014-03-31 15:00:14 +0100963}
964
965// Test suspend flag, return target of taken suspend branch
966LIR* Arm64Mir2Lir::OpTestSuspend(LIR* target) {
Zheng Xubaa7c882014-06-30 14:26:50 +0800967 NewLIR3(kA64Subs3rRd, rwSUSPEND, rwSUSPEND, 1);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100968 return OpCondBranch((target == NULL) ? kCondEq : kCondNe, target);
969}
970
971// Decrement register and branch on condition
972LIR* Arm64Mir2Lir::OpDecAndBranch(ConditionCode c_code, RegStorage reg, LIR* target) {
buzbee33ae5582014-06-12 14:56:32 -0700973 // Combine sub & test using sub setflags encoding here. We need to make sure a
974 // subtract form that sets carry is used, so generate explicitly.
975 // TODO: might be best to add a new op, kOpSubs, and handle it generically.
Matteo Franchin4163c532014-07-15 15:20:27 +0100976 A64Opcode opcode = reg.Is64Bit() ? WIDE(kA64Subs3rRd) : UNWIDE(kA64Subs3rRd);
buzbee33ae5582014-06-12 14:56:32 -0700977 NewLIR3(opcode, reg.GetReg(), reg.GetReg(), 1); // For value == 1, this should set flags.
Vladimir Marko8dea81c2014-06-06 14:50:36 +0100978 DCHECK(last_lir_insn_->u.m.def_mask->HasBit(ResourceMask::kCCode));
Matteo Franchin43ec8732014-03-31 15:00:14 +0100979 return OpCondBranch(c_code, target);
980}
981
Andreas Gampeb14329f2014-05-15 11:16:06 -0700982bool Arm64Mir2Lir::GenMemBarrier(MemBarrierKind barrier_kind) {
Elliott Hughes8366ca02014-11-17 12:02:05 -0800983 if (!cu_->GetInstructionSetFeatures()->IsSmp()) {
984 return false;
985 }
Matteo Franchin43ec8732014-03-31 15:00:14 +0100986 // Start off with using the last LIR as the barrier. If it is not enough, then we will generate one.
987 LIR* barrier = last_lir_insn_;
988
989 int dmb_flavor;
990 // TODO: revisit Arm barrier kinds
991 switch (barrier_kind) {
Hans Boehm48f5c472014-06-27 14:50:10 -0700992 case kAnyStore: dmb_flavor = kISH; break;
993 case kLoadAny: dmb_flavor = kISH; break;
994 // We conjecture that kISHLD is insufficient. It is documented
995 // to provide LoadLoad | StoreStore ordering. But if this were used
996 // to implement volatile loads, we suspect that the lack of store
997 // atomicity on ARM would cause us to allow incorrect results for
998 // the canonical IRIW example. But we're not sure.
999 // We should be using acquire loads instead.
Matteo Franchin43ec8732014-03-31 15:00:14 +01001000 case kStoreStore: dmb_flavor = kISHST; break;
Hans Boehm48f5c472014-06-27 14:50:10 -07001001 case kAnyAny: dmb_flavor = kISH; break;
Matteo Franchin43ec8732014-03-31 15:00:14 +01001002 default:
1003 LOG(FATAL) << "Unexpected MemBarrierKind: " << barrier_kind;
1004 dmb_flavor = kSY; // quiet gcc.
1005 break;
1006 }
1007
Andreas Gampeb14329f2014-05-15 11:16:06 -07001008 bool ret = false;
1009
Matteo Franchin43ec8732014-03-31 15:00:14 +01001010 // If the same barrier already exists, don't generate another.
1011 if (barrier == nullptr
Matteo Franchine45fb9e2014-05-06 10:10:30 +01001012 || (barrier->opcode != kA64Dmb1B || barrier->operands[0] != dmb_flavor)) {
1013 barrier = NewLIR1(kA64Dmb1B, dmb_flavor);
Andreas Gampeb14329f2014-05-15 11:16:06 -07001014 ret = true;
Matteo Franchin43ec8732014-03-31 15:00:14 +01001015 }
1016
1017 // At this point we must have a memory barrier. Mark it as a scheduling barrier as well.
1018 DCHECK(!barrier->flags.use_def_invalid);
Vladimir Marko8dea81c2014-06-06 14:50:36 +01001019 barrier->u.m.def_mask = &kEncodeAll;
Andreas Gampeb14329f2014-05-15 11:16:06 -07001020 return ret;
Matteo Franchin43ec8732014-03-31 15:00:14 +01001021}
1022
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001023void Arm64Mir2Lir::GenIntToLong(RegLocation rl_dest, RegLocation rl_src) {
1024 RegLocation rl_result;
1025
1026 rl_src = LoadValue(rl_src, kCoreReg);
1027 rl_result = EvalLocWide(rl_dest, kCoreReg, true);
Andreas Gampe4b537a82014-06-30 22:24:53 -07001028 NewLIR4(WIDE(kA64Sbfm4rrdd), rl_result.reg.GetReg(), As64BitReg(rl_src.reg).GetReg(), 0, 31);
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001029 StoreValueWide(rl_dest, rl_result);
1030}
1031
1032void Arm64Mir2Lir::GenDivRemLong(Instruction::Code opcode, RegLocation rl_dest,
Razvan A Lupusoru5c5676b2014-09-29 16:42:11 -07001033 RegLocation rl_src1, RegLocation rl_src2, bool is_div, int flags) {
Matteo Franchin7c6c2ac2014-07-01 18:03:08 +01001034 if (rl_src2.is_const) {
1035 DCHECK(rl_src2.wide);
1036 int64_t lit = mir_graph_->ConstantValueWide(rl_src2);
1037 if (HandleEasyDivRem64(opcode, is_div, rl_src1, rl_dest, lit)) {
1038 return;
1039 }
1040 }
1041
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001042 RegLocation rl_result;
1043 rl_src1 = LoadValueWide(rl_src1, kCoreReg);
1044 rl_src2 = LoadValueWide(rl_src2, kCoreReg);
Razvan A Lupusoru5c5676b2014-09-29 16:42:11 -07001045 if ((flags & MIR_IGNORE_DIV_ZERO_CHECK) == 0) {
1046 GenDivZeroCheck(rl_src2.reg);
1047 }
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001048 rl_result = GenDivRem(rl_dest, rl_src1.reg, rl_src2.reg, is_div);
Matteo Franchin43ec8732014-03-31 15:00:14 +01001049 StoreValueWide(rl_dest, rl_result);
1050}
1051
Matteo Franchine45fb9e2014-05-06 10:10:30 +01001052void Arm64Mir2Lir::GenLongOp(OpKind op, RegLocation rl_dest, RegLocation rl_src1,
1053 RegLocation rl_src2) {
1054 RegLocation rl_result;
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001055
Matteo Franchine45fb9e2014-05-06 10:10:30 +01001056 rl_src1 = LoadValueWide(rl_src1, kCoreReg);
1057 rl_src2 = LoadValueWide(rl_src2, kCoreReg);
1058 rl_result = EvalLocWide(rl_dest, kCoreReg, true);
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001059 OpRegRegRegShift(op, rl_result.reg, rl_src1.reg, rl_src2.reg, ENCODE_NO_SHIFT);
1060 StoreValueWide(rl_dest, rl_result);
1061}
1062
1063void Arm64Mir2Lir::GenNegLong(RegLocation rl_dest, RegLocation rl_src) {
1064 RegLocation rl_result;
1065
1066 rl_src = LoadValueWide(rl_src, kCoreReg);
1067 rl_result = EvalLocWide(rl_dest, kCoreReg, true);
1068 OpRegRegShift(kOpNeg, rl_result.reg, rl_src.reg, ENCODE_NO_SHIFT);
1069 StoreValueWide(rl_dest, rl_result);
1070}
1071
1072void Arm64Mir2Lir::GenNotLong(RegLocation rl_dest, RegLocation rl_src) {
1073 RegLocation rl_result;
1074
1075 rl_src = LoadValueWide(rl_src, kCoreReg);
1076 rl_result = EvalLocWide(rl_dest, kCoreReg, true);
1077 OpRegRegShift(kOpMvn, rl_result.reg, rl_src.reg, ENCODE_NO_SHIFT);
Matteo Franchine45fb9e2014-05-06 10:10:30 +01001078 StoreValueWide(rl_dest, rl_result);
1079}
1080
Andreas Gampec76c6142014-08-04 16:30:03 -07001081void Arm64Mir2Lir::GenArithOpLong(Instruction::Code opcode, RegLocation rl_dest,
Razvan A Lupusoru5c5676b2014-09-29 16:42:11 -07001082 RegLocation rl_src1, RegLocation rl_src2, int flags) {
Andreas Gampec76c6142014-08-04 16:30:03 -07001083 switch (opcode) {
1084 case Instruction::NOT_LONG:
1085 GenNotLong(rl_dest, rl_src2);
1086 return;
1087 case Instruction::ADD_LONG:
1088 case Instruction::ADD_LONG_2ADDR:
1089 GenLongOp(kOpAdd, rl_dest, rl_src1, rl_src2);
1090 return;
1091 case Instruction::SUB_LONG:
1092 case Instruction::SUB_LONG_2ADDR:
1093 GenLongOp(kOpSub, rl_dest, rl_src1, rl_src2);
1094 return;
1095 case Instruction::MUL_LONG:
1096 case Instruction::MUL_LONG_2ADDR:
1097 GenLongOp(kOpMul, rl_dest, rl_src1, rl_src2);
1098 return;
1099 case Instruction::DIV_LONG:
1100 case Instruction::DIV_LONG_2ADDR:
Razvan A Lupusoru5c5676b2014-09-29 16:42:11 -07001101 GenDivRemLong(opcode, rl_dest, rl_src1, rl_src2, /*is_div*/ true, flags);
Andreas Gampec76c6142014-08-04 16:30:03 -07001102 return;
1103 case Instruction::REM_LONG:
1104 case Instruction::REM_LONG_2ADDR:
Razvan A Lupusoru5c5676b2014-09-29 16:42:11 -07001105 GenDivRemLong(opcode, rl_dest, rl_src1, rl_src2, /*is_div*/ false, flags);
Andreas Gampec76c6142014-08-04 16:30:03 -07001106 return;
1107 case Instruction::AND_LONG_2ADDR:
1108 case Instruction::AND_LONG:
1109 GenLongOp(kOpAnd, rl_dest, rl_src1, rl_src2);
1110 return;
1111 case Instruction::OR_LONG:
1112 case Instruction::OR_LONG_2ADDR:
1113 GenLongOp(kOpOr, rl_dest, rl_src1, rl_src2);
1114 return;
1115 case Instruction::XOR_LONG:
1116 case Instruction::XOR_LONG_2ADDR:
1117 GenLongOp(kOpXor, rl_dest, rl_src1, rl_src2);
1118 return;
1119 case Instruction::NEG_LONG: {
1120 GenNegLong(rl_dest, rl_src2);
1121 return;
1122 }
1123 default:
1124 LOG(FATAL) << "Invalid long arith op";
1125 return;
1126 }
Matteo Franchin43ec8732014-03-31 15:00:14 +01001127}
1128
1129/*
1130 * Generate array load
1131 */
1132void Arm64Mir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array,
1133 RegLocation rl_index, RegLocation rl_dest, int scale) {
1134 RegisterClass reg_class = RegClassBySize(size);
1135 int len_offset = mirror::Array::LengthOffset().Int32Value();
1136 int data_offset;
1137 RegLocation rl_result;
1138 bool constant_index = rl_index.is_const;
buzbeea0cd2d72014-06-01 09:33:49 -07001139 rl_array = LoadValue(rl_array, kRefReg);
Matteo Franchin43ec8732014-03-31 15:00:14 +01001140 if (!constant_index) {
1141 rl_index = LoadValue(rl_index, kCoreReg);
1142 }
1143
1144 if (rl_dest.wide) {
1145 data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Int32Value();
1146 } else {
1147 data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Int32Value();
1148 }
1149
Matteo Franchin43ec8732014-03-31 15:00:14 +01001150 /* null object? */
1151 GenNullCheck(rl_array.reg, opt_flags);
1152
1153 bool needs_range_check = (!(opt_flags & MIR_IGNORE_RANGE_CHECK));
1154 RegStorage reg_len;
1155 if (needs_range_check) {
1156 reg_len = AllocTemp();
1157 /* Get len */
1158 Load32Disp(rl_array.reg, len_offset, reg_len);
1159 MarkPossibleNullPointerException(opt_flags);
1160 } else {
1161 ForceImplicitNullCheck(rl_array.reg, opt_flags);
1162 }
Vladimir Markoe08785b2014-11-07 16:11:00 +00001163 if (constant_index) {
Matteo Franchin43ec8732014-03-31 15:00:14 +01001164 rl_result = EvalLoc(rl_dest, reg_class, true);
1165
1166 if (needs_range_check) {
Vladimir Markoe08785b2014-11-07 16:11:00 +00001167 GenArrayBoundsCheck(mir_graph_->ConstantValue(rl_index), reg_len);
Matteo Franchin43ec8732014-03-31 15:00:14 +01001168 FreeTemp(reg_len);
1169 }
Vladimir Markoe08785b2014-11-07 16:11:00 +00001170 // Fold the constant index into the data offset.
1171 data_offset += mir_graph_->ConstantValue(rl_index) << scale;
Andreas Gampe3c12c512014-06-24 18:46:29 +00001172 if (rl_result.ref) {
Vladimir Markoe08785b2014-11-07 16:11:00 +00001173 LoadRefDisp(rl_array.reg, data_offset, rl_result.reg, kNotVolatile);
Andreas Gampe3c12c512014-06-24 18:46:29 +00001174 } else {
Vladimir Markoe08785b2014-11-07 16:11:00 +00001175 LoadBaseDisp(rl_array.reg, data_offset, rl_result.reg, size, kNotVolatile);
Matteo Franchin43ec8732014-03-31 15:00:14 +01001176 }
1177 } else {
Vladimir Markoe08785b2014-11-07 16:11:00 +00001178 // Offset base, then use indexed load.
buzbeea0cd2d72014-06-01 09:33:49 -07001179 RegStorage reg_ptr = AllocTempRef();
Matteo Franchin43ec8732014-03-31 15:00:14 +01001180 OpRegRegImm(kOpAdd, reg_ptr, rl_array.reg, data_offset);
1181 FreeTemp(rl_array.reg);
1182 rl_result = EvalLoc(rl_dest, reg_class, true);
1183
1184 if (needs_range_check) {
1185 GenArrayBoundsCheck(rl_index.reg, reg_len);
1186 FreeTemp(reg_len);
1187 }
Andreas Gampe3c12c512014-06-24 18:46:29 +00001188 if (rl_result.ref) {
Vladimir Markoe08785b2014-11-07 16:11:00 +00001189 LoadRefIndexed(reg_ptr, rl_index.reg, rl_result.reg, scale);
Andreas Gampe3c12c512014-06-24 18:46:29 +00001190 } else {
Vladimir Markoe08785b2014-11-07 16:11:00 +00001191 LoadBaseIndexed(reg_ptr, rl_index.reg, rl_result.reg, scale, size);
Andreas Gampe3c12c512014-06-24 18:46:29 +00001192 }
Matteo Franchin43ec8732014-03-31 15:00:14 +01001193 FreeTemp(reg_ptr);
Vladimir Markoe08785b2014-11-07 16:11:00 +00001194 }
1195 if (rl_dest.wide) {
1196 StoreValueWide(rl_dest, rl_result);
1197 } else {
Matteo Franchin43ec8732014-03-31 15:00:14 +01001198 StoreValue(rl_dest, rl_result);
1199 }
1200}
1201
1202/*
1203 * Generate array store
1204 *
1205 */
1206void Arm64Mir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array,
1207 RegLocation rl_index, RegLocation rl_src, int scale, bool card_mark) {
1208 RegisterClass reg_class = RegClassBySize(size);
1209 int len_offset = mirror::Array::LengthOffset().Int32Value();
1210 bool constant_index = rl_index.is_const;
1211
1212 int data_offset;
1213 if (size == k64 || size == kDouble) {
1214 data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Int32Value();
1215 } else {
1216 data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Int32Value();
1217 }
1218
buzbeea0cd2d72014-06-01 09:33:49 -07001219 rl_array = LoadValue(rl_array, kRefReg);
Matteo Franchin43ec8732014-03-31 15:00:14 +01001220 if (!constant_index) {
1221 rl_index = LoadValue(rl_index, kCoreReg);
1222 }
1223
1224 RegStorage reg_ptr;
1225 bool allocated_reg_ptr_temp = false;
1226 if (constant_index) {
1227 reg_ptr = rl_array.reg;
1228 } else if (IsTemp(rl_array.reg) && !card_mark) {
1229 Clobber(rl_array.reg);
1230 reg_ptr = rl_array.reg;
1231 } else {
1232 allocated_reg_ptr_temp = true;
buzbeea0cd2d72014-06-01 09:33:49 -07001233 reg_ptr = AllocTempRef();
Matteo Franchin43ec8732014-03-31 15:00:14 +01001234 }
1235
1236 /* null object? */
1237 GenNullCheck(rl_array.reg, opt_flags);
1238
1239 bool needs_range_check = (!(opt_flags & MIR_IGNORE_RANGE_CHECK));
1240 RegStorage reg_len;
1241 if (needs_range_check) {
1242 reg_len = AllocTemp();
1243 // NOTE: max live temps(4) here.
1244 /* Get len */
1245 Load32Disp(rl_array.reg, len_offset, reg_len);
1246 MarkPossibleNullPointerException(opt_flags);
1247 } else {
1248 ForceImplicitNullCheck(rl_array.reg, opt_flags);
1249 }
1250 /* at this point, reg_ptr points to array, 2 live temps */
Vladimir Markoe08785b2014-11-07 16:11:00 +00001251 if (rl_src.wide) {
1252 rl_src = LoadValueWide(rl_src, reg_class);
1253 } else {
1254 rl_src = LoadValue(rl_src, reg_class);
1255 }
1256 if (constant_index) {
Matteo Franchin43ec8732014-03-31 15:00:14 +01001257 if (needs_range_check) {
Vladimir Markoe08785b2014-11-07 16:11:00 +00001258 GenArrayBoundsCheck(mir_graph_->ConstantValue(rl_index), reg_len);
Matteo Franchin43ec8732014-03-31 15:00:14 +01001259 FreeTemp(reg_len);
1260 }
Vladimir Markoe08785b2014-11-07 16:11:00 +00001261 // Fold the constant index into the data offset.
1262 data_offset += mir_graph_->ConstantValue(rl_index) << scale;
Andreas Gampe3c12c512014-06-24 18:46:29 +00001263 if (rl_src.ref) {
1264 StoreRefDisp(reg_ptr, data_offset, rl_src.reg, kNotVolatile);
1265 } else {
1266 StoreBaseDisp(reg_ptr, data_offset, rl_src.reg, size, kNotVolatile);
1267 }
Matteo Franchin43ec8732014-03-31 15:00:14 +01001268 } else {
1269 /* reg_ptr -> array data */
1270 OpRegRegImm(kOpAdd, reg_ptr, rl_array.reg, data_offset);
Matteo Franchin43ec8732014-03-31 15:00:14 +01001271 if (needs_range_check) {
1272 GenArrayBoundsCheck(rl_index.reg, reg_len);
1273 FreeTemp(reg_len);
1274 }
Andreas Gampe3c12c512014-06-24 18:46:29 +00001275 if (rl_src.ref) {
Vladimir Markoe08785b2014-11-07 16:11:00 +00001276 StoreRefIndexed(reg_ptr, rl_index.reg, rl_src.reg, scale);
Andreas Gampe3c12c512014-06-24 18:46:29 +00001277 } else {
Vladimir Markoe08785b2014-11-07 16:11:00 +00001278 StoreBaseIndexed(reg_ptr, rl_index.reg, rl_src.reg, scale, size);
Andreas Gampe3c12c512014-06-24 18:46:29 +00001279 }
Matteo Franchin43ec8732014-03-31 15:00:14 +01001280 }
1281 if (allocated_reg_ptr_temp) {
1282 FreeTemp(reg_ptr);
1283 }
1284 if (card_mark) {
1285 MarkGCCard(rl_src.reg, rl_array.reg);
1286 }
1287}
1288
Matteo Franchin43ec8732014-03-31 15:00:14 +01001289void Arm64Mir2Lir::GenShiftImmOpLong(Instruction::Code opcode,
Razvan A Lupusoru5c5676b2014-09-29 16:42:11 -07001290 RegLocation rl_dest, RegLocation rl_src, RegLocation rl_shift,
Ian Rogers6a3c1fc2014-10-31 00:33:20 -07001291 int flags ATTRIBUTE_UNUSED) {
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001292 OpKind op = kOpBkpt;
Matteo Franchin43ec8732014-03-31 15:00:14 +01001293 // Per spec, we only care about low 6 bits of shift amount.
1294 int shift_amount = mir_graph_->ConstantValue(rl_shift) & 0x3f;
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001295 rl_src = LoadValueWide(rl_src, kCoreReg);
Matteo Franchin43ec8732014-03-31 15:00:14 +01001296 if (shift_amount == 0) {
1297 StoreValueWide(rl_dest, rl_src);
1298 return;
1299 }
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001300
1301 RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true);
Matteo Franchin43ec8732014-03-31 15:00:14 +01001302 switch (opcode) {
1303 case Instruction::SHL_LONG:
1304 case Instruction::SHL_LONG_2ADDR:
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001305 op = kOpLsl;
Matteo Franchin43ec8732014-03-31 15:00:14 +01001306 break;
1307 case Instruction::SHR_LONG:
1308 case Instruction::SHR_LONG_2ADDR:
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001309 op = kOpAsr;
Matteo Franchin43ec8732014-03-31 15:00:14 +01001310 break;
1311 case Instruction::USHR_LONG:
1312 case Instruction::USHR_LONG_2ADDR:
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001313 op = kOpLsr;
Matteo Franchin43ec8732014-03-31 15:00:14 +01001314 break;
1315 default:
1316 LOG(FATAL) << "Unexpected case";
1317 }
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001318 OpRegRegImm(op, rl_result.reg, rl_src.reg, shift_amount);
Matteo Franchin43ec8732014-03-31 15:00:14 +01001319 StoreValueWide(rl_dest, rl_result);
1320}
1321
Matteo Franchine45fb9e2014-05-06 10:10:30 +01001322void Arm64Mir2Lir::GenArithImmOpLong(Instruction::Code opcode, RegLocation rl_dest,
Razvan A Lupusoru5c5676b2014-09-29 16:42:11 -07001323 RegLocation rl_src1, RegLocation rl_src2, int flags) {
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001324 OpKind op = kOpBkpt;
Matteo Franchin43ec8732014-03-31 15:00:14 +01001325 switch (opcode) {
1326 case Instruction::ADD_LONG:
1327 case Instruction::ADD_LONG_2ADDR:
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001328 op = kOpAdd;
1329 break;
Matteo Franchin43ec8732014-03-31 15:00:14 +01001330 case Instruction::SUB_LONG:
1331 case Instruction::SUB_LONG_2ADDR:
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001332 op = kOpSub;
Matteo Franchin43ec8732014-03-31 15:00:14 +01001333 break;
1334 case Instruction::AND_LONG:
1335 case Instruction::AND_LONG_2ADDR:
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001336 op = kOpAnd;
Matteo Franchin43ec8732014-03-31 15:00:14 +01001337 break;
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001338 case Instruction::OR_LONG:
1339 case Instruction::OR_LONG_2ADDR:
1340 op = kOpOr;
Matteo Franchin43ec8732014-03-31 15:00:14 +01001341 break;
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001342 case Instruction::XOR_LONG:
1343 case Instruction::XOR_LONG_2ADDR:
1344 op = kOpXor;
1345 break;
Matteo Franchin43ec8732014-03-31 15:00:14 +01001346 default:
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001347 LOG(FATAL) << "Unexpected opcode";
Matteo Franchin43ec8732014-03-31 15:00:14 +01001348 }
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001349
Matteo Franchinc763e352014-07-04 12:53:27 +01001350 if (op == kOpSub) {
1351 if (!rl_src2.is_const) {
Razvan A Lupusoru5c5676b2014-09-29 16:42:11 -07001352 return GenArithOpLong(opcode, rl_dest, rl_src1, rl_src2, flags);
Matteo Franchinc763e352014-07-04 12:53:27 +01001353 }
1354 } else {
1355 // Associativity.
1356 if (!rl_src2.is_const) {
1357 DCHECK(rl_src1.is_const);
1358 std::swap(rl_src1, rl_src2);
1359 }
1360 }
1361 DCHECK(rl_src2.is_const);
1362 int64_t val = mir_graph_->ConstantValueWide(rl_src2);
1363
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001364 rl_src1 = LoadValueWide(rl_src1, kCoreReg);
1365 RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true);
Zheng Xue2eb29e2014-06-12 10:22:33 +08001366 OpRegRegImm64(op, rl_result.reg, rl_src1.reg, val);
Matteo Franchin43ec8732014-03-31 15:00:14 +01001367 StoreValueWide(rl_dest, rl_result);
1368}
1369
Andreas Gampef29ecd62014-07-29 00:35:00 -07001370static uint32_t ExtractReg(uint32_t reg_mask, int* reg) {
1371 // Find first register.
1372 int first_bit_set = CTZ(reg_mask) + 1;
1373 *reg = *reg + first_bit_set;
1374 reg_mask >>= first_bit_set;
1375 return reg_mask;
1376}
1377
Matteo Franchine45fb9e2014-05-06 10:10:30 +01001378/**
1379 * @brief Split a register list in pairs or registers.
1380 *
1381 * Given a list of registers in @p reg_mask, split the list in pairs. Use as follows:
1382 * @code
1383 * int reg1 = -1, reg2 = -1;
1384 * while (reg_mask) {
1385 * reg_mask = GenPairWise(reg_mask, & reg1, & reg2);
1386 * if (UNLIKELY(reg2 < 0)) {
1387 * // Single register in reg1.
1388 * } else {
1389 * // Pair in reg1, reg2.
1390 * }
1391 * }
1392 * @endcode
1393 */
Andreas Gampef29ecd62014-07-29 00:35:00 -07001394static uint32_t GenPairWise(uint32_t reg_mask, int* reg1, int* reg2) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +01001395 // Find first register.
Andreas Gampef29ecd62014-07-29 00:35:00 -07001396 int first_bit_set = CTZ(reg_mask) + 1;
Matteo Franchine45fb9e2014-05-06 10:10:30 +01001397 int reg = *reg1 + first_bit_set;
1398 reg_mask >>= first_bit_set;
1399
1400 if (LIKELY(reg_mask)) {
1401 // Save the first register, find the second and use the pair opcode.
Andreas Gampef29ecd62014-07-29 00:35:00 -07001402 int second_bit_set = CTZ(reg_mask) + 1;
Matteo Franchine45fb9e2014-05-06 10:10:30 +01001403 *reg2 = reg;
1404 reg_mask >>= second_bit_set;
1405 *reg1 = reg + second_bit_set;
1406 return reg_mask;
1407 }
1408
1409 // Use the single opcode, as we just have one register.
1410 *reg1 = reg;
1411 *reg2 = -1;
1412 return reg_mask;
1413}
1414
Andreas Gampef29ecd62014-07-29 00:35:00 -07001415static void SpillCoreRegs(Arm64Mir2Lir* m2l, RegStorage base, int offset, uint32_t reg_mask) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +01001416 int reg1 = -1, reg2 = -1;
Matteo Franchinbc6d1972014-05-13 12:33:28 +01001417 const int reg_log2_size = 3;
Matteo Franchine45fb9e2014-05-06 10:10:30 +01001418
Matteo Franchinbc6d1972014-05-13 12:33:28 +01001419 for (offset = (offset >> reg_log2_size); reg_mask; offset += 2) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +01001420 reg_mask = GenPairWise(reg_mask, & reg1, & reg2);
1421 if (UNLIKELY(reg2 < 0)) {
Andreas Gampef29ecd62014-07-29 00:35:00 -07001422 m2l->NewLIR3(WIDE(kA64Str3rXD), RegStorage::Solo64(reg1).GetReg(), base.GetReg(), offset);
Matteo Franchine45fb9e2014-05-06 10:10:30 +01001423 } else {
Andreas Gampef29ecd62014-07-29 00:35:00 -07001424 m2l->NewLIR4(WIDE(kA64Stp4rrXD), RegStorage::Solo64(reg2).GetReg(),
1425 RegStorage::Solo64(reg1).GetReg(), base.GetReg(), offset);
Matteo Franchinbc6d1972014-05-13 12:33:28 +01001426 }
1427 }
1428}
1429
1430// TODO(Arm64): consider using ld1 and st1?
Andreas Gampef29ecd62014-07-29 00:35:00 -07001431static void SpillFPRegs(Arm64Mir2Lir* m2l, RegStorage base, int offset, uint32_t reg_mask) {
Matteo Franchinbc6d1972014-05-13 12:33:28 +01001432 int reg1 = -1, reg2 = -1;
1433 const int reg_log2_size = 3;
1434
1435 for (offset = (offset >> reg_log2_size); reg_mask; offset += 2) {
1436 reg_mask = GenPairWise(reg_mask, & reg1, & reg2);
1437 if (UNLIKELY(reg2 < 0)) {
Matteo Franchin4163c532014-07-15 15:20:27 +01001438 m2l->NewLIR3(WIDE(kA64Str3fXD), RegStorage::FloatSolo64(reg1).GetReg(), base.GetReg(),
Andreas Gampef29ecd62014-07-29 00:35:00 -07001439 offset);
Matteo Franchinbc6d1972014-05-13 12:33:28 +01001440 } else {
Andreas Gampef29ecd62014-07-29 00:35:00 -07001441 m2l->NewLIR4(WIDE(kA64Stp4ffXD), RegStorage::FloatSolo64(reg2).GetReg(),
1442 RegStorage::FloatSolo64(reg1).GetReg(), base.GetReg(), offset);
Matteo Franchine45fb9e2014-05-06 10:10:30 +01001443 }
1444 }
1445}
1446
Ian Rogers6a3c1fc2014-10-31 00:33:20 -07001447static int SpillRegsPreSub(Arm64Mir2Lir* m2l, uint32_t core_reg_mask, uint32_t fp_reg_mask,
1448 int frame_size) {
Andreas Gampef29ecd62014-07-29 00:35:00 -07001449 m2l->OpRegRegImm(kOpSub, rs_sp, rs_sp, frame_size);
1450
1451 int core_count = POPCOUNT(core_reg_mask);
1452
1453 if (fp_reg_mask != 0) {
1454 // Spill FP regs.
1455 int fp_count = POPCOUNT(fp_reg_mask);
1456 int spill_offset = frame_size - (core_count + fp_count) * kArm64PointerSize;
1457 SpillFPRegs(m2l, rs_sp, spill_offset, fp_reg_mask);
1458 }
1459
1460 if (core_reg_mask != 0) {
1461 // Spill core regs.
1462 int spill_offset = frame_size - (core_count * kArm64PointerSize);
1463 SpillCoreRegs(m2l, rs_sp, spill_offset, core_reg_mask);
1464 }
1465
1466 return frame_size;
1467}
1468
1469static int SpillRegsPreIndexed(Arm64Mir2Lir* m2l, RegStorage base, uint32_t core_reg_mask,
Ian Rogers6a3c1fc2014-10-31 00:33:20 -07001470 uint32_t fp_reg_mask) {
Andreas Gampef29ecd62014-07-29 00:35:00 -07001471 // Otherwise, spill both core and fp regs at the same time.
1472 // The very first instruction will be an stp with pre-indexed address, moving the stack pointer
1473 // down. From then on, we fill upwards. This will generate overall the same number of instructions
1474 // as the specialized code above in most cases (exception being odd number of core and even
1475 // non-zero fp spills), but is more flexible, as the offsets are guaranteed small.
1476 //
1477 // Some demonstrative fill cases : (c) = core, (f) = fp
1478 // cc 44 cc 44 cc 22 cc 33 fc => 1[1/2]
1479 // fc => 23 fc => 23 ff => 11 ff => 22
1480 // ff 11 f 11 f 11
1481 //
1482 int reg1 = -1, reg2 = -1;
1483 int core_count = POPCOUNT(core_reg_mask);
1484 int fp_count = POPCOUNT(fp_reg_mask);
1485
1486 int combined = fp_count + core_count;
1487 int all_offset = RoundUp(combined, 2); // Needs to be 16B = 2-reg aligned.
1488
1489 int cur_offset = 2; // What's the starting offset after the first stp? We expect the base slot
1490 // to be filled.
1491
1492 // First figure out whether the bottom is FP or core.
1493 if (fp_count > 0) {
1494 // Some FP spills.
1495 //
1496 // Four cases: (d0 is dummy to fill up stp)
1497 // 1) Single FP, even number of core -> stp d0, fp_reg
1498 // 2) Single FP, odd number of core -> stp fp_reg, d0
1499 // 3) More FP, even number combined -> stp fp_reg1, fp_reg2
1500 // 4) More FP, odd number combined -> stp d0, fp_reg
1501 if (fp_count == 1) {
1502 fp_reg_mask = ExtractReg(fp_reg_mask, &reg1);
1503 DCHECK_EQ(fp_reg_mask, 0U);
1504 if (core_count % 2 == 0) {
1505 m2l->NewLIR4(WIDE(kA64StpPre4ffXD),
1506 RegStorage::FloatSolo64(reg1).GetReg(),
1507 RegStorage::FloatSolo64(reg1).GetReg(),
1508 base.GetReg(), -all_offset);
1509 } else {
1510 m2l->NewLIR4(WIDE(kA64StpPre4ffXD),
1511 RegStorage::FloatSolo64(reg1).GetReg(),
1512 RegStorage::FloatSolo64(reg1).GetReg(),
1513 base.GetReg(), -all_offset);
1514 cur_offset = 0; // That core reg needs to go into the upper half.
1515 }
1516 } else {
1517 if (combined % 2 == 0) {
1518 fp_reg_mask = GenPairWise(fp_reg_mask, &reg1, &reg2);
1519 m2l->NewLIR4(WIDE(kA64StpPre4ffXD), RegStorage::FloatSolo64(reg2).GetReg(),
1520 RegStorage::FloatSolo64(reg1).GetReg(), base.GetReg(), -all_offset);
1521 } else {
1522 fp_reg_mask = ExtractReg(fp_reg_mask, &reg1);
1523 m2l->NewLIR4(WIDE(kA64StpPre4ffXD), rs_d0.GetReg(), RegStorage::FloatSolo64(reg1).GetReg(),
1524 base.GetReg(), -all_offset);
1525 }
1526 }
1527 } else {
1528 // No FP spills.
1529 //
1530 // Two cases:
1531 // 1) Even number of core -> stp core1, core2
1532 // 2) Odd number of core -> stp xzr, core1
1533 if (core_count % 2 == 1) {
1534 core_reg_mask = ExtractReg(core_reg_mask, &reg1);
1535 m2l->NewLIR4(WIDE(kA64StpPre4rrXD), rs_xzr.GetReg(),
1536 RegStorage::Solo64(reg1).GetReg(), base.GetReg(), -all_offset);
1537 } else {
1538 core_reg_mask = GenPairWise(core_reg_mask, &reg1, &reg2);
1539 m2l->NewLIR4(WIDE(kA64StpPre4rrXD), RegStorage::Solo64(reg2).GetReg(),
1540 RegStorage::Solo64(reg1).GetReg(), base.GetReg(), -all_offset);
1541 }
1542 }
1543
1544 if (fp_count != 0) {
1545 for (; fp_reg_mask != 0;) {
1546 // Have some FP regs to do.
1547 fp_reg_mask = GenPairWise(fp_reg_mask, &reg1, &reg2);
1548 if (UNLIKELY(reg2 < 0)) {
Matteo Franchin4163c532014-07-15 15:20:27 +01001549 m2l->NewLIR3(WIDE(kA64Str3fXD), RegStorage::FloatSolo64(reg1).GetReg(), base.GetReg(),
Andreas Gampef29ecd62014-07-29 00:35:00 -07001550 cur_offset);
1551 // Do not increment offset here, as the second half will be filled by a core reg.
1552 } else {
1553 m2l->NewLIR4(WIDE(kA64Stp4ffXD), RegStorage::FloatSolo64(reg2).GetReg(),
1554 RegStorage::FloatSolo64(reg1).GetReg(), base.GetReg(), cur_offset);
1555 cur_offset += 2;
1556 }
1557 }
1558
1559 // Reset counting.
1560 reg1 = -1;
1561
1562 // If there is an odd number of core registers, we need to store the bottom now.
1563 if (core_count % 2 == 1) {
1564 core_reg_mask = ExtractReg(core_reg_mask, &reg1);
1565 m2l->NewLIR3(WIDE(kA64Str3rXD), RegStorage::Solo64(reg1).GetReg(), base.GetReg(),
1566 cur_offset + 1);
1567 cur_offset += 2; // Half-slot filled now.
1568 }
1569 }
1570
1571 // Spill the rest of the core regs. They are guaranteed to be even.
1572 DCHECK_EQ(POPCOUNT(core_reg_mask) % 2, 0);
1573 for (; core_reg_mask != 0; cur_offset += 2) {
1574 core_reg_mask = GenPairWise(core_reg_mask, &reg1, &reg2);
1575 m2l->NewLIR4(WIDE(kA64Stp4rrXD), RegStorage::Solo64(reg2).GetReg(),
1576 RegStorage::Solo64(reg1).GetReg(), base.GetReg(), cur_offset);
1577 }
1578
1579 DCHECK_EQ(cur_offset, all_offset);
1580
1581 return all_offset * 8;
1582}
1583
1584int Arm64Mir2Lir::SpillRegs(RegStorage base, uint32_t core_reg_mask, uint32_t fp_reg_mask,
1585 int frame_size) {
1586 // If the frame size is small enough that all offsets would fit into the immediates, use that
1587 // setup, as it decrements sp early (kind of instruction scheduling), and is not worse
1588 // instruction-count wise than the complicated code below.
1589 //
1590 // This case is also optimal when we have an odd number of core spills, and an even (non-zero)
1591 // number of fp spills.
1592 if ((RoundUp(frame_size, 8) / 8 <= 63)) {
Ian Rogers6a3c1fc2014-10-31 00:33:20 -07001593 return SpillRegsPreSub(this, core_reg_mask, fp_reg_mask, frame_size);
Andreas Gampef29ecd62014-07-29 00:35:00 -07001594 } else {
Ian Rogers6a3c1fc2014-10-31 00:33:20 -07001595 return SpillRegsPreIndexed(this, base, core_reg_mask, fp_reg_mask);
Andreas Gampef29ecd62014-07-29 00:35:00 -07001596 }
1597}
1598
1599static void UnSpillCoreRegs(Arm64Mir2Lir* m2l, RegStorage base, int offset, uint32_t reg_mask) {
1600 int reg1 = -1, reg2 = -1;
1601 const int reg_log2_size = 3;
1602
1603 for (offset = (offset >> reg_log2_size); reg_mask; offset += 2) {
1604 reg_mask = GenPairWise(reg_mask, & reg1, & reg2);
1605 if (UNLIKELY(reg2 < 0)) {
1606 m2l->NewLIR3(WIDE(kA64Ldr3rXD), RegStorage::Solo64(reg1).GetReg(), base.GetReg(), offset);
1607 } else {
1608 DCHECK_LE(offset, 63);
1609 m2l->NewLIR4(WIDE(kA64Ldp4rrXD), RegStorage::Solo64(reg2).GetReg(),
1610 RegStorage::Solo64(reg1).GetReg(), base.GetReg(), offset);
1611 }
1612 }
1613}
1614
1615static void UnSpillFPRegs(Arm64Mir2Lir* m2l, RegStorage base, int offset, uint32_t reg_mask) {
1616 int reg1 = -1, reg2 = -1;
1617 const int reg_log2_size = 3;
1618
1619 for (offset = (offset >> reg_log2_size); reg_mask; offset += 2) {
1620 reg_mask = GenPairWise(reg_mask, & reg1, & reg2);
1621 if (UNLIKELY(reg2 < 0)) {
Matteo Franchin4163c532014-07-15 15:20:27 +01001622 m2l->NewLIR3(WIDE(kA64Ldr3fXD), RegStorage::FloatSolo64(reg1).GetReg(), base.GetReg(),
Andreas Gampef29ecd62014-07-29 00:35:00 -07001623 offset);
1624 } else {
1625 m2l->NewLIR4(WIDE(kA64Ldp4ffXD), RegStorage::FloatSolo64(reg2).GetReg(),
1626 RegStorage::FloatSolo64(reg1).GetReg(), base.GetReg(), offset);
1627 }
1628 }
1629}
1630
1631void Arm64Mir2Lir::UnspillRegs(RegStorage base, uint32_t core_reg_mask, uint32_t fp_reg_mask,
1632 int frame_size) {
Ian Rogersb28c1c02014-11-08 11:21:21 -08001633 DCHECK_EQ(base, rs_sp);
Andreas Gampef29ecd62014-07-29 00:35:00 -07001634 // Restore saves and drop stack frame.
1635 // 2 versions:
1636 //
1637 // 1. (Original): Try to address directly, then drop the whole frame.
1638 // Limitation: ldp is a 7b signed immediate.
1639 //
1640 // 2. (New): Drop the non-save-part. Then do similar to original, which is now guaranteed to be
1641 // in range. Then drop the rest.
1642 //
1643 // TODO: In methods with few spills but huge frame, it would be better to do non-immediate loads
1644 // in variant 1.
1645
1646 // "Magic" constant, 63 (max signed 7b) * 8.
1647 static constexpr int kMaxFramesizeForOffset = 63 * kArm64PointerSize;
1648
1649 const int num_core_spills = POPCOUNT(core_reg_mask);
1650 const int num_fp_spills = POPCOUNT(fp_reg_mask);
1651
1652 int early_drop = 0;
1653
1654 if (frame_size > kMaxFramesizeForOffset) {
1655 // Second variant. Drop the frame part.
1656
1657 // TODO: Always use the first formula, as num_fp_spills would be zero?
1658 if (fp_reg_mask != 0) {
1659 early_drop = frame_size - kArm64PointerSize * (num_fp_spills + num_core_spills);
1660 } else {
1661 early_drop = frame_size - kArm64PointerSize * num_core_spills;
1662 }
1663
1664 // Drop needs to be 16B aligned, so that SP keeps aligned.
1665 early_drop = RoundDown(early_drop, 16);
1666
1667 OpRegImm64(kOpAdd, rs_sp, early_drop);
1668 }
1669
1670 // Unspill.
1671 if (fp_reg_mask != 0) {
1672 int offset = frame_size - early_drop - kArm64PointerSize * (num_fp_spills + num_core_spills);
1673 UnSpillFPRegs(this, rs_sp, offset, fp_reg_mask);
1674 }
1675 if (core_reg_mask != 0) {
1676 int offset = frame_size - early_drop - kArm64PointerSize * num_core_spills;
1677 UnSpillCoreRegs(this, rs_sp, offset, core_reg_mask);
1678 }
1679
1680 // Drop the (rest of) the frame.
1681 OpRegImm64(kOpAdd, rs_sp, frame_size - early_drop);
1682}
1683
Serban Constantinescu23abec92014-07-02 16:13:38 +01001684bool Arm64Mir2Lir::GenInlinedReverseBits(CallInfo* info, OpSize size) {
Matteo Franchin4163c532014-07-15 15:20:27 +01001685 A64Opcode wide = IsWide(size) ? WIDE(0) : UNWIDE(0);
Serban Constantinescu23abec92014-07-02 16:13:38 +01001686 RegLocation rl_src_i = info->args[0];
Fred Shih37f05ef2014-07-16 18:38:08 -07001687 RegLocation rl_dest = IsWide(size) ? InlineTargetWide(info) : InlineTarget(info); // result reg
Serban Constantinescu23abec92014-07-02 16:13:38 +01001688 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
Fred Shih37f05ef2014-07-16 18:38:08 -07001689 RegLocation rl_i = IsWide(size) ? LoadValueWide(rl_src_i, kCoreReg) : LoadValue(rl_src_i, kCoreReg);
Serban Constantinescu23abec92014-07-02 16:13:38 +01001690 NewLIR2(kA64Rbit2rr | wide, rl_result.reg.GetReg(), rl_i.reg.GetReg());
Fred Shih37f05ef2014-07-16 18:38:08 -07001691 IsWide(size) ? StoreValueWide(rl_dest, rl_result) : StoreValue(rl_dest, rl_result);
Serban Constantinescu23abec92014-07-02 16:13:38 +01001692 return true;
1693}
1694
Matteo Franchin43ec8732014-03-31 15:00:14 +01001695} // namespace art