blob: d92dea21c29155a6dee06a9f3a8356c762cb560a [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
Andreas Gampe0b9203e2015-01-22 20:39:27 -080019#include "codegen_arm64.h"
20
Elliott Hughes8366ca02014-11-17 12:02:05 -080021#include "arch/instruction_set_features.h"
Matteo Franchin43ec8732014-03-31 15:00:14 +010022#include "arm64_lir.h"
Vladimir Marko80afd022015-05-19 18:08:00 +010023#include "base/bit_utils.h"
Andreas Gampe0b9203e2015-01-22 20:39:27 -080024#include "base/logging.h"
25#include "dex/compiler_ir.h"
26#include "dex/mir_graph.h"
Matteo Franchin43ec8732014-03-31 15:00:14 +010027#include "dex/quick/mir_to_lir-inl.h"
buzbeeb5860fb2014-06-21 15:31:01 -070028#include "dex/reg_storage_eq.h"
Andreas Gampe0b9203e2015-01-22 20:39:27 -080029#include "driver/compiler_driver.h"
Matteo Franchin43ec8732014-03-31 15:00:14 +010030#include "entrypoints/quick/quick_entrypoints.h"
Ian Rogers7e70b002014-10-08 11:47:24 -070031#include "mirror/array-inl.h"
Matteo Franchin43ec8732014-03-31 15:00:14 +010032
33namespace art {
34
35LIR* Arm64Mir2Lir::OpCmpBranch(ConditionCode cond, RegStorage src1, RegStorage src2, LIR* target) {
36 OpRegReg(kOpCmp, src1, src2);
37 return OpCondBranch(cond, target);
38}
39
Roland Levillain4b8f1ec2015-08-26 18:34:03 +010040LIR* Arm64Mir2Lir::OpIT(ConditionCode ccode ATTRIBUTE_UNUSED, const char* guide ATTRIBUTE_UNUSED) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +010041 LOG(FATAL) << "Unexpected use of OpIT for Arm64";
Ian Rogers6a3c1fc2014-10-31 00:33:20 -070042 UNREACHABLE();
Matteo Franchin43ec8732014-03-31 15:00:14 +010043}
44
Roland Levillain4b8f1ec2015-08-26 18:34:03 +010045void Arm64Mir2Lir::OpEndIT(LIR* it ATTRIBUTE_UNUSED) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +010046 LOG(FATAL) << "Unexpected use of OpEndIT for Arm64";
Matteo Franchin43ec8732014-03-31 15:00:14 +010047}
48
49/*
50 * 64-bit 3way compare function.
Matteo Franchine45fb9e2014-05-06 10:10:30 +010051 * cmp xA, xB
Zheng Xu511c8a62014-06-03 16:22:23 +080052 * csinc wC, wzr, wzr, eq // wC = (xA == xB) ? 0 : 1
53 * csneg wC, wC, wC, ge // wC = (xA >= xB) ? wC : -wC
Matteo Franchin43ec8732014-03-31 15:00:14 +010054 */
Matteo Franchine45fb9e2014-05-06 10:10:30 +010055void Arm64Mir2Lir::GenCmpLong(RegLocation rl_dest, RegLocation rl_src1,
56 RegLocation rl_src2) {
57 RegLocation rl_result;
Matteo Franchin43ec8732014-03-31 15:00:14 +010058 rl_src1 = LoadValueWide(rl_src1, kCoreReg);
59 rl_src2 = LoadValueWide(rl_src2, kCoreReg);
Matteo Franchine45fb9e2014-05-06 10:10:30 +010060 rl_result = EvalLoc(rl_dest, kCoreReg, true);
Matteo Franchin43ec8732014-03-31 15:00:14 +010061
Matteo Franchine45fb9e2014-05-06 10:10:30 +010062 OpRegReg(kOpCmp, rl_src1.reg, rl_src2.reg);
Zheng Xu511c8a62014-06-03 16:22:23 +080063 NewLIR4(kA64Csinc4rrrc, rl_result.reg.GetReg(), rwzr, rwzr, kArmCondEq);
64 NewLIR4(kA64Csneg4rrrc, rl_result.reg.GetReg(), rl_result.reg.GetReg(),
65 rl_result.reg.GetReg(), kArmCondGe);
66 StoreValue(rl_dest, rl_result);
Serban Constantinescued65c5e2014-05-22 15:10:18 +010067}
68
69void Arm64Mir2Lir::GenShiftOpLong(Instruction::Code opcode, RegLocation rl_dest,
70 RegLocation rl_src1, RegLocation rl_shift) {
71 OpKind op = kOpBkpt;
72 switch (opcode) {
73 case Instruction::SHL_LONG:
74 case Instruction::SHL_LONG_2ADDR:
75 op = kOpLsl;
76 break;
77 case Instruction::SHR_LONG:
78 case Instruction::SHR_LONG_2ADDR:
79 op = kOpAsr;
80 break;
81 case Instruction::USHR_LONG:
82 case Instruction::USHR_LONG_2ADDR:
83 op = kOpLsr;
84 break;
85 default:
86 LOG(FATAL) << "Unexpected case: " << opcode;
87 }
Zheng Xue2eb29e2014-06-12 10:22:33 +080088 rl_shift = LoadValue(rl_shift, kCoreReg);
Serban Constantinescued65c5e2014-05-22 15:10:18 +010089 rl_src1 = LoadValueWide(rl_src1, kCoreReg);
90 RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true);
Zheng Xue2eb29e2014-06-12 10:22:33 +080091 OpRegRegReg(op, rl_result.reg, rl_src1.reg, As64BitReg(rl_shift.reg));
Serban Constantinescued65c5e2014-05-22 15:10:18 +010092 StoreValueWide(rl_dest, rl_result);
Matteo Franchin43ec8732014-03-31 15:00:14 +010093}
94
Andreas Gampe90969af2014-07-15 23:02:11 -070095static constexpr bool kUseDeltaEncodingInGenSelect = false;
Andreas Gampe381f8ac2014-07-10 03:23:41 -070096
Andreas Gampe90969af2014-07-15 23:02:11 -070097void Arm64Mir2Lir::GenSelect(int32_t true_val, int32_t false_val, ConditionCode ccode,
98 RegStorage rs_dest, int result_reg_class) {
99 if (false_val == 0 || // 0 is better as first operand.
100 true_val == 1 || // Potentially Csinc.
101 true_val == -1 || // Potentially Csinv.
102 true_val == false_val + 1) { // Potentially Csinc.
103 ccode = NegateComparison(ccode);
104 std::swap(true_val, false_val);
105 }
106
107 ArmConditionCode code = ArmConditionEncoding(ccode);
108
109 int opcode; // The opcode.
110 RegStorage left_op = RegStorage::InvalidReg(); // The operands.
111 RegStorage right_op = RegStorage::InvalidReg(); // The operands.
112
113 bool is_wide = rs_dest.Is64Bit();
114
115 RegStorage zero_reg = is_wide ? rs_xzr : rs_wzr;
116
117 if (true_val == 0) {
118 left_op = zero_reg;
119 } else {
120 left_op = rs_dest;
121 LoadConstantNoClobber(rs_dest, true_val);
122 }
123 if (false_val == 1) {
124 right_op = zero_reg;
125 opcode = kA64Csinc4rrrc;
126 } else if (false_val == -1) {
127 right_op = zero_reg;
128 opcode = kA64Csinv4rrrc;
129 } else if (false_val == true_val + 1) {
130 right_op = left_op;
131 opcode = kA64Csinc4rrrc;
132 } else if (false_val == -true_val) {
133 right_op = left_op;
134 opcode = kA64Csneg4rrrc;
135 } else if (false_val == ~true_val) {
136 right_op = left_op;
137 opcode = kA64Csinv4rrrc;
138 } else if (true_val == 0) {
139 // left_op is zero_reg.
140 right_op = rs_dest;
141 LoadConstantNoClobber(rs_dest, false_val);
142 opcode = kA64Csel4rrrc;
143 } else {
144 // Generic case.
145 RegStorage t_reg2 = AllocTypedTemp(false, result_reg_class);
146 if (is_wide) {
147 if (t_reg2.Is32Bit()) {
148 t_reg2 = As64BitReg(t_reg2);
149 }
150 } else {
151 if (t_reg2.Is64Bit()) {
152 t_reg2 = As32BitReg(t_reg2);
153 }
154 }
155
156 if (kUseDeltaEncodingInGenSelect) {
157 int32_t delta = false_val - true_val;
158 uint32_t abs_val = delta < 0 ? -delta : delta;
159
160 if (abs_val < 0x1000) { // TODO: Replace with InexpensiveConstant with opcode.
161 // Can encode as immediate to an add.
162 right_op = t_reg2;
163 OpRegRegImm(kOpAdd, t_reg2, left_op, delta);
164 }
165 }
166
167 // Load as constant.
168 if (!right_op.Valid()) {
169 LoadConstantNoClobber(t_reg2, false_val);
170 right_op = t_reg2;
171 }
172
173 opcode = kA64Csel4rrrc;
174 }
175
176 DCHECK(left_op.Valid() && right_op.Valid());
177 NewLIR4(is_wide ? WIDE(opcode) : opcode, rs_dest.GetReg(), left_op.GetReg(), right_op.GetReg(),
178 code);
179}
180
181void Arm64Mir2Lir::GenSelectConst32(RegStorage left_op, RegStorage right_op, ConditionCode code,
182 int32_t true_val, int32_t false_val, RegStorage rs_dest,
Ian Rogers6a3c1fc2014-10-31 00:33:20 -0700183 RegisterClass dest_reg_class) {
Andreas Gampe90969af2014-07-15 23:02:11 -0700184 DCHECK(rs_dest.Valid());
185 OpRegReg(kOpCmp, left_op, right_op);
186 GenSelect(true_val, false_val, code, rs_dest, dest_reg_class);
187}
188
Roland Levillain4b8f1ec2015-08-26 18:34:03 +0100189void Arm64Mir2Lir::GenSelect(BasicBlock* bb ATTRIBUTE_UNUSED, MIR* mir) {
Andreas Gampe90969af2014-07-15 23:02:11 -0700190 RegLocation rl_src = mir_graph_->GetSrc(mir, 0);
191 rl_src = LoadValue(rl_src, rl_src.ref ? kRefReg : kCoreReg);
Andreas Gampe381f8ac2014-07-10 03:23:41 -0700192 // rl_src may be aliased with rl_result/rl_dest, so do compare early.
193 OpRegImm(kOpCmp, rl_src.reg, 0);
194
Andreas Gampe90969af2014-07-15 23:02:11 -0700195 RegLocation rl_dest = mir_graph_->GetDest(mir);
Serban Constantinescu05e27ff2014-05-28 13:21:45 +0100196
Andreas Gampe381f8ac2014-07-10 03:23:41 -0700197 // The kMirOpSelect has two variants, one for constants and one for moves.
Andreas Gampe381f8ac2014-07-10 03:23:41 -0700198 if (mir->ssa_rep->num_uses == 1) {
Andreas Gampe90969af2014-07-15 23:02:11 -0700199 RegLocation rl_result = EvalLoc(rl_dest, rl_dest.ref ? kRefReg : kCoreReg, true);
200 GenSelect(mir->dalvikInsn.vB, mir->dalvikInsn.vC, mir->meta.ccode, rl_result.reg,
201 rl_dest.ref ? kRefReg : kCoreReg);
202 StoreValue(rl_dest, rl_result);
Andreas Gampe381f8ac2014-07-10 03:23:41 -0700203 } else {
204 RegLocation rl_true = mir_graph_->reg_location_[mir->ssa_rep->uses[1]];
205 RegLocation rl_false = mir_graph_->reg_location_[mir->ssa_rep->uses[2]];
206
Andreas Gampe90969af2014-07-15 23:02:11 -0700207 RegisterClass result_reg_class = rl_dest.ref ? kRefReg : kCoreReg;
Andreas Gampe381f8ac2014-07-10 03:23:41 -0700208 rl_true = LoadValue(rl_true, result_reg_class);
209 rl_false = LoadValue(rl_false, result_reg_class);
Andreas Gampe90969af2014-07-15 23:02:11 -0700210 RegLocation rl_result = EvalLoc(rl_dest, result_reg_class, true);
Andreas Gampe381f8ac2014-07-10 03:23:41 -0700211
Andreas Gampe90969af2014-07-15 23:02:11 -0700212 bool is_wide = rl_dest.ref || rl_dest.wide;
Andreas Gampe381f8ac2014-07-10 03:23:41 -0700213 int opcode = is_wide ? WIDE(kA64Csel4rrrc) : kA64Csel4rrrc;
214 NewLIR4(opcode, rl_result.reg.GetReg(),
Andreas Gampe90969af2014-07-15 23:02:11 -0700215 rl_true.reg.GetReg(), rl_false.reg.GetReg(), ArmConditionEncoding(mir->meta.ccode));
216 StoreValue(rl_dest, rl_result);
Andreas Gampe381f8ac2014-07-10 03:23:41 -0700217 }
Matteo Franchin43ec8732014-03-31 15:00:14 +0100218}
219
220void Arm64Mir2Lir::GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir) {
221 RegLocation rl_src1 = mir_graph_->GetSrcWide(mir, 0);
222 RegLocation rl_src2 = mir_graph_->GetSrcWide(mir, 2);
Serban Constantinescu05e27ff2014-05-28 13:21:45 +0100223 LIR* taken = &block_label_list_[bb->taken];
224 LIR* not_taken = &block_label_list_[bb->fall_through];
Matteo Franchin43ec8732014-03-31 15:00:14 +0100225 // Normalize such that if either operand is constant, src2 will be constant.
226 ConditionCode ccode = mir->meta.ccode;
227 if (rl_src1.is_const) {
228 std::swap(rl_src1, rl_src2);
229 ccode = FlipComparisonOrder(ccode);
230 }
Serban Constantinescu05e27ff2014-05-28 13:21:45 +0100231
Andreas Gampe381f8ac2014-07-10 03:23:41 -0700232 rl_src1 = LoadValueWide(rl_src1, kCoreReg);
233
Matteo Franchin43ec8732014-03-31 15:00:14 +0100234 if (rl_src2.is_const) {
Andreas Gampe381f8ac2014-07-10 03:23:41 -0700235 // TODO: Optimize for rl_src1.is_const? (Does happen in the boot image at the moment.)
236
Matteo Franchin43ec8732014-03-31 15:00:14 +0100237 int64_t val = mir_graph_->ConstantValueWide(rl_src2);
Serban Constantinescu05e27ff2014-05-28 13:21:45 +0100238 // Special handling using cbz & cbnz.
239 if (val == 0 && (ccode == kCondEq || ccode == kCondNe)) {
240 OpCmpImmBranch(ccode, rl_src1.reg, 0, taken);
241 OpCmpImmBranch(NegateComparison(ccode), rl_src1.reg, 0, not_taken);
242 return;
Andreas Gampe381f8ac2014-07-10 03:23:41 -0700243 }
244
Serban Constantinescu05e27ff2014-05-28 13:21:45 +0100245 // Only handle Imm if src2 is not already in a register.
Andreas Gampe381f8ac2014-07-10 03:23:41 -0700246 rl_src2 = UpdateLocWide(rl_src2);
247 if (rl_src2.location != kLocPhysReg) {
Serban Constantinescu05e27ff2014-05-28 13:21:45 +0100248 OpRegImm64(kOpCmp, rl_src1.reg, val);
249 OpCondBranch(ccode, taken);
250 OpCondBranch(NegateComparison(ccode), not_taken);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100251 return;
252 }
253 }
Serban Constantinescu05e27ff2014-05-28 13:21:45 +0100254
Matteo Franchin43ec8732014-03-31 15:00:14 +0100255 rl_src2 = LoadValueWide(rl_src2, kCoreReg);
Serban Constantinescu05e27ff2014-05-28 13:21:45 +0100256 OpRegReg(kOpCmp, rl_src1.reg, rl_src2.reg);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100257 OpCondBranch(ccode, taken);
Serban Constantinescu05e27ff2014-05-28 13:21:45 +0100258 OpCondBranch(NegateComparison(ccode), not_taken);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100259}
260
261/*
262 * Generate a register comparison to an immediate and branch. Caller
263 * is responsible for setting branch target field.
264 */
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100265LIR* Arm64Mir2Lir::OpCmpImmBranch(ConditionCode cond, RegStorage reg, int check_value,
266 LIR* target) {
Andreas Gampe9522af92014-07-14 20:16:59 -0700267 LIR* branch = nullptr;
Matteo Franchin43ec8732014-03-31 15:00:14 +0100268 ArmConditionCode arm_cond = ArmConditionEncoding(cond);
Andreas Gampe9522af92014-07-14 20:16:59 -0700269 if (check_value == 0) {
270 if (arm_cond == kArmCondEq || arm_cond == kArmCondNe) {
Matteo Franchin4163c532014-07-15 15:20:27 +0100271 A64Opcode opcode = (arm_cond == kArmCondEq) ? kA64Cbz2rt : kA64Cbnz2rt;
272 A64Opcode wide = reg.Is64Bit() ? WIDE(0) : UNWIDE(0);
Andreas Gampe9522af92014-07-14 20:16:59 -0700273 branch = NewLIR2(opcode | wide, reg.GetReg(), 0);
274 } else if (arm_cond == kArmCondLs) {
275 // kArmCondLs is an unsigned less or equal. A comparison r <= 0 is then the same as cbz.
276 // This case happens for a bounds check of array[0].
Matteo Franchin4163c532014-07-15 15:20:27 +0100277 A64Opcode opcode = kA64Cbz2rt;
278 A64Opcode wide = reg.Is64Bit() ? WIDE(0) : UNWIDE(0);
Andreas Gampe9522af92014-07-14 20:16:59 -0700279 branch = NewLIR2(opcode | wide, reg.GetReg(), 0);
Zheng Xu5d7cdec2014-08-18 17:28:22 +0800280 } else if (arm_cond == kArmCondLt || arm_cond == kArmCondGe) {
Matteo Franchin4163c532014-07-15 15:20:27 +0100281 A64Opcode opcode = (arm_cond == kArmCondLt) ? kA64Tbnz3rht : kA64Tbz3rht;
282 A64Opcode wide = reg.Is64Bit() ? WIDE(0) : UNWIDE(0);
Zheng Xu5d7cdec2014-08-18 17:28:22 +0800283 int value = reg.Is64Bit() ? 63 : 31;
284 branch = NewLIR3(opcode | wide, reg.GetReg(), value, 0);
Andreas Gampe9522af92014-07-14 20:16:59 -0700285 }
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,
Dave Allison69dfe512014-07-11 17:11:58 +0000299 LIR* target, LIR** compare) {
300 DCHECK(compare == nullptr);
Zheng Xu7c1c2632014-06-17 18:17:31 +0800301 // It is possible that temp register is 64-bit. (ArgReg or RefReg)
302 // Always compare 32-bit value no matter what temp_reg is.
303 if (temp_reg.Is64Bit()) {
304 temp_reg = As32BitReg(temp_reg);
305 }
306 Load32Disp(base_reg, offset, temp_reg);
307 LIR* branch = OpCmpImmBranch(cond, temp_reg, check_value, target);
308 return branch;
309}
310
Matteo Franchin43ec8732014-03-31 15:00:14 +0100311LIR* Arm64Mir2Lir::OpRegCopyNoInsert(RegStorage r_dest, RegStorage r_src) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100312 bool dest_is_fp = r_dest.IsFloat();
313 bool src_is_fp = r_src.IsFloat();
Matteo Franchin4163c532014-07-15 15:20:27 +0100314 A64Opcode opcode = kA64Brk1d;
Matteo Franchin43ec8732014-03-31 15:00:14 +0100315 LIR* res;
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100316
317 if (LIKELY(dest_is_fp == src_is_fp)) {
318 if (LIKELY(!dest_is_fp)) {
Andreas Gampe4b537a82014-06-30 22:24:53 -0700319 DCHECK_EQ(r_dest.Is64Bit(), r_src.Is64Bit());
320
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100321 // Core/core copy.
322 // Copies involving the sp register require a different instruction.
323 opcode = UNLIKELY(A64_REG_IS_SP(r_dest.GetReg())) ? kA64Add4RRdT : kA64Mov2rr;
324
325 // TODO(Arm64): kA64Add4RRdT formally has 4 args, but is used as a 2 args instruction.
326 // This currently works because the other arguments are set to 0 by default. We should
327 // rather introduce an alias kA64Mov2RR.
328
329 // core/core copy. Do a x/x copy only if both registers are x.
330 if (r_dest.Is64Bit() && r_src.Is64Bit()) {
331 opcode = WIDE(opcode);
332 }
333 } else {
334 // Float/float copy.
335 bool dest_is_double = r_dest.IsDouble();
336 bool src_is_double = r_src.IsDouble();
337
338 // We do not do float/double or double/float casts here.
339 DCHECK_EQ(dest_is_double, src_is_double);
340
341 // Homogeneous float/float copy.
Matteo Franchin4163c532014-07-15 15:20:27 +0100342 opcode = (dest_is_double) ? WIDE(kA64Fmov2ff) : kA64Fmov2ff;
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100343 }
344 } else {
345 // Inhomogeneous register copy.
346 if (dest_is_fp) {
347 if (r_dest.IsDouble()) {
348 opcode = kA64Fmov2Sx;
349 } else {
Andreas Gampe4b537a82014-06-30 22:24:53 -0700350 r_src = Check32BitReg(r_src);
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100351 opcode = kA64Fmov2sw;
352 }
353 } else {
354 if (r_src.IsDouble()) {
355 opcode = kA64Fmov2xS;
356 } else {
Andreas Gampe4b537a82014-06-30 22:24:53 -0700357 r_dest = Check32BitReg(r_dest);
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100358 opcode = kA64Fmov2ws;
359 }
360 }
Matteo Franchin43ec8732014-03-31 15:00:14 +0100361 }
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100362
Matteo Franchin43ec8732014-03-31 15:00:14 +0100363 res = RawLIR(current_dalvik_offset_, opcode, r_dest.GetReg(), r_src.GetReg());
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100364
Matteo Franchin43ec8732014-03-31 15:00:14 +0100365 if (!(cu_->disable_opt & (1 << kSafeOptimizations)) && r_dest == r_src) {
366 res->flags.is_nop = true;
367 }
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100368
Matteo Franchin43ec8732014-03-31 15:00:14 +0100369 return res;
370}
371
372void Arm64Mir2Lir::OpRegCopy(RegStorage r_dest, RegStorage r_src) {
373 if (r_dest != r_src) {
374 LIR* res = OpRegCopyNoInsert(r_dest, r_src);
375 AppendLIR(res);
376 }
377}
378
379void Arm64Mir2Lir::OpRegCopyWide(RegStorage r_dest, RegStorage r_src) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100380 OpRegCopy(r_dest, r_src);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100381}
382
383// Table of magic divisors
384struct MagicTable {
Matteo Franchin7c6c2ac2014-07-01 18:03:08 +0100385 int magic64_base;
386 int magic64_eor;
387 uint64_t magic64;
388 uint32_t magic32;
Matteo Franchin43ec8732014-03-31 15:00:14 +0100389 uint32_t shift;
390 DividePattern pattern;
391};
392
393static const MagicTable magic_table[] = {
Matteo Franchin7c6c2ac2014-07-01 18:03:08 +0100394 { 0, 0, 0, 0, 0, DivideNone}, // 0
395 { 0, 0, 0, 0, 0, DivideNone}, // 1
396 { 0, 0, 0, 0, 0, DivideNone}, // 2
397 {0x3c, -1, 0x5555555555555556, 0x55555556, 0, Divide3}, // 3
398 { 0, 0, 0, 0, 0, DivideNone}, // 4
399 {0xf9, -1, 0x6666666666666667, 0x66666667, 1, Divide5}, // 5
400 {0x7c, 0x1041, 0x2AAAAAAAAAAAAAAB, 0x2AAAAAAB, 0, Divide3}, // 6
401 { -1, -1, 0x924924924924924A, 0x92492493, 2, Divide7}, // 7
402 { 0, 0, 0, 0, 0, DivideNone}, // 8
403 { -1, -1, 0x38E38E38E38E38E4, 0x38E38E39, 1, Divide5}, // 9
404 {0xf9, -1, 0x6666666666666667, 0x66666667, 2, Divide5}, // 10
405 { -1, -1, 0x2E8BA2E8BA2E8BA3, 0x2E8BA2E9, 1, Divide5}, // 11
406 {0x7c, 0x1041, 0x2AAAAAAAAAAAAAAB, 0x2AAAAAAB, 1, Divide5}, // 12
407 { -1, -1, 0x4EC4EC4EC4EC4EC5, 0x4EC4EC4F, 2, Divide5}, // 13
408 { -1, -1, 0x924924924924924A, 0x92492493, 3, Divide7}, // 14
409 {0x78, -1, 0x8888888888888889, 0x88888889, 3, Divide7}, // 15
Matteo Franchin43ec8732014-03-31 15:00:14 +0100410};
411
412// Integer division by constant via reciprocal multiply (Hacker's Delight, 10-4)
Roland Levillain4b8f1ec2015-08-26 18:34:03 +0100413bool Arm64Mir2Lir::SmallLiteralDivRem(Instruction::Code dalvik_opcode ATTRIBUTE_UNUSED,
414 bool is_div,
415 RegLocation rl_src,
416 RegLocation rl_dest,
417 int lit) {
Matteo Franchinc61b3c92014-06-18 11:52:47 +0100418 if ((lit < 0) || (lit >= static_cast<int>(arraysize(magic_table)))) {
Matteo Franchin43ec8732014-03-31 15:00:14 +0100419 return false;
420 }
421 DividePattern pattern = magic_table[lit].pattern;
422 if (pattern == DivideNone) {
423 return false;
424 }
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100425 // Tuning: add rem patterns
426 if (!is_div) {
427 return false;
428 }
Matteo Franchin43ec8732014-03-31 15:00:14 +0100429
430 RegStorage r_magic = AllocTemp();
Matteo Franchin7c6c2ac2014-07-01 18:03:08 +0100431 LoadConstant(r_magic, magic_table[lit].magic32);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100432 rl_src = LoadValue(rl_src, kCoreReg);
433 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
Matteo Franchinc61b3c92014-06-18 11:52:47 +0100434 RegStorage r_long_mul = AllocTemp();
Matteo Franchin65420b22014-10-27 13:29:30 +0000435 NewLIR3(kA64Smull3xww, As64BitReg(r_long_mul).GetReg(), r_magic.GetReg(), rl_src.reg.GetReg());
Matteo Franchin43ec8732014-03-31 15:00:14 +0100436 switch (pattern) {
437 case Divide3:
Matteo Franchinc61b3c92014-06-18 11:52:47 +0100438 OpRegRegImm(kOpLsr, As64BitReg(r_long_mul), As64BitReg(r_long_mul), 32);
439 OpRegRegRegShift(kOpSub, rl_result.reg, r_long_mul, rl_src.reg, EncodeShift(kA64Asr, 31));
Matteo Franchin43ec8732014-03-31 15:00:14 +0100440 break;
441 case Divide5:
Matteo Franchinc61b3c92014-06-18 11:52:47 +0100442 OpRegRegImm(kOpAsr, As64BitReg(r_long_mul), As64BitReg(r_long_mul),
443 32 + magic_table[lit].shift);
444 OpRegRegRegShift(kOpSub, rl_result.reg, r_long_mul, rl_src.reg, EncodeShift(kA64Asr, 31));
Matteo Franchin43ec8732014-03-31 15:00:14 +0100445 break;
446 case Divide7:
Matteo Franchinc61b3c92014-06-18 11:52:47 +0100447 OpRegRegRegShift(kOpAdd, As64BitReg(r_long_mul), As64BitReg(rl_src.reg),
448 As64BitReg(r_long_mul), EncodeShift(kA64Lsr, 32));
449 OpRegRegImm(kOpAsr, r_long_mul, r_long_mul, magic_table[lit].shift);
450 OpRegRegRegShift(kOpSub, rl_result.reg, r_long_mul, rl_src.reg, EncodeShift(kA64Asr, 31));
Matteo Franchin43ec8732014-03-31 15:00:14 +0100451 break;
452 default:
453 LOG(FATAL) << "Unexpected pattern: " << pattern;
454 }
Matteo Franchin43ec8732014-03-31 15:00:14 +0100455 StoreValue(rl_dest, rl_result);
456 return true;
457}
458
Roland Levillain4b8f1ec2015-08-26 18:34:03 +0100459bool Arm64Mir2Lir::SmallLiteralDivRem64(Instruction::Code dalvik_opcode ATTRIBUTE_UNUSED,
460 bool is_div,
461 RegLocation rl_src,
462 RegLocation rl_dest,
463 int64_t lit) {
Matteo Franchin7c6c2ac2014-07-01 18:03:08 +0100464 if ((lit < 0) || (lit >= static_cast<int>(arraysize(magic_table)))) {
465 return false;
466 }
467 DividePattern pattern = magic_table[lit].pattern;
468 if (pattern == DivideNone) {
469 return false;
470 }
471 // Tuning: add rem patterns
472 if (!is_div) {
473 return false;
474 }
475
476 RegStorage r_magic = AllocTempWide();
477 rl_src = LoadValueWide(rl_src, kCoreReg);
478 RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true);
479 RegStorage r_long_mul = AllocTempWide();
480
481 if (magic_table[lit].magic64_base >= 0) {
482 // Check that the entry in the table is correct.
483 if (kIsDebugBuild) {
484 uint64_t reconstructed_imm;
485 uint64_t base = DecodeLogicalImmediate(/*is_wide*/true, magic_table[lit].magic64_base);
486 if (magic_table[lit].magic64_eor >= 0) {
487 uint64_t eor = DecodeLogicalImmediate(/*is_wide*/true, magic_table[lit].magic64_eor);
488 reconstructed_imm = base ^ eor;
489 } else {
490 reconstructed_imm = base + 1;
491 }
Andreas Gampece410622014-11-24 14:23:53 -0800492 DCHECK_EQ(reconstructed_imm, magic_table[lit].magic64) << " for literal " << lit;
Matteo Franchin7c6c2ac2014-07-01 18:03:08 +0100493 }
494
495 // Load the magic constant in two instructions.
496 NewLIR3(WIDE(kA64Orr3Rrl), r_magic.GetReg(), rxzr, magic_table[lit].magic64_base);
497 if (magic_table[lit].magic64_eor >= 0) {
498 NewLIR3(WIDE(kA64Eor3Rrl), r_magic.GetReg(), r_magic.GetReg(),
499 magic_table[lit].magic64_eor);
500 } else {
501 NewLIR4(WIDE(kA64Add4RRdT), r_magic.GetReg(), r_magic.GetReg(), 1, 0);
502 }
503 } else {
504 LoadConstantWide(r_magic, magic_table[lit].magic64);
505 }
506
507 NewLIR3(kA64Smulh3xxx, r_long_mul.GetReg(), r_magic.GetReg(), rl_src.reg.GetReg());
508 switch (pattern) {
509 case Divide3:
510 OpRegRegRegShift(kOpSub, rl_result.reg, r_long_mul, rl_src.reg, EncodeShift(kA64Asr, 63));
511 break;
512 case Divide5:
513 OpRegRegImm(kOpAsr, r_long_mul, r_long_mul, magic_table[lit].shift);
514 OpRegRegRegShift(kOpSub, rl_result.reg, r_long_mul, rl_src.reg, EncodeShift(kA64Asr, 63));
515 break;
516 case Divide7:
517 OpRegRegReg(kOpAdd, r_long_mul, rl_src.reg, r_long_mul);
518 OpRegRegImm(kOpAsr, r_long_mul, r_long_mul, magic_table[lit].shift);
519 OpRegRegRegShift(kOpSub, rl_result.reg, r_long_mul, rl_src.reg, EncodeShift(kA64Asr, 63));
520 break;
521 default:
522 LOG(FATAL) << "Unexpected pattern: " << pattern;
523 }
524 StoreValueWide(rl_dest, rl_result);
525 return true;
526}
527
Matteo Franchinc61b3c92014-06-18 11:52:47 +0100528// Returns true if it added instructions to 'cu' to divide 'rl_src' by 'lit'
529// and store the result in 'rl_dest'.
530bool Arm64Mir2Lir::HandleEasyDivRem(Instruction::Code dalvik_opcode, bool is_div,
531 RegLocation rl_src, RegLocation rl_dest, int lit) {
Matteo Franchin7c6c2ac2014-07-01 18:03:08 +0100532 return HandleEasyDivRem64(dalvik_opcode, is_div, rl_src, rl_dest, static_cast<int>(lit));
533}
534
535// Returns true if it added instructions to 'cu' to divide 'rl_src' by 'lit'
536// and store the result in 'rl_dest'.
537bool Arm64Mir2Lir::HandleEasyDivRem64(Instruction::Code dalvik_opcode, bool is_div,
538 RegLocation rl_src, RegLocation rl_dest, int64_t lit) {
539 const bool is_64bit = rl_dest.wide;
540 const int nbits = (is_64bit) ? 64 : 32;
541
Matteo Franchinc61b3c92014-06-18 11:52:47 +0100542 if (lit < 2) {
543 return false;
544 }
545 if (!IsPowerOfTwo(lit)) {
Matteo Franchin7c6c2ac2014-07-01 18:03:08 +0100546 if (is_64bit) {
547 return SmallLiteralDivRem64(dalvik_opcode, is_div, rl_src, rl_dest, lit);
548 } else {
549 return SmallLiteralDivRem(dalvik_opcode, is_div, rl_src, rl_dest, static_cast<int32_t>(lit));
550 }
Matteo Franchinc61b3c92014-06-18 11:52:47 +0100551 }
Andreas Gampe7e499922015-01-06 08:28:12 -0800552 int k = CTZ(lit);
Matteo Franchin7c6c2ac2014-07-01 18:03:08 +0100553 if (k >= nbits - 2) {
Matteo Franchinc61b3c92014-06-18 11:52:47 +0100554 // Avoid special cases.
555 return false;
556 }
Matteo Franchin7c6c2ac2014-07-01 18:03:08 +0100557
558 RegLocation rl_result;
559 RegStorage t_reg;
560 if (is_64bit) {
561 rl_src = LoadValueWide(rl_src, kCoreReg);
562 rl_result = EvalLocWide(rl_dest, kCoreReg, true);
563 t_reg = AllocTempWide();
564 } else {
565 rl_src = LoadValue(rl_src, kCoreReg);
566 rl_result = EvalLoc(rl_dest, kCoreReg, true);
567 t_reg = AllocTemp();
568 }
569
570 int shift = EncodeShift(kA64Lsr, nbits - k);
Matteo Franchinc61b3c92014-06-18 11:52:47 +0100571 if (is_div) {
Matteo Franchinc61b3c92014-06-18 11:52:47 +0100572 if (lit == 2) {
573 // Division by 2 is by far the most common division by constant.
Matteo Franchin7c6c2ac2014-07-01 18:03:08 +0100574 OpRegRegRegShift(kOpAdd, t_reg, rl_src.reg, rl_src.reg, shift);
Matteo Franchinc61b3c92014-06-18 11:52:47 +0100575 OpRegRegImm(kOpAsr, rl_result.reg, t_reg, k);
576 } else {
Matteo Franchin7c6c2ac2014-07-01 18:03:08 +0100577 OpRegRegImm(kOpAsr, t_reg, rl_src.reg, nbits - 1);
578 OpRegRegRegShift(kOpAdd, t_reg, rl_src.reg, t_reg, shift);
Matteo Franchinc61b3c92014-06-18 11:52:47 +0100579 OpRegRegImm(kOpAsr, rl_result.reg, t_reg, k);
580 }
581 } else {
Matteo Franchinc61b3c92014-06-18 11:52:47 +0100582 if (lit == 2) {
Matteo Franchin7c6c2ac2014-07-01 18:03:08 +0100583 OpRegRegRegShift(kOpAdd, t_reg, rl_src.reg, rl_src.reg, shift);
584 OpRegRegImm64(kOpAnd, t_reg, t_reg, lit - 1);
585 OpRegRegRegShift(kOpSub, rl_result.reg, t_reg, rl_src.reg, shift);
Matteo Franchinc61b3c92014-06-18 11:52:47 +0100586 } else {
Matteo Franchin7c6c2ac2014-07-01 18:03:08 +0100587 RegStorage t_reg2 = (is_64bit) ? AllocTempWide() : AllocTemp();
588 OpRegRegImm(kOpAsr, t_reg, rl_src.reg, nbits - 1);
589 OpRegRegRegShift(kOpAdd, t_reg2, rl_src.reg, t_reg, shift);
590 OpRegRegImm64(kOpAnd, t_reg2, t_reg2, lit - 1);
591 OpRegRegRegShift(kOpSub, rl_result.reg, t_reg2, t_reg, shift);
Matteo Franchinc61b3c92014-06-18 11:52:47 +0100592 }
593 }
Matteo Franchin7c6c2ac2014-07-01 18:03:08 +0100594
595 if (is_64bit) {
596 StoreValueWide(rl_dest, rl_result);
597 } else {
598 StoreValue(rl_dest, rl_result);
599 }
Matteo Franchinc61b3c92014-06-18 11:52:47 +0100600 return true;
601}
602
Roland Levillain4b8f1ec2015-08-26 18:34:03 +0100603bool Arm64Mir2Lir::EasyMultiply(RegLocation rl_src ATTRIBUTE_UNUSED,
604 RegLocation rl_dest ATTRIBUTE_UNUSED,
605 int lit ATTRIBUTE_UNUSED) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100606 LOG(FATAL) << "Unexpected use of EasyMultiply for Arm64";
Ian Rogers6a3c1fc2014-10-31 00:33:20 -0700607 UNREACHABLE();
Matteo Franchin43ec8732014-03-31 15:00:14 +0100608}
609
Roland Levillain4b8f1ec2015-08-26 18:34:03 +0100610RegLocation Arm64Mir2Lir::GenDivRemLit(RegLocation rl_dest ATTRIBUTE_UNUSED,
611 RegLocation rl_src1 ATTRIBUTE_UNUSED,
612 int lit ATTRIBUTE_UNUSED,
613 bool is_div ATTRIBUTE_UNUSED) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100614 LOG(FATAL) << "Unexpected use of GenDivRemLit for Arm64";
Ian Rogers6a3c1fc2014-10-31 00:33:20 -0700615 UNREACHABLE();
Matteo Franchin43ec8732014-03-31 15:00:14 +0100616}
617
618RegLocation Arm64Mir2Lir::GenDivRemLit(RegLocation rl_dest, RegStorage reg1, int lit, bool is_div) {
619 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
620
621 // Put the literal in a temp.
622 RegStorage lit_temp = AllocTemp();
623 LoadConstant(lit_temp, lit);
624 // Use the generic case for div/rem with arg2 in a register.
625 // TODO: The literal temp can be freed earlier during a modulus to reduce reg pressure.
626 rl_result = GenDivRem(rl_result, reg1, lit_temp, is_div);
627 FreeTemp(lit_temp);
628
629 return rl_result;
630}
631
Roland Levillain4b8f1ec2015-08-26 18:34:03 +0100632RegLocation Arm64Mir2Lir::GenDivRem(RegLocation rl_dest ATTRIBUTE_UNUSED,
633 RegLocation rl_src1 ATTRIBUTE_UNUSED,
634 RegLocation rl_src2 ATTRIBUTE_UNUSED,
635 bool is_div ATTRIBUTE_UNUSED,
636 int flags ATTRIBUTE_UNUSED) {
Matteo Franchin7c6c2ac2014-07-01 18:03:08 +0100637 LOG(FATAL) << "Unexpected use of GenDivRem for Arm64";
Ian Rogers6a3c1fc2014-10-31 00:33:20 -0700638 UNREACHABLE();
Matteo Franchin7c6c2ac2014-07-01 18:03:08 +0100639}
640
Serban Constantinescued65c5e2014-05-22 15:10:18 +0100641RegLocation Arm64Mir2Lir::GenDivRem(RegLocation rl_dest, RegStorage r_src1, RegStorage r_src2,
Matteo Franchin7c6c2ac2014-07-01 18:03:08 +0100642 bool is_div) {
Serban Constantinescued65c5e2014-05-22 15:10:18 +0100643 CHECK_EQ(r_src1.Is64Bit(), r_src2.Is64Bit());
644
Matteo Franchin43ec8732014-03-31 15:00:14 +0100645 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
646 if (is_div) {
Serban Constantinescued65c5e2014-05-22 15:10:18 +0100647 OpRegRegReg(kOpDiv, rl_result.reg, r_src1, r_src2);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100648 } else {
Serban Constantinescued65c5e2014-05-22 15:10:18 +0100649 // temp = r_src1 / r_src2
650 // dest = r_src1 - temp * r_src2
651 RegStorage temp;
Matteo Franchin4163c532014-07-15 15:20:27 +0100652 A64Opcode wide;
Serban Constantinescued65c5e2014-05-22 15:10:18 +0100653 if (rl_result.reg.Is64Bit()) {
654 temp = AllocTempWide();
655 wide = WIDE(0);
656 } else {
657 temp = AllocTemp();
658 wide = UNWIDE(0);
659 }
660 OpRegRegReg(kOpDiv, temp, r_src1, r_src2);
661 NewLIR4(kA64Msub4rrrr | wide, rl_result.reg.GetReg(), temp.GetReg(),
Matteo Franchin65420b22014-10-27 13:29:30 +0000662 r_src2.GetReg(), r_src1.GetReg());
Matteo Franchin43ec8732014-03-31 15:00:14 +0100663 FreeTemp(temp);
664 }
Matteo Franchin43ec8732014-03-31 15:00:14 +0100665 return rl_result;
666}
667
Martyn Capewell9a8a5062014-08-07 11:31:48 +0100668bool Arm64Mir2Lir::GenInlinedAbsInt(CallInfo* info) {
669 RegLocation rl_src = info->args[0];
670 rl_src = LoadValue(rl_src, kCoreReg);
671 RegLocation rl_dest = InlineTarget(info);
672 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
673
674 // Compare the source value with zero. Write the negated value to the result if
675 // negative, otherwise write the original value.
676 OpRegImm(kOpCmp, rl_src.reg, 0);
677 NewLIR4(kA64Csneg4rrrc, rl_result.reg.GetReg(), rl_src.reg.GetReg(), rl_src.reg.GetReg(),
678 kArmCondPl);
679 StoreValue(rl_dest, rl_result);
680 return true;
681}
682
Serban Constantinescu169489b2014-06-11 16:43:35 +0100683bool Arm64Mir2Lir::GenInlinedAbsLong(CallInfo* info) {
684 RegLocation rl_src = info->args[0];
685 rl_src = LoadValueWide(rl_src, kCoreReg);
686 RegLocation rl_dest = InlineTargetWide(info);
687 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
Martyn Capewell9a8a5062014-08-07 11:31:48 +0100688
689 // Compare the source value with zero. Write the negated value to the result if
690 // negative, otherwise write the original value.
691 OpRegImm(kOpCmp, rl_src.reg, 0);
692 NewLIR4(WIDE(kA64Csneg4rrrc), rl_result.reg.GetReg(), rl_src.reg.GetReg(),
693 rl_src.reg.GetReg(), kArmCondPl);
Serban Constantinescu169489b2014-06-11 16:43:35 +0100694 StoreValueWide(rl_dest, rl_result);
695 return true;
696}
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100697
Serban Constantinescu23abec92014-07-02 16:13:38 +0100698bool Arm64Mir2Lir::GenInlinedMinMax(CallInfo* info, bool is_min, bool is_long) {
Serban Constantinescu169489b2014-06-11 16:43:35 +0100699 DCHECK_EQ(cu_->instruction_set, kArm64);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100700 RegLocation rl_src1 = info->args[0];
Serban Constantinescu23abec92014-07-02 16:13:38 +0100701 RegLocation rl_src2 = (is_long) ? info->args[2] : info->args[1];
702 rl_src1 = (is_long) ? LoadValueWide(rl_src1, kCoreReg) : LoadValue(rl_src1, kCoreReg);
703 rl_src2 = (is_long) ? LoadValueWide(rl_src2, kCoreReg) : LoadValue(rl_src2, kCoreReg);
704 RegLocation rl_dest = (is_long) ? InlineTargetWide(info) : InlineTarget(info);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100705 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
706 OpRegReg(kOpCmp, rl_src1.reg, rl_src2.reg);
Serban Constantinescu23abec92014-07-02 16:13:38 +0100707 NewLIR4((is_long) ? WIDE(kA64Csel4rrrc) : kA64Csel4rrrc, rl_result.reg.GetReg(),
708 rl_src1.reg.GetReg(), rl_src2.reg.GetReg(), (is_min) ? kArmCondLt : kArmCondGt);
709 (is_long) ? StoreValueWide(rl_dest, rl_result) :StoreValue(rl_dest, rl_result);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100710 return true;
711}
712
713bool Arm64Mir2Lir::GenInlinedPeek(CallInfo* info, OpSize size) {
714 RegLocation rl_src_address = info->args[0]; // long address
Serban Constantinescu63fe93d2014-06-30 17:10:28 +0100715 RegLocation rl_dest = (size == k64) ? InlineTargetWide(info) : InlineTarget(info);
716 RegLocation rl_address = LoadValueWide(rl_src_address, kCoreReg);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100717 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
Serban Constantinescu169489b2014-06-11 16:43:35 +0100718
Andreas Gampe3c12c512014-06-24 18:46:29 +0000719 LoadBaseDisp(rl_address.reg, 0, rl_result.reg, size, kNotVolatile);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100720 if (size == k64) {
Matteo Franchin43ec8732014-03-31 15:00:14 +0100721 StoreValueWide(rl_dest, rl_result);
722 } else {
723 DCHECK(size == kSignedByte || size == kSignedHalf || size == k32);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100724 StoreValue(rl_dest, rl_result);
725 }
726 return true;
727}
728
729bool Arm64Mir2Lir::GenInlinedPoke(CallInfo* info, OpSize size) {
730 RegLocation rl_src_address = info->args[0]; // long address
Matteo Franchin43ec8732014-03-31 15:00:14 +0100731 RegLocation rl_src_value = info->args[2]; // [size] value
Serban Constantinescu63fe93d2014-06-30 17:10:28 +0100732 RegLocation rl_address = LoadValueWide(rl_src_address, kCoreReg);
Serban Constantinescu169489b2014-06-11 16:43:35 +0100733
734 RegLocation rl_value;
Matteo Franchin43ec8732014-03-31 15:00:14 +0100735 if (size == k64) {
Serban Constantinescu169489b2014-06-11 16:43:35 +0100736 rl_value = LoadValueWide(rl_src_value, kCoreReg);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100737 } else {
738 DCHECK(size == kSignedByte || size == kSignedHalf || size == k32);
Serban Constantinescu169489b2014-06-11 16:43:35 +0100739 rl_value = LoadValue(rl_src_value, kCoreReg);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100740 }
Andreas Gampe3c12c512014-06-24 18:46:29 +0000741 StoreBaseDisp(rl_address.reg, 0, rl_value.reg, size, kNotVolatile);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100742 return true;
743}
744
Matteo Franchin43ec8732014-03-31 15:00:14 +0100745bool Arm64Mir2Lir::GenInlinedCas(CallInfo* info, bool is_long, bool is_object) {
Serban Constantinescu169489b2014-06-11 16:43:35 +0100746 DCHECK_EQ(cu_->instruction_set, kArm64);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100747 // Unused - RegLocation rl_src_unsafe = info->args[0];
748 RegLocation rl_src_obj = info->args[1]; // Object - known non-null
749 RegLocation rl_src_offset = info->args[2]; // long low
Matteo Franchin43ec8732014-03-31 15:00:14 +0100750 RegLocation rl_src_expected = info->args[4]; // int, long or Object
751 // If is_long, high half is in info->args[5]
752 RegLocation rl_src_new_value = info->args[is_long ? 6 : 5]; // int, long or Object
753 // If is_long, high half is in info->args[7]
754 RegLocation rl_dest = InlineTarget(info); // boolean place for result
755
Serban Constantinescu169489b2014-06-11 16:43:35 +0100756 // Load Object and offset
buzbeea0cd2d72014-06-01 09:33:49 -0700757 RegLocation rl_object = LoadValue(rl_src_obj, kRefReg);
Serban Constantinescu63fe93d2014-06-30 17:10:28 +0100758 RegLocation rl_offset = LoadValueWide(rl_src_offset, kCoreReg);
Serban Constantinescu169489b2014-06-11 16:43:35 +0100759
Matteo Franchin43ec8732014-03-31 15:00:14 +0100760 RegLocation rl_new_value;
Serban Constantinescu169489b2014-06-11 16:43:35 +0100761 RegLocation rl_expected;
762 if (is_long) {
Matteo Franchin43ec8732014-03-31 15:00:14 +0100763 rl_new_value = LoadValueWide(rl_src_new_value, kCoreReg);
Serban Constantinescu169489b2014-06-11 16:43:35 +0100764 rl_expected = LoadValueWide(rl_src_expected, kCoreReg);
765 } else {
766 rl_new_value = LoadValue(rl_src_new_value, is_object ? kRefReg : kCoreReg);
767 rl_expected = LoadValue(rl_src_expected, is_object ? kRefReg : kCoreReg);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100768 }
769
770 if (is_object && !mir_graph_->IsConstantNullRef(rl_new_value)) {
771 // Mark card for object assuming new value is stored.
Vladimir Marko743b98c2014-11-24 19:45:41 +0000772 MarkGCCard(0, rl_new_value.reg, rl_object.reg);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100773 }
774
Serban Constantinescu169489b2014-06-11 16:43:35 +0100775 RegStorage r_ptr = AllocTempRef();
Matteo Franchin43ec8732014-03-31 15:00:14 +0100776 OpRegRegReg(kOpAdd, r_ptr, rl_object.reg, rl_offset.reg);
777
778 // Free now unneeded rl_object and rl_offset to give more temps.
779 ClobberSReg(rl_object.s_reg_low);
780 FreeTemp(rl_object.reg);
781 ClobberSReg(rl_offset.s_reg_low);
782 FreeTemp(rl_offset.reg);
783
Matteo Franchin43ec8732014-03-31 15:00:14 +0100784 // do {
785 // tmp = [r_ptr] - expected;
786 // } while (tmp == 0 && failure([r_ptr] <- r_new_value));
787 // result = tmp != 0;
788
Serban Constantinescu169489b2014-06-11 16:43:35 +0100789 RegStorage r_tmp;
Serban Constantinescu63fe93d2014-06-30 17:10:28 +0100790 RegStorage r_tmp_stored;
791 RegStorage rl_new_value_stored = rl_new_value.reg;
Matteo Franchin4163c532014-07-15 15:20:27 +0100792 A64Opcode wide = UNWIDE(0);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100793 if (is_long) {
Serban Constantinescu63fe93d2014-06-30 17:10:28 +0100794 r_tmp_stored = r_tmp = AllocTempWide();
795 wide = WIDE(0);
Serban Constantinescu169489b2014-06-11 16:43:35 +0100796 } else if (is_object) {
Serban Constantinescu63fe93d2014-06-30 17:10:28 +0100797 // References use 64-bit registers, but are stored as compressed 32-bit values.
798 // This means r_tmp_stored != r_tmp.
Serban Constantinescu169489b2014-06-11 16:43:35 +0100799 r_tmp = AllocTempRef();
Serban Constantinescu63fe93d2014-06-30 17:10:28 +0100800 r_tmp_stored = As32BitReg(r_tmp);
801 rl_new_value_stored = As32BitReg(rl_new_value_stored);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100802 } else {
Serban Constantinescu63fe93d2014-06-30 17:10:28 +0100803 r_tmp_stored = r_tmp = AllocTemp();
Matteo Franchin43ec8732014-03-31 15:00:14 +0100804 }
805
Serban Constantinescu63fe93d2014-06-30 17:10:28 +0100806 RegStorage r_tmp32 = (r_tmp.Is32Bit()) ? r_tmp : As32BitReg(r_tmp);
Serban Constantinescu169489b2014-06-11 16:43:35 +0100807 LIR* loop = NewLIR0(kPseudoTargetLabel);
Serban Constantinescu63fe93d2014-06-30 17:10:28 +0100808 NewLIR2(kA64Ldaxr2rX | wide, r_tmp_stored.GetReg(), r_ptr.GetReg());
Serban Constantinescu169489b2014-06-11 16:43:35 +0100809 OpRegReg(kOpCmp, r_tmp, rl_expected.reg);
Vladimir Marko8dea81c2014-06-06 14:50:36 +0100810 DCHECK(last_lir_insn_->u.m.def_mask->HasBit(ResourceMask::kCCode));
Mathieu Chartier2cebb242015-04-21 16:50:40 -0700811 LIR* early_exit = OpCondBranch(kCondNe, nullptr);
Serban Constantinescu63fe93d2014-06-30 17:10:28 +0100812 NewLIR3(kA64Stlxr3wrX | wide, r_tmp32.GetReg(), rl_new_value_stored.GetReg(), r_ptr.GetReg());
813 NewLIR3(kA64Cmp3RdT, r_tmp32.GetReg(), 0, ENCODE_NO_SHIFT);
Serban Constantinescu169489b2014-06-11 16:43:35 +0100814 DCHECK(last_lir_insn_->u.m.def_mask->HasBit(ResourceMask::kCCode));
815 OpCondBranch(kCondNe, loop);
816
Serban Constantinescu63fe93d2014-06-30 17:10:28 +0100817 LIR* exit_loop = NewLIR0(kPseudoTargetLabel);
818 early_exit->target = exit_loop;
819
Serban Constantinescu169489b2014-06-11 16:43:35 +0100820 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
Serban Constantinescu63fe93d2014-06-30 17:10:28 +0100821 NewLIR4(kA64Csinc4rrrc, rl_result.reg.GetReg(), rwzr, rwzr, kArmCondNe);
Serban Constantinescu169489b2014-06-11 16:43:35 +0100822
Matteo Franchin43ec8732014-03-31 15:00:14 +0100823 FreeTemp(r_tmp); // Now unneeded.
Serban Constantinescu169489b2014-06-11 16:43:35 +0100824 FreeTemp(r_ptr); // Now unneeded.
Matteo Franchin43ec8732014-03-31 15:00:14 +0100825
826 StoreValue(rl_dest, rl_result);
827
Matteo Franchin43ec8732014-03-31 15:00:14 +0100828 return true;
829}
830
Zheng Xu947717a2014-08-07 14:05:23 +0800831bool Arm64Mir2Lir::GenInlinedArrayCopyCharArray(CallInfo* info) {
832 constexpr int kLargeArrayThreshold = 512;
833
834 RegLocation rl_src = info->args[0];
835 RegLocation rl_src_pos = info->args[1];
836 RegLocation rl_dst = info->args[2];
837 RegLocation rl_dst_pos = info->args[3];
838 RegLocation rl_length = info->args[4];
839 // Compile time check, handle exception by non-inline method to reduce related meta-data.
840 if ((rl_src_pos.is_const && (mir_graph_->ConstantValue(rl_src_pos) < 0)) ||
841 (rl_dst_pos.is_const && (mir_graph_->ConstantValue(rl_dst_pos) < 0)) ||
842 (rl_length.is_const && (mir_graph_->ConstantValue(rl_length) < 0))) {
843 return false;
844 }
845
846 ClobberCallerSave();
847 LockCallTemps(); // Prepare for explicit register usage.
848 RegStorage rs_src = rs_x0;
849 RegStorage rs_dst = rs_x1;
850 LoadValueDirectFixed(rl_src, rs_src);
851 LoadValueDirectFixed(rl_dst, rs_dst);
852
853 // Handle null pointer exception in slow-path.
854 LIR* src_check_branch = OpCmpImmBranch(kCondEq, rs_src, 0, nullptr);
855 LIR* dst_check_branch = OpCmpImmBranch(kCondEq, rs_dst, 0, nullptr);
856 // Handle potential overlapping in slow-path.
857 // TUNING: Support overlapping cases.
858 LIR* src_dst_same = OpCmpBranch(kCondEq, rs_src, rs_dst, nullptr);
859 // Handle exception or big length in slow-path.
860 RegStorage rs_length = rs_w2;
861 LoadValueDirectFixed(rl_length, rs_length);
862 LIR* len_neg_or_too_big = OpCmpImmBranch(kCondHi, rs_length, kLargeArrayThreshold, nullptr);
863 // Src bounds check.
864 RegStorage rs_src_pos = rs_w3;
865 RegStorage rs_arr_length = rs_w4;
866 LoadValueDirectFixed(rl_src_pos, rs_src_pos);
867 LIR* src_pos_negative = OpCmpImmBranch(kCondLt, rs_src_pos, 0, nullptr);
868 Load32Disp(rs_src, mirror::Array::LengthOffset().Int32Value(), rs_arr_length);
869 OpRegReg(kOpSub, rs_arr_length, rs_src_pos);
870 LIR* src_bad_len = OpCmpBranch(kCondLt, rs_arr_length, rs_length, nullptr);
871 // Dst bounds check.
872 RegStorage rs_dst_pos = rs_w5;
873 LoadValueDirectFixed(rl_dst_pos, rs_dst_pos);
874 LIR* dst_pos_negative = OpCmpImmBranch(kCondLt, rs_dst_pos, 0, nullptr);
875 Load32Disp(rs_dst, mirror::Array::LengthOffset().Int32Value(), rs_arr_length);
876 OpRegReg(kOpSub, rs_arr_length, rs_dst_pos);
877 LIR* dst_bad_len = OpCmpBranch(kCondLt, rs_arr_length, rs_length, nullptr);
878
879 // Everything is checked now.
880 // Set rs_src to the address of the first element to be copied.
881 rs_src_pos = As64BitReg(rs_src_pos);
882 OpRegImm(kOpAdd, rs_src, mirror::Array::DataOffset(2).Int32Value());
883 OpRegRegImm(kOpLsl, rs_src_pos, rs_src_pos, 1);
884 OpRegReg(kOpAdd, rs_src, rs_src_pos);
885 // Set rs_src to the address of the first element to be copied.
886 rs_dst_pos = As64BitReg(rs_dst_pos);
887 OpRegImm(kOpAdd, rs_dst, mirror::Array::DataOffset(2).Int32Value());
888 OpRegRegImm(kOpLsl, rs_dst_pos, rs_dst_pos, 1);
889 OpRegReg(kOpAdd, rs_dst, rs_dst_pos);
890
891 // rs_arr_length won't be not used anymore.
892 RegStorage rs_tmp = rs_arr_length;
893 // Use 64-bit view since rs_length will be used as index.
894 rs_length = As64BitReg(rs_length);
895 OpRegRegImm(kOpLsl, rs_length, rs_length, 1);
896
897 // Copy one element.
Zheng Xu5d7cdec2014-08-18 17:28:22 +0800898 LIR* jmp_to_copy_two = NewLIR3(WIDE(kA64Tbz3rht), rs_length.GetReg(), 1, 0);
Zheng Xu947717a2014-08-07 14:05:23 +0800899 OpRegImm(kOpSub, rs_length, 2);
900 LoadBaseIndexed(rs_src, rs_length, rs_tmp, 0, kSignedHalf);
901 StoreBaseIndexed(rs_dst, rs_length, rs_tmp, 0, kSignedHalf);
902
903 // Copy two elements.
904 LIR *copy_two = NewLIR0(kPseudoTargetLabel);
Zheng Xu5d7cdec2014-08-18 17:28:22 +0800905 LIR* jmp_to_copy_four = NewLIR3(WIDE(kA64Tbz3rht), rs_length.GetReg(), 2, 0);
Zheng Xu947717a2014-08-07 14:05:23 +0800906 OpRegImm(kOpSub, rs_length, 4);
907 LoadBaseIndexed(rs_src, rs_length, rs_tmp, 0, k32);
908 StoreBaseIndexed(rs_dst, rs_length, rs_tmp, 0, k32);
909
910 // Copy four elements.
911 LIR *copy_four = NewLIR0(kPseudoTargetLabel);
912 LIR* jmp_to_ret = OpCmpImmBranch(kCondEq, rs_length, 0, nullptr);
913 LIR *begin_loop = NewLIR0(kPseudoTargetLabel);
914 OpRegImm(kOpSub, rs_length, 8);
915 rs_tmp = As64BitReg(rs_tmp);
916 LoadBaseIndexed(rs_src, rs_length, rs_tmp, 0, k64);
917 StoreBaseIndexed(rs_dst, rs_length, rs_tmp, 0, k64);
918 LIR* jmp_to_loop = OpCmpImmBranch(kCondNe, rs_length, 0, nullptr);
919 LIR* loop_finished = OpUnconditionalBranch(nullptr);
920
921 LIR *check_failed = NewLIR0(kPseudoTargetLabel);
922 LIR* launchpad_branch = OpUnconditionalBranch(nullptr);
923 LIR* return_point = NewLIR0(kPseudoTargetLabel);
924
925 src_check_branch->target = check_failed;
926 dst_check_branch->target = check_failed;
927 src_dst_same->target = check_failed;
928 len_neg_or_too_big->target = check_failed;
929 src_pos_negative->target = check_failed;
930 src_bad_len->target = check_failed;
931 dst_pos_negative->target = check_failed;
932 dst_bad_len->target = check_failed;
933 jmp_to_copy_two->target = copy_two;
934 jmp_to_copy_four->target = copy_four;
935 jmp_to_ret->target = return_point;
936 jmp_to_loop->target = begin_loop;
937 loop_finished->target = return_point;
938
939 AddIntrinsicSlowPath(info, launchpad_branch, return_point);
Serguei Katkov9863daf2014-09-04 15:21:32 +0700940 ClobberCallerSave(); // We must clobber everything because slow path will return here
Zheng Xu947717a2014-08-07 14:05:23 +0800941
942 return true;
943}
944
Vladimir Markof6737f72015-03-23 17:05:14 +0000945void Arm64Mir2Lir::OpPcRelLoad(RegStorage reg, LIR* target) {
Serban Constantinescu63999682014-07-15 17:44:21 +0100946 ScopedMemRefType mem_ref_type(this, ResourceMask::kLiteral);
Vladimir Markof6737f72015-03-23 17:05:14 +0000947 LIR* lir = NewLIR2(kA64Ldr2rp, As32BitReg(reg).GetReg(), 0);
948 lir->target = target;
Matteo Franchin43ec8732014-03-31 15:00:14 +0100949}
950
Vladimir Marko20f85592015-03-19 10:07:02 +0000951bool Arm64Mir2Lir::CanUseOpPcRelDexCacheArrayLoad() const {
Vladimir Marko20f85592015-03-19 10:07:02 +0000952 return dex_cache_arrays_layout_.Valid();
953}
954
Mathieu Chartiere401d142015-04-22 13:56:20 -0700955void Arm64Mir2Lir::OpPcRelDexCacheArrayLoad(const DexFile* dex_file, int offset, RegStorage r_dest,
956 bool wide) {
Vladimir Marko20f85592015-03-19 10:07:02 +0000957 LIR* adrp = NewLIR2(kA64Adrp2xd, r_dest.GetReg(), 0);
958 adrp->operands[2] = WrapPointer(dex_file);
959 adrp->operands[3] = offset;
960 adrp->operands[4] = WrapPointer(adrp);
961 dex_cache_access_insns_.push_back(adrp);
Mathieu Chartiere401d142015-04-22 13:56:20 -0700962 if (wide) {
963 DCHECK(r_dest.Is64Bit());
964 }
965 LIR* ldr = LoadBaseDisp(r_dest, 0, r_dest, wide ? k64 : kReference, kNotVolatile);
Vladimir Marko20f85592015-03-19 10:07:02 +0000966 ldr->operands[4] = adrp->operands[4];
967 ldr->flags.fixup = kFixupLabel;
968 dex_cache_access_insns_.push_back(ldr);
969}
970
Roland Levillain4b8f1ec2015-08-26 18:34:03 +0100971LIR* Arm64Mir2Lir::OpVldm(RegStorage r_base ATTRIBUTE_UNUSED, int count ATTRIBUTE_UNUSED) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100972 LOG(FATAL) << "Unexpected use of OpVldm for Arm64";
Ian Rogers6a3c1fc2014-10-31 00:33:20 -0700973 UNREACHABLE();
Matteo Franchin43ec8732014-03-31 15:00:14 +0100974}
975
Roland Levillain4b8f1ec2015-08-26 18:34:03 +0100976LIR* Arm64Mir2Lir::OpVstm(RegStorage r_base ATTRIBUTE_UNUSED, int count ATTRIBUTE_UNUSED) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100977 LOG(FATAL) << "Unexpected use of OpVstm for Arm64";
Ian Rogers6a3c1fc2014-10-31 00:33:20 -0700978 UNREACHABLE();
Matteo Franchin43ec8732014-03-31 15:00:14 +0100979}
980
Ningsheng Jiana262f772014-11-25 16:48:07 +0800981void Arm64Mir2Lir::GenMaddMsubInt(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2,
982 RegLocation rl_src3, bool is_sub) {
983 rl_src1 = LoadValue(rl_src1, kCoreReg);
984 rl_src2 = LoadValue(rl_src2, kCoreReg);
985 rl_src3 = LoadValue(rl_src3, kCoreReg);
986 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
987 NewLIR4(is_sub ? kA64Msub4rrrr : kA64Madd4rrrr, rl_result.reg.GetReg(), rl_src1.reg.GetReg(),
988 rl_src2.reg.GetReg(), rl_src3.reg.GetReg());
989 StoreValue(rl_dest, rl_result);
990}
991
992void Arm64Mir2Lir::GenMaddMsubLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2,
993 RegLocation rl_src3, bool is_sub) {
994 rl_src1 = LoadValueWide(rl_src1, kCoreReg);
995 rl_src2 = LoadValueWide(rl_src2, kCoreReg);
996 rl_src3 = LoadValueWide(rl_src3, kCoreReg);
997 RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true);
998 NewLIR4(is_sub ? WIDE(kA64Msub4rrrr) : WIDE(kA64Madd4rrrr), rl_result.reg.GetReg(),
999 rl_src1.reg.GetReg(), rl_src2.reg.GetReg(), rl_src3.reg.GetReg());
1000 StoreValueWide(rl_dest, rl_result);
1001}
1002
Matteo Franchin43ec8732014-03-31 15:00:14 +01001003void Arm64Mir2Lir::GenMultiplyByTwoBitMultiplier(RegLocation rl_src,
Ian Rogers6a3c1fc2014-10-31 00:33:20 -07001004 RegLocation rl_result, int lit ATTRIBUTE_UNUSED,
1005 int first_bit, int second_bit) {
Ningsheng Jiana262f772014-11-25 16:48:07 +08001006 OpRegRegRegShift(kOpAdd, rl_result.reg, rl_src.reg, rl_src.reg,
1007 EncodeShift(kA64Lsl, second_bit - first_bit));
Matteo Franchin43ec8732014-03-31 15:00:14 +01001008 if (first_bit != 0) {
1009 OpRegRegImm(kOpLsl, rl_result.reg, rl_result.reg, first_bit);
1010 }
1011}
1012
Ian Rogers6a3c1fc2014-10-31 00:33:20 -07001013void Arm64Mir2Lir::GenDivZeroCheckWide(RegStorage reg ATTRIBUTE_UNUSED) {
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001014 LOG(FATAL) << "Unexpected use of GenDivZero for Arm64";
Matteo Franchin43ec8732014-03-31 15:00:14 +01001015}
1016
1017// Test suspend flag, return target of taken suspend branch
1018LIR* Arm64Mir2Lir::OpTestSuspend(LIR* target) {
Zheng Xu69a50302015-04-14 20:04:41 +08001019 RegStorage r_tmp = AllocTemp();
1020 LoadBaseDisp(rs_xSELF, Thread::ThreadFlagsOffset<kArm64PointerSize>().Int32Value(), r_tmp,
1021 kUnsignedHalf, kNotVolatile);
1022 LIR* cmp_branch = OpCmpImmBranch(target == nullptr ? kCondNe: kCondEq, r_tmp, 0, target);
1023 FreeTemp(r_tmp);
1024 return cmp_branch;
Matteo Franchin43ec8732014-03-31 15:00:14 +01001025}
1026
1027// Decrement register and branch on condition
1028LIR* Arm64Mir2Lir::OpDecAndBranch(ConditionCode c_code, RegStorage reg, LIR* target) {
buzbee33ae5582014-06-12 14:56:32 -07001029 // Combine sub & test using sub setflags encoding here. We need to make sure a
1030 // subtract form that sets carry is used, so generate explicitly.
1031 // TODO: might be best to add a new op, kOpSubs, and handle it generically.
Matteo Franchin4163c532014-07-15 15:20:27 +01001032 A64Opcode opcode = reg.Is64Bit() ? WIDE(kA64Subs3rRd) : UNWIDE(kA64Subs3rRd);
buzbee33ae5582014-06-12 14:56:32 -07001033 NewLIR3(opcode, reg.GetReg(), reg.GetReg(), 1); // For value == 1, this should set flags.
Vladimir Marko8dea81c2014-06-06 14:50:36 +01001034 DCHECK(last_lir_insn_->u.m.def_mask->HasBit(ResourceMask::kCCode));
Matteo Franchin43ec8732014-03-31 15:00:14 +01001035 return OpCondBranch(c_code, target);
1036}
1037
Andreas Gampeb14329f2014-05-15 11:16:06 -07001038bool Arm64Mir2Lir::GenMemBarrier(MemBarrierKind barrier_kind) {
Andreas Gampe0b9203e2015-01-22 20:39:27 -08001039 if (!cu_->compiler_driver->GetInstructionSetFeatures()->IsSmp()) {
Elliott Hughes8366ca02014-11-17 12:02:05 -08001040 return false;
1041 }
Matteo Franchin43ec8732014-03-31 15:00:14 +01001042 // Start off with using the last LIR as the barrier. If it is not enough, then we will generate one.
1043 LIR* barrier = last_lir_insn_;
1044
1045 int dmb_flavor;
1046 // TODO: revisit Arm barrier kinds
1047 switch (barrier_kind) {
Hans Boehm48f5c472014-06-27 14:50:10 -07001048 case kAnyStore: dmb_flavor = kISH; break;
1049 case kLoadAny: dmb_flavor = kISH; break;
1050 // We conjecture that kISHLD is insufficient. It is documented
1051 // to provide LoadLoad | StoreStore ordering. But if this were used
1052 // to implement volatile loads, we suspect that the lack of store
1053 // atomicity on ARM would cause us to allow incorrect results for
1054 // the canonical IRIW example. But we're not sure.
1055 // We should be using acquire loads instead.
Matteo Franchin43ec8732014-03-31 15:00:14 +01001056 case kStoreStore: dmb_flavor = kISHST; break;
Hans Boehm48f5c472014-06-27 14:50:10 -07001057 case kAnyAny: dmb_flavor = kISH; break;
Matteo Franchin43ec8732014-03-31 15:00:14 +01001058 default:
1059 LOG(FATAL) << "Unexpected MemBarrierKind: " << barrier_kind;
1060 dmb_flavor = kSY; // quiet gcc.
1061 break;
1062 }
1063
Andreas Gampeb14329f2014-05-15 11:16:06 -07001064 bool ret = false;
1065
Matteo Franchin43ec8732014-03-31 15:00:14 +01001066 // If the same barrier already exists, don't generate another.
1067 if (barrier == nullptr
Matteo Franchine45fb9e2014-05-06 10:10:30 +01001068 || (barrier->opcode != kA64Dmb1B || barrier->operands[0] != dmb_flavor)) {
1069 barrier = NewLIR1(kA64Dmb1B, dmb_flavor);
Andreas Gampeb14329f2014-05-15 11:16:06 -07001070 ret = true;
Matteo Franchin43ec8732014-03-31 15:00:14 +01001071 }
1072
1073 // At this point we must have a memory barrier. Mark it as a scheduling barrier as well.
1074 DCHECK(!barrier->flags.use_def_invalid);
Vladimir Marko8dea81c2014-06-06 14:50:36 +01001075 barrier->u.m.def_mask = &kEncodeAll;
Andreas Gampeb14329f2014-05-15 11:16:06 -07001076 return ret;
Matteo Franchin43ec8732014-03-31 15:00:14 +01001077}
1078
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001079void Arm64Mir2Lir::GenIntToLong(RegLocation rl_dest, RegLocation rl_src) {
1080 RegLocation rl_result;
1081
1082 rl_src = LoadValue(rl_src, kCoreReg);
1083 rl_result = EvalLocWide(rl_dest, kCoreReg, true);
Andreas Gampe4b537a82014-06-30 22:24:53 -07001084 NewLIR4(WIDE(kA64Sbfm4rrdd), rl_result.reg.GetReg(), As64BitReg(rl_src.reg).GetReg(), 0, 31);
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001085 StoreValueWide(rl_dest, rl_result);
1086}
1087
1088void Arm64Mir2Lir::GenDivRemLong(Instruction::Code opcode, RegLocation rl_dest,
Razvan A Lupusoru5c5676b2014-09-29 16:42:11 -07001089 RegLocation rl_src1, RegLocation rl_src2, bool is_div, int flags) {
Matteo Franchin7c6c2ac2014-07-01 18:03:08 +01001090 if (rl_src2.is_const) {
1091 DCHECK(rl_src2.wide);
1092 int64_t lit = mir_graph_->ConstantValueWide(rl_src2);
1093 if (HandleEasyDivRem64(opcode, is_div, rl_src1, rl_dest, lit)) {
1094 return;
1095 }
1096 }
1097
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001098 RegLocation rl_result;
1099 rl_src1 = LoadValueWide(rl_src1, kCoreReg);
1100 rl_src2 = LoadValueWide(rl_src2, kCoreReg);
Razvan A Lupusoru5c5676b2014-09-29 16:42:11 -07001101 if ((flags & MIR_IGNORE_DIV_ZERO_CHECK) == 0) {
1102 GenDivZeroCheck(rl_src2.reg);
1103 }
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001104 rl_result = GenDivRem(rl_dest, rl_src1.reg, rl_src2.reg, is_div);
Matteo Franchin43ec8732014-03-31 15:00:14 +01001105 StoreValueWide(rl_dest, rl_result);
1106}
1107
Matteo Franchine45fb9e2014-05-06 10:10:30 +01001108void Arm64Mir2Lir::GenLongOp(OpKind op, RegLocation rl_dest, RegLocation rl_src1,
1109 RegLocation rl_src2) {
1110 RegLocation rl_result;
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001111
Matteo Franchine45fb9e2014-05-06 10:10:30 +01001112 rl_src1 = LoadValueWide(rl_src1, kCoreReg);
1113 rl_src2 = LoadValueWide(rl_src2, kCoreReg);
1114 rl_result = EvalLocWide(rl_dest, kCoreReg, true);
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001115 OpRegRegRegShift(op, rl_result.reg, rl_src1.reg, rl_src2.reg, ENCODE_NO_SHIFT);
1116 StoreValueWide(rl_dest, rl_result);
1117}
1118
1119void Arm64Mir2Lir::GenNegLong(RegLocation rl_dest, RegLocation rl_src) {
1120 RegLocation rl_result;
1121
1122 rl_src = LoadValueWide(rl_src, kCoreReg);
1123 rl_result = EvalLocWide(rl_dest, kCoreReg, true);
1124 OpRegRegShift(kOpNeg, rl_result.reg, rl_src.reg, ENCODE_NO_SHIFT);
1125 StoreValueWide(rl_dest, rl_result);
1126}
1127
1128void Arm64Mir2Lir::GenNotLong(RegLocation rl_dest, RegLocation rl_src) {
1129 RegLocation rl_result;
1130
1131 rl_src = LoadValueWide(rl_src, kCoreReg);
1132 rl_result = EvalLocWide(rl_dest, kCoreReg, true);
1133 OpRegRegShift(kOpMvn, rl_result.reg, rl_src.reg, ENCODE_NO_SHIFT);
Matteo Franchine45fb9e2014-05-06 10:10:30 +01001134 StoreValueWide(rl_dest, rl_result);
1135}
1136
Andreas Gampec76c6142014-08-04 16:30:03 -07001137void Arm64Mir2Lir::GenArithOpLong(Instruction::Code opcode, RegLocation rl_dest,
Razvan A Lupusoru5c5676b2014-09-29 16:42:11 -07001138 RegLocation rl_src1, RegLocation rl_src2, int flags) {
Andreas Gampec76c6142014-08-04 16:30:03 -07001139 switch (opcode) {
1140 case Instruction::NOT_LONG:
1141 GenNotLong(rl_dest, rl_src2);
1142 return;
1143 case Instruction::ADD_LONG:
1144 case Instruction::ADD_LONG_2ADDR:
1145 GenLongOp(kOpAdd, rl_dest, rl_src1, rl_src2);
1146 return;
1147 case Instruction::SUB_LONG:
1148 case Instruction::SUB_LONG_2ADDR:
1149 GenLongOp(kOpSub, rl_dest, rl_src1, rl_src2);
1150 return;
1151 case Instruction::MUL_LONG:
1152 case Instruction::MUL_LONG_2ADDR:
1153 GenLongOp(kOpMul, rl_dest, rl_src1, rl_src2);
1154 return;
1155 case Instruction::DIV_LONG:
1156 case Instruction::DIV_LONG_2ADDR:
Razvan A Lupusoru5c5676b2014-09-29 16:42:11 -07001157 GenDivRemLong(opcode, rl_dest, rl_src1, rl_src2, /*is_div*/ true, flags);
Andreas Gampec76c6142014-08-04 16:30:03 -07001158 return;
1159 case Instruction::REM_LONG:
1160 case Instruction::REM_LONG_2ADDR:
Razvan A Lupusoru5c5676b2014-09-29 16:42:11 -07001161 GenDivRemLong(opcode, rl_dest, rl_src1, rl_src2, /*is_div*/ false, flags);
Andreas Gampec76c6142014-08-04 16:30:03 -07001162 return;
1163 case Instruction::AND_LONG_2ADDR:
1164 case Instruction::AND_LONG:
1165 GenLongOp(kOpAnd, rl_dest, rl_src1, rl_src2);
1166 return;
1167 case Instruction::OR_LONG:
1168 case Instruction::OR_LONG_2ADDR:
1169 GenLongOp(kOpOr, rl_dest, rl_src1, rl_src2);
1170 return;
1171 case Instruction::XOR_LONG:
1172 case Instruction::XOR_LONG_2ADDR:
1173 GenLongOp(kOpXor, rl_dest, rl_src1, rl_src2);
1174 return;
1175 case Instruction::NEG_LONG: {
1176 GenNegLong(rl_dest, rl_src2);
1177 return;
1178 }
1179 default:
1180 LOG(FATAL) << "Invalid long arith op";
1181 return;
1182 }
Matteo Franchin43ec8732014-03-31 15:00:14 +01001183}
1184
1185/*
1186 * Generate array load
1187 */
1188void Arm64Mir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array,
1189 RegLocation rl_index, RegLocation rl_dest, int scale) {
1190 RegisterClass reg_class = RegClassBySize(size);
1191 int len_offset = mirror::Array::LengthOffset().Int32Value();
1192 int data_offset;
1193 RegLocation rl_result;
1194 bool constant_index = rl_index.is_const;
buzbeea0cd2d72014-06-01 09:33:49 -07001195 rl_array = LoadValue(rl_array, kRefReg);
Matteo Franchin43ec8732014-03-31 15:00:14 +01001196 if (!constant_index) {
1197 rl_index = LoadValue(rl_index, kCoreReg);
1198 }
1199
1200 if (rl_dest.wide) {
1201 data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Int32Value();
1202 } else {
1203 data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Int32Value();
1204 }
1205
Matteo Franchin43ec8732014-03-31 15:00:14 +01001206 /* null object? */
1207 GenNullCheck(rl_array.reg, opt_flags);
1208
1209 bool needs_range_check = (!(opt_flags & MIR_IGNORE_RANGE_CHECK));
1210 RegStorage reg_len;
1211 if (needs_range_check) {
1212 reg_len = AllocTemp();
1213 /* Get len */
1214 Load32Disp(rl_array.reg, len_offset, reg_len);
1215 MarkPossibleNullPointerException(opt_flags);
1216 } else {
1217 ForceImplicitNullCheck(rl_array.reg, opt_flags);
1218 }
Vladimir Markoe08785b2014-11-07 16:11:00 +00001219 if (constant_index) {
Matteo Franchin43ec8732014-03-31 15:00:14 +01001220 rl_result = EvalLoc(rl_dest, reg_class, true);
1221
1222 if (needs_range_check) {
Vladimir Markoe08785b2014-11-07 16:11:00 +00001223 GenArrayBoundsCheck(mir_graph_->ConstantValue(rl_index), reg_len);
Matteo Franchin43ec8732014-03-31 15:00:14 +01001224 FreeTemp(reg_len);
1225 }
Vladimir Markoe08785b2014-11-07 16:11:00 +00001226 // Fold the constant index into the data offset.
1227 data_offset += mir_graph_->ConstantValue(rl_index) << scale;
Andreas Gampe3c12c512014-06-24 18:46:29 +00001228 if (rl_result.ref) {
Vladimir Markoe08785b2014-11-07 16:11:00 +00001229 LoadRefDisp(rl_array.reg, data_offset, rl_result.reg, kNotVolatile);
Andreas Gampe3c12c512014-06-24 18:46:29 +00001230 } else {
Vladimir Markoe08785b2014-11-07 16:11:00 +00001231 LoadBaseDisp(rl_array.reg, data_offset, rl_result.reg, size, kNotVolatile);
Matteo Franchin43ec8732014-03-31 15:00:14 +01001232 }
1233 } else {
Vladimir Markoe08785b2014-11-07 16:11:00 +00001234 // Offset base, then use indexed load.
buzbeea0cd2d72014-06-01 09:33:49 -07001235 RegStorage reg_ptr = AllocTempRef();
Matteo Franchin43ec8732014-03-31 15:00:14 +01001236 OpRegRegImm(kOpAdd, reg_ptr, rl_array.reg, data_offset);
1237 FreeTemp(rl_array.reg);
1238 rl_result = EvalLoc(rl_dest, reg_class, true);
1239
1240 if (needs_range_check) {
1241 GenArrayBoundsCheck(rl_index.reg, reg_len);
1242 FreeTemp(reg_len);
1243 }
Andreas Gampe3c12c512014-06-24 18:46:29 +00001244 if (rl_result.ref) {
Vladimir Markoe08785b2014-11-07 16:11:00 +00001245 LoadRefIndexed(reg_ptr, rl_index.reg, rl_result.reg, scale);
Andreas Gampe3c12c512014-06-24 18:46:29 +00001246 } else {
Vladimir Markoe08785b2014-11-07 16:11:00 +00001247 LoadBaseIndexed(reg_ptr, rl_index.reg, rl_result.reg, scale, size);
Andreas Gampe3c12c512014-06-24 18:46:29 +00001248 }
Matteo Franchin43ec8732014-03-31 15:00:14 +01001249 FreeTemp(reg_ptr);
Vladimir Markoe08785b2014-11-07 16:11:00 +00001250 }
1251 if (rl_dest.wide) {
1252 StoreValueWide(rl_dest, rl_result);
1253 } else {
Matteo Franchin43ec8732014-03-31 15:00:14 +01001254 StoreValue(rl_dest, rl_result);
1255 }
1256}
1257
1258/*
1259 * Generate array store
1260 *
1261 */
1262void Arm64Mir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array,
1263 RegLocation rl_index, RegLocation rl_src, int scale, bool card_mark) {
1264 RegisterClass reg_class = RegClassBySize(size);
1265 int len_offset = mirror::Array::LengthOffset().Int32Value();
1266 bool constant_index = rl_index.is_const;
1267
1268 int data_offset;
1269 if (size == k64 || size == kDouble) {
1270 data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Int32Value();
1271 } else {
1272 data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Int32Value();
1273 }
1274
buzbeea0cd2d72014-06-01 09:33:49 -07001275 rl_array = LoadValue(rl_array, kRefReg);
Matteo Franchin43ec8732014-03-31 15:00:14 +01001276 if (!constant_index) {
1277 rl_index = LoadValue(rl_index, kCoreReg);
1278 }
1279
1280 RegStorage reg_ptr;
1281 bool allocated_reg_ptr_temp = false;
1282 if (constant_index) {
1283 reg_ptr = rl_array.reg;
1284 } else if (IsTemp(rl_array.reg) && !card_mark) {
1285 Clobber(rl_array.reg);
1286 reg_ptr = rl_array.reg;
1287 } else {
1288 allocated_reg_ptr_temp = true;
buzbeea0cd2d72014-06-01 09:33:49 -07001289 reg_ptr = AllocTempRef();
Matteo Franchin43ec8732014-03-31 15:00:14 +01001290 }
1291
1292 /* null object? */
1293 GenNullCheck(rl_array.reg, opt_flags);
1294
1295 bool needs_range_check = (!(opt_flags & MIR_IGNORE_RANGE_CHECK));
1296 RegStorage reg_len;
1297 if (needs_range_check) {
1298 reg_len = AllocTemp();
1299 // NOTE: max live temps(4) here.
1300 /* Get len */
1301 Load32Disp(rl_array.reg, len_offset, reg_len);
1302 MarkPossibleNullPointerException(opt_flags);
1303 } else {
1304 ForceImplicitNullCheck(rl_array.reg, opt_flags);
1305 }
1306 /* at this point, reg_ptr points to array, 2 live temps */
Vladimir Markoe08785b2014-11-07 16:11:00 +00001307 if (rl_src.wide) {
1308 rl_src = LoadValueWide(rl_src, reg_class);
1309 } else {
1310 rl_src = LoadValue(rl_src, reg_class);
1311 }
1312 if (constant_index) {
Matteo Franchin43ec8732014-03-31 15:00:14 +01001313 if (needs_range_check) {
Vladimir Markoe08785b2014-11-07 16:11:00 +00001314 GenArrayBoundsCheck(mir_graph_->ConstantValue(rl_index), reg_len);
Matteo Franchin43ec8732014-03-31 15:00:14 +01001315 FreeTemp(reg_len);
1316 }
Vladimir Markoe08785b2014-11-07 16:11:00 +00001317 // Fold the constant index into the data offset.
1318 data_offset += mir_graph_->ConstantValue(rl_index) << scale;
Andreas Gampe3c12c512014-06-24 18:46:29 +00001319 if (rl_src.ref) {
1320 StoreRefDisp(reg_ptr, data_offset, rl_src.reg, kNotVolatile);
1321 } else {
1322 StoreBaseDisp(reg_ptr, data_offset, rl_src.reg, size, kNotVolatile);
1323 }
Matteo Franchin43ec8732014-03-31 15:00:14 +01001324 } else {
1325 /* reg_ptr -> array data */
1326 OpRegRegImm(kOpAdd, reg_ptr, rl_array.reg, data_offset);
Matteo Franchin43ec8732014-03-31 15:00:14 +01001327 if (needs_range_check) {
1328 GenArrayBoundsCheck(rl_index.reg, reg_len);
1329 FreeTemp(reg_len);
1330 }
Andreas Gampe3c12c512014-06-24 18:46:29 +00001331 if (rl_src.ref) {
Vladimir Markoe08785b2014-11-07 16:11:00 +00001332 StoreRefIndexed(reg_ptr, rl_index.reg, rl_src.reg, scale);
Andreas Gampe3c12c512014-06-24 18:46:29 +00001333 } else {
Vladimir Markoe08785b2014-11-07 16:11:00 +00001334 StoreBaseIndexed(reg_ptr, rl_index.reg, rl_src.reg, scale, size);
Andreas Gampe3c12c512014-06-24 18:46:29 +00001335 }
Matteo Franchin43ec8732014-03-31 15:00:14 +01001336 }
1337 if (allocated_reg_ptr_temp) {
1338 FreeTemp(reg_ptr);
1339 }
1340 if (card_mark) {
Vladimir Marko743b98c2014-11-24 19:45:41 +00001341 MarkGCCard(opt_flags, rl_src.reg, rl_array.reg);
Matteo Franchin43ec8732014-03-31 15:00:14 +01001342 }
1343}
1344
Matteo Franchin43ec8732014-03-31 15:00:14 +01001345void Arm64Mir2Lir::GenShiftImmOpLong(Instruction::Code opcode,
Razvan A Lupusoru5c5676b2014-09-29 16:42:11 -07001346 RegLocation rl_dest, RegLocation rl_src, RegLocation rl_shift,
Ian Rogers6a3c1fc2014-10-31 00:33:20 -07001347 int flags ATTRIBUTE_UNUSED) {
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001348 OpKind op = kOpBkpt;
Matteo Franchin43ec8732014-03-31 15:00:14 +01001349 // Per spec, we only care about low 6 bits of shift amount.
1350 int shift_amount = mir_graph_->ConstantValue(rl_shift) & 0x3f;
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001351 rl_src = LoadValueWide(rl_src, kCoreReg);
Matteo Franchin43ec8732014-03-31 15:00:14 +01001352 if (shift_amount == 0) {
1353 StoreValueWide(rl_dest, rl_src);
1354 return;
1355 }
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001356
1357 RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true);
Matteo Franchin43ec8732014-03-31 15:00:14 +01001358 switch (opcode) {
1359 case Instruction::SHL_LONG:
1360 case Instruction::SHL_LONG_2ADDR:
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001361 op = kOpLsl;
Matteo Franchin43ec8732014-03-31 15:00:14 +01001362 break;
1363 case Instruction::SHR_LONG:
1364 case Instruction::SHR_LONG_2ADDR:
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001365 op = kOpAsr;
Matteo Franchin43ec8732014-03-31 15:00:14 +01001366 break;
1367 case Instruction::USHR_LONG:
1368 case Instruction::USHR_LONG_2ADDR:
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001369 op = kOpLsr;
Matteo Franchin43ec8732014-03-31 15:00:14 +01001370 break;
1371 default:
1372 LOG(FATAL) << "Unexpected case";
1373 }
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001374 OpRegRegImm(op, rl_result.reg, rl_src.reg, shift_amount);
Matteo Franchin43ec8732014-03-31 15:00:14 +01001375 StoreValueWide(rl_dest, rl_result);
1376}
1377
Matteo Franchine45fb9e2014-05-06 10:10:30 +01001378void Arm64Mir2Lir::GenArithImmOpLong(Instruction::Code opcode, RegLocation rl_dest,
Razvan A Lupusoru5c5676b2014-09-29 16:42:11 -07001379 RegLocation rl_src1, RegLocation rl_src2, int flags) {
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001380 OpKind op = kOpBkpt;
Matteo Franchin43ec8732014-03-31 15:00:14 +01001381 switch (opcode) {
1382 case Instruction::ADD_LONG:
1383 case Instruction::ADD_LONG_2ADDR:
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001384 op = kOpAdd;
1385 break;
Matteo Franchin43ec8732014-03-31 15:00:14 +01001386 case Instruction::SUB_LONG:
1387 case Instruction::SUB_LONG_2ADDR:
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001388 op = kOpSub;
Matteo Franchin43ec8732014-03-31 15:00:14 +01001389 break;
1390 case Instruction::AND_LONG:
1391 case Instruction::AND_LONG_2ADDR:
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001392 op = kOpAnd;
Matteo Franchin43ec8732014-03-31 15:00:14 +01001393 break;
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001394 case Instruction::OR_LONG:
1395 case Instruction::OR_LONG_2ADDR:
1396 op = kOpOr;
Matteo Franchin43ec8732014-03-31 15:00:14 +01001397 break;
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001398 case Instruction::XOR_LONG:
1399 case Instruction::XOR_LONG_2ADDR:
1400 op = kOpXor;
1401 break;
Matteo Franchin43ec8732014-03-31 15:00:14 +01001402 default:
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001403 LOG(FATAL) << "Unexpected opcode";
Matteo Franchin43ec8732014-03-31 15:00:14 +01001404 }
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001405
Matteo Franchinc763e352014-07-04 12:53:27 +01001406 if (op == kOpSub) {
1407 if (!rl_src2.is_const) {
Razvan A Lupusoru5c5676b2014-09-29 16:42:11 -07001408 return GenArithOpLong(opcode, rl_dest, rl_src1, rl_src2, flags);
Matteo Franchinc763e352014-07-04 12:53:27 +01001409 }
1410 } else {
1411 // Associativity.
1412 if (!rl_src2.is_const) {
1413 DCHECK(rl_src1.is_const);
1414 std::swap(rl_src1, rl_src2);
1415 }
1416 }
1417 DCHECK(rl_src2.is_const);
1418 int64_t val = mir_graph_->ConstantValueWide(rl_src2);
1419
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001420 rl_src1 = LoadValueWide(rl_src1, kCoreReg);
1421 RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true);
Zheng Xue2eb29e2014-06-12 10:22:33 +08001422 OpRegRegImm64(op, rl_result.reg, rl_src1.reg, val);
Matteo Franchin43ec8732014-03-31 15:00:14 +01001423 StoreValueWide(rl_dest, rl_result);
1424}
1425
Andreas Gampef29ecd62014-07-29 00:35:00 -07001426static uint32_t ExtractReg(uint32_t reg_mask, int* reg) {
1427 // Find first register.
1428 int first_bit_set = CTZ(reg_mask) + 1;
1429 *reg = *reg + first_bit_set;
1430 reg_mask >>= first_bit_set;
1431 return reg_mask;
1432}
1433
Matteo Franchine45fb9e2014-05-06 10:10:30 +01001434/**
1435 * @brief Split a register list in pairs or registers.
1436 *
1437 * Given a list of registers in @p reg_mask, split the list in pairs. Use as follows:
1438 * @code
1439 * int reg1 = -1, reg2 = -1;
1440 * while (reg_mask) {
1441 * reg_mask = GenPairWise(reg_mask, & reg1, & reg2);
1442 * if (UNLIKELY(reg2 < 0)) {
1443 * // Single register in reg1.
1444 * } else {
1445 * // Pair in reg1, reg2.
1446 * }
1447 * }
1448 * @endcode
1449 */
Andreas Gampef29ecd62014-07-29 00:35:00 -07001450static uint32_t GenPairWise(uint32_t reg_mask, int* reg1, int* reg2) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +01001451 // Find first register.
Andreas Gampef29ecd62014-07-29 00:35:00 -07001452 int first_bit_set = CTZ(reg_mask) + 1;
Matteo Franchine45fb9e2014-05-06 10:10:30 +01001453 int reg = *reg1 + first_bit_set;
1454 reg_mask >>= first_bit_set;
1455
1456 if (LIKELY(reg_mask)) {
1457 // Save the first register, find the second and use the pair opcode.
Andreas Gampef29ecd62014-07-29 00:35:00 -07001458 int second_bit_set = CTZ(reg_mask) + 1;
Matteo Franchine45fb9e2014-05-06 10:10:30 +01001459 *reg2 = reg;
1460 reg_mask >>= second_bit_set;
1461 *reg1 = reg + second_bit_set;
1462 return reg_mask;
1463 }
1464
1465 // Use the single opcode, as we just have one register.
1466 *reg1 = reg;
1467 *reg2 = -1;
1468 return reg_mask;
1469}
1470
David Srbecky1109fb32015-04-07 20:21:06 +01001471static dwarf::Reg DwarfCoreReg(int num) {
1472 return dwarf::Reg::Arm64Core(num);
1473}
1474
1475static dwarf::Reg DwarfFpReg(int num) {
1476 return dwarf::Reg::Arm64Fp(num);
1477}
1478
Andreas Gampef29ecd62014-07-29 00:35:00 -07001479static void SpillCoreRegs(Arm64Mir2Lir* m2l, RegStorage base, int offset, uint32_t reg_mask) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +01001480 int reg1 = -1, reg2 = -1;
Matteo Franchinbc6d1972014-05-13 12:33:28 +01001481 const int reg_log2_size = 3;
Matteo Franchine45fb9e2014-05-06 10:10:30 +01001482
Matteo Franchinbc6d1972014-05-13 12:33:28 +01001483 for (offset = (offset >> reg_log2_size); reg_mask; offset += 2) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +01001484 reg_mask = GenPairWise(reg_mask, & reg1, & reg2);
1485 if (UNLIKELY(reg2 < 0)) {
Andreas Gampef29ecd62014-07-29 00:35:00 -07001486 m2l->NewLIR3(WIDE(kA64Str3rXD), RegStorage::Solo64(reg1).GetReg(), base.GetReg(), offset);
David Srbecky1109fb32015-04-07 20:21:06 +01001487 m2l->cfi().RelOffset(DwarfCoreReg(reg1), offset << reg_log2_size);
Matteo Franchine45fb9e2014-05-06 10:10:30 +01001488 } else {
Andreas Gampef29ecd62014-07-29 00:35:00 -07001489 m2l->NewLIR4(WIDE(kA64Stp4rrXD), RegStorage::Solo64(reg2).GetReg(),
1490 RegStorage::Solo64(reg1).GetReg(), base.GetReg(), offset);
David Srbecky1109fb32015-04-07 20:21:06 +01001491 m2l->cfi().RelOffset(DwarfCoreReg(reg2), offset << reg_log2_size);
1492 m2l->cfi().RelOffset(DwarfCoreReg(reg1), (offset + 1) << reg_log2_size);
Matteo Franchinbc6d1972014-05-13 12:33:28 +01001493 }
1494 }
1495}
1496
1497// TODO(Arm64): consider using ld1 and st1?
Andreas Gampef29ecd62014-07-29 00:35:00 -07001498static void SpillFPRegs(Arm64Mir2Lir* m2l, RegStorage base, int offset, uint32_t reg_mask) {
Matteo Franchinbc6d1972014-05-13 12:33:28 +01001499 int reg1 = -1, reg2 = -1;
1500 const int reg_log2_size = 3;
1501
1502 for (offset = (offset >> reg_log2_size); reg_mask; offset += 2) {
1503 reg_mask = GenPairWise(reg_mask, & reg1, & reg2);
1504 if (UNLIKELY(reg2 < 0)) {
Matteo Franchin4163c532014-07-15 15:20:27 +01001505 m2l->NewLIR3(WIDE(kA64Str3fXD), RegStorage::FloatSolo64(reg1).GetReg(), base.GetReg(),
Andreas Gampef29ecd62014-07-29 00:35:00 -07001506 offset);
David Srbecky1109fb32015-04-07 20:21:06 +01001507 m2l->cfi().RelOffset(DwarfFpReg(reg1), offset << reg_log2_size);
Matteo Franchinbc6d1972014-05-13 12:33:28 +01001508 } else {
Andreas Gampef29ecd62014-07-29 00:35:00 -07001509 m2l->NewLIR4(WIDE(kA64Stp4ffXD), RegStorage::FloatSolo64(reg2).GetReg(),
1510 RegStorage::FloatSolo64(reg1).GetReg(), base.GetReg(), offset);
David Srbecky1109fb32015-04-07 20:21:06 +01001511 m2l->cfi().RelOffset(DwarfFpReg(reg2), offset << reg_log2_size);
1512 m2l->cfi().RelOffset(DwarfFpReg(reg1), (offset + 1) << reg_log2_size);
Matteo Franchine45fb9e2014-05-06 10:10:30 +01001513 }
1514 }
1515}
1516
Ian Rogers6a3c1fc2014-10-31 00:33:20 -07001517static int SpillRegsPreSub(Arm64Mir2Lir* m2l, uint32_t core_reg_mask, uint32_t fp_reg_mask,
1518 int frame_size) {
Andreas Gampef29ecd62014-07-29 00:35:00 -07001519 m2l->OpRegRegImm(kOpSub, rs_sp, rs_sp, frame_size);
David Srbecky1109fb32015-04-07 20:21:06 +01001520 m2l->cfi().AdjustCFAOffset(frame_size);
Andreas Gampef29ecd62014-07-29 00:35:00 -07001521
1522 int core_count = POPCOUNT(core_reg_mask);
1523
1524 if (fp_reg_mask != 0) {
1525 // Spill FP regs.
1526 int fp_count = POPCOUNT(fp_reg_mask);
1527 int spill_offset = frame_size - (core_count + fp_count) * kArm64PointerSize;
1528 SpillFPRegs(m2l, rs_sp, spill_offset, fp_reg_mask);
1529 }
1530
1531 if (core_reg_mask != 0) {
1532 // Spill core regs.
1533 int spill_offset = frame_size - (core_count * kArm64PointerSize);
1534 SpillCoreRegs(m2l, rs_sp, spill_offset, core_reg_mask);
1535 }
1536
1537 return frame_size;
1538}
1539
1540static int SpillRegsPreIndexed(Arm64Mir2Lir* m2l, RegStorage base, uint32_t core_reg_mask,
Ian Rogers6a3c1fc2014-10-31 00:33:20 -07001541 uint32_t fp_reg_mask) {
Andreas Gampef29ecd62014-07-29 00:35:00 -07001542 // Otherwise, spill both core and fp regs at the same time.
1543 // The very first instruction will be an stp with pre-indexed address, moving the stack pointer
1544 // down. From then on, we fill upwards. This will generate overall the same number of instructions
1545 // as the specialized code above in most cases (exception being odd number of core and even
1546 // non-zero fp spills), but is more flexible, as the offsets are guaranteed small.
1547 //
1548 // Some demonstrative fill cases : (c) = core, (f) = fp
1549 // cc 44 cc 44 cc 22 cc 33 fc => 1[1/2]
1550 // fc => 23 fc => 23 ff => 11 ff => 22
1551 // ff 11 f 11 f 11
1552 //
1553 int reg1 = -1, reg2 = -1;
1554 int core_count = POPCOUNT(core_reg_mask);
1555 int fp_count = POPCOUNT(fp_reg_mask);
1556
1557 int combined = fp_count + core_count;
1558 int all_offset = RoundUp(combined, 2); // Needs to be 16B = 2-reg aligned.
1559
1560 int cur_offset = 2; // What's the starting offset after the first stp? We expect the base slot
1561 // to be filled.
1562
1563 // First figure out whether the bottom is FP or core.
1564 if (fp_count > 0) {
1565 // Some FP spills.
1566 //
1567 // Four cases: (d0 is dummy to fill up stp)
1568 // 1) Single FP, even number of core -> stp d0, fp_reg
1569 // 2) Single FP, odd number of core -> stp fp_reg, d0
1570 // 3) More FP, even number combined -> stp fp_reg1, fp_reg2
1571 // 4) More FP, odd number combined -> stp d0, fp_reg
1572 if (fp_count == 1) {
1573 fp_reg_mask = ExtractReg(fp_reg_mask, &reg1);
1574 DCHECK_EQ(fp_reg_mask, 0U);
1575 if (core_count % 2 == 0) {
1576 m2l->NewLIR4(WIDE(kA64StpPre4ffXD),
1577 RegStorage::FloatSolo64(reg1).GetReg(),
1578 RegStorage::FloatSolo64(reg1).GetReg(),
1579 base.GetReg(), -all_offset);
David Srbecky1109fb32015-04-07 20:21:06 +01001580 m2l->cfi().AdjustCFAOffset(all_offset * kArm64PointerSize);
1581 m2l->cfi().RelOffset(DwarfFpReg(reg1), kArm64PointerSize);
Andreas Gampef29ecd62014-07-29 00:35:00 -07001582 } else {
1583 m2l->NewLIR4(WIDE(kA64StpPre4ffXD),
1584 RegStorage::FloatSolo64(reg1).GetReg(),
1585 RegStorage::FloatSolo64(reg1).GetReg(),
1586 base.GetReg(), -all_offset);
David Srbecky1109fb32015-04-07 20:21:06 +01001587 m2l->cfi().AdjustCFAOffset(all_offset * kArm64PointerSize);
1588 m2l->cfi().RelOffset(DwarfFpReg(reg1), 0);
Andreas Gampef29ecd62014-07-29 00:35:00 -07001589 cur_offset = 0; // That core reg needs to go into the upper half.
1590 }
1591 } else {
1592 if (combined % 2 == 0) {
1593 fp_reg_mask = GenPairWise(fp_reg_mask, &reg1, &reg2);
1594 m2l->NewLIR4(WIDE(kA64StpPre4ffXD), RegStorage::FloatSolo64(reg2).GetReg(),
1595 RegStorage::FloatSolo64(reg1).GetReg(), base.GetReg(), -all_offset);
David Srbecky1109fb32015-04-07 20:21:06 +01001596 m2l->cfi().AdjustCFAOffset(all_offset * kArm64PointerSize);
1597 m2l->cfi().RelOffset(DwarfFpReg(reg2), 0);
1598 m2l->cfi().RelOffset(DwarfFpReg(reg1), kArm64PointerSize);
Andreas Gampef29ecd62014-07-29 00:35:00 -07001599 } else {
1600 fp_reg_mask = ExtractReg(fp_reg_mask, &reg1);
1601 m2l->NewLIR4(WIDE(kA64StpPre4ffXD), rs_d0.GetReg(), RegStorage::FloatSolo64(reg1).GetReg(),
1602 base.GetReg(), -all_offset);
David Srbecky1109fb32015-04-07 20:21:06 +01001603 m2l->cfi().AdjustCFAOffset(all_offset * kArm64PointerSize);
1604 m2l->cfi().RelOffset(DwarfFpReg(reg1), kArm64PointerSize);
Andreas Gampef29ecd62014-07-29 00:35:00 -07001605 }
1606 }
1607 } else {
1608 // No FP spills.
1609 //
1610 // Two cases:
1611 // 1) Even number of core -> stp core1, core2
1612 // 2) Odd number of core -> stp xzr, core1
1613 if (core_count % 2 == 1) {
1614 core_reg_mask = ExtractReg(core_reg_mask, &reg1);
1615 m2l->NewLIR4(WIDE(kA64StpPre4rrXD), rs_xzr.GetReg(),
1616 RegStorage::Solo64(reg1).GetReg(), base.GetReg(), -all_offset);
David Srbecky1109fb32015-04-07 20:21:06 +01001617 m2l->cfi().AdjustCFAOffset(all_offset * kArm64PointerSize);
1618 m2l->cfi().RelOffset(DwarfCoreReg(reg1), kArm64PointerSize);
Andreas Gampef29ecd62014-07-29 00:35:00 -07001619 } else {
1620 core_reg_mask = GenPairWise(core_reg_mask, &reg1, &reg2);
1621 m2l->NewLIR4(WIDE(kA64StpPre4rrXD), RegStorage::Solo64(reg2).GetReg(),
1622 RegStorage::Solo64(reg1).GetReg(), base.GetReg(), -all_offset);
David Srbecky1109fb32015-04-07 20:21:06 +01001623 m2l->cfi().AdjustCFAOffset(all_offset * kArm64PointerSize);
1624 m2l->cfi().RelOffset(DwarfCoreReg(reg2), 0);
1625 m2l->cfi().RelOffset(DwarfCoreReg(reg1), kArm64PointerSize);
Andreas Gampef29ecd62014-07-29 00:35:00 -07001626 }
1627 }
David Srbecky1109fb32015-04-07 20:21:06 +01001628 DCHECK_EQ(m2l->cfi().GetCurrentCFAOffset(),
1629 static_cast<int>(all_offset * kArm64PointerSize));
Andreas Gampef29ecd62014-07-29 00:35:00 -07001630
1631 if (fp_count != 0) {
1632 for (; fp_reg_mask != 0;) {
1633 // Have some FP regs to do.
1634 fp_reg_mask = GenPairWise(fp_reg_mask, &reg1, &reg2);
1635 if (UNLIKELY(reg2 < 0)) {
Matteo Franchin4163c532014-07-15 15:20:27 +01001636 m2l->NewLIR3(WIDE(kA64Str3fXD), RegStorage::FloatSolo64(reg1).GetReg(), base.GetReg(),
Andreas Gampef29ecd62014-07-29 00:35:00 -07001637 cur_offset);
David Srbecky1109fb32015-04-07 20:21:06 +01001638 m2l->cfi().RelOffset(DwarfFpReg(reg1), cur_offset * kArm64PointerSize);
Andreas Gampef29ecd62014-07-29 00:35:00 -07001639 // Do not increment offset here, as the second half will be filled by a core reg.
1640 } else {
1641 m2l->NewLIR4(WIDE(kA64Stp4ffXD), RegStorage::FloatSolo64(reg2).GetReg(),
1642 RegStorage::FloatSolo64(reg1).GetReg(), base.GetReg(), cur_offset);
David Srbecky1109fb32015-04-07 20:21:06 +01001643 m2l->cfi().RelOffset(DwarfFpReg(reg2), cur_offset * kArm64PointerSize);
1644 m2l->cfi().RelOffset(DwarfFpReg(reg1), (cur_offset + 1) * kArm64PointerSize);
Andreas Gampef29ecd62014-07-29 00:35:00 -07001645 cur_offset += 2;
1646 }
1647 }
1648
1649 // Reset counting.
1650 reg1 = -1;
1651
1652 // If there is an odd number of core registers, we need to store the bottom now.
1653 if (core_count % 2 == 1) {
1654 core_reg_mask = ExtractReg(core_reg_mask, &reg1);
1655 m2l->NewLIR3(WIDE(kA64Str3rXD), RegStorage::Solo64(reg1).GetReg(), base.GetReg(),
1656 cur_offset + 1);
David Srbecky1109fb32015-04-07 20:21:06 +01001657 m2l->cfi().RelOffset(DwarfCoreReg(reg1), (cur_offset + 1) * kArm64PointerSize);
Andreas Gampef29ecd62014-07-29 00:35:00 -07001658 cur_offset += 2; // Half-slot filled now.
1659 }
1660 }
1661
1662 // Spill the rest of the core regs. They are guaranteed to be even.
1663 DCHECK_EQ(POPCOUNT(core_reg_mask) % 2, 0);
1664 for (; core_reg_mask != 0; cur_offset += 2) {
1665 core_reg_mask = GenPairWise(core_reg_mask, &reg1, &reg2);
1666 m2l->NewLIR4(WIDE(kA64Stp4rrXD), RegStorage::Solo64(reg2).GetReg(),
1667 RegStorage::Solo64(reg1).GetReg(), base.GetReg(), cur_offset);
David Srbecky1109fb32015-04-07 20:21:06 +01001668 m2l->cfi().RelOffset(DwarfCoreReg(reg2), cur_offset * kArm64PointerSize);
1669 m2l->cfi().RelOffset(DwarfCoreReg(reg1), (cur_offset + 1) * kArm64PointerSize);
Andreas Gampef29ecd62014-07-29 00:35:00 -07001670 }
1671
1672 DCHECK_EQ(cur_offset, all_offset);
1673
1674 return all_offset * 8;
1675}
1676
1677int Arm64Mir2Lir::SpillRegs(RegStorage base, uint32_t core_reg_mask, uint32_t fp_reg_mask,
1678 int frame_size) {
1679 // If the frame size is small enough that all offsets would fit into the immediates, use that
1680 // setup, as it decrements sp early (kind of instruction scheduling), and is not worse
1681 // instruction-count wise than the complicated code below.
1682 //
1683 // This case is also optimal when we have an odd number of core spills, and an even (non-zero)
1684 // number of fp spills.
1685 if ((RoundUp(frame_size, 8) / 8 <= 63)) {
Ian Rogers6a3c1fc2014-10-31 00:33:20 -07001686 return SpillRegsPreSub(this, core_reg_mask, fp_reg_mask, frame_size);
Andreas Gampef29ecd62014-07-29 00:35:00 -07001687 } else {
Ian Rogers6a3c1fc2014-10-31 00:33:20 -07001688 return SpillRegsPreIndexed(this, base, core_reg_mask, fp_reg_mask);
Andreas Gampef29ecd62014-07-29 00:35:00 -07001689 }
1690}
1691
1692static void UnSpillCoreRegs(Arm64Mir2Lir* m2l, RegStorage base, int offset, uint32_t reg_mask) {
1693 int reg1 = -1, reg2 = -1;
1694 const int reg_log2_size = 3;
1695
1696 for (offset = (offset >> reg_log2_size); reg_mask; offset += 2) {
1697 reg_mask = GenPairWise(reg_mask, & reg1, & reg2);
1698 if (UNLIKELY(reg2 < 0)) {
1699 m2l->NewLIR3(WIDE(kA64Ldr3rXD), RegStorage::Solo64(reg1).GetReg(), base.GetReg(), offset);
David Srbecky1109fb32015-04-07 20:21:06 +01001700 m2l->cfi().Restore(DwarfCoreReg(reg1));
Andreas Gampef29ecd62014-07-29 00:35:00 -07001701 } else {
1702 DCHECK_LE(offset, 63);
1703 m2l->NewLIR4(WIDE(kA64Ldp4rrXD), RegStorage::Solo64(reg2).GetReg(),
1704 RegStorage::Solo64(reg1).GetReg(), base.GetReg(), offset);
David Srbecky1109fb32015-04-07 20:21:06 +01001705 m2l->cfi().Restore(DwarfCoreReg(reg2));
1706 m2l->cfi().Restore(DwarfCoreReg(reg1));
Andreas Gampef29ecd62014-07-29 00:35:00 -07001707 }
1708 }
1709}
1710
1711static void UnSpillFPRegs(Arm64Mir2Lir* m2l, RegStorage base, int offset, uint32_t reg_mask) {
1712 int reg1 = -1, reg2 = -1;
1713 const int reg_log2_size = 3;
1714
1715 for (offset = (offset >> reg_log2_size); reg_mask; offset += 2) {
1716 reg_mask = GenPairWise(reg_mask, & reg1, & reg2);
1717 if (UNLIKELY(reg2 < 0)) {
Matteo Franchin4163c532014-07-15 15:20:27 +01001718 m2l->NewLIR3(WIDE(kA64Ldr3fXD), RegStorage::FloatSolo64(reg1).GetReg(), base.GetReg(),
Andreas Gampef29ecd62014-07-29 00:35:00 -07001719 offset);
David Srbecky1109fb32015-04-07 20:21:06 +01001720 m2l->cfi().Restore(DwarfFpReg(reg1));
Andreas Gampef29ecd62014-07-29 00:35:00 -07001721 } else {
1722 m2l->NewLIR4(WIDE(kA64Ldp4ffXD), RegStorage::FloatSolo64(reg2).GetReg(),
1723 RegStorage::FloatSolo64(reg1).GetReg(), base.GetReg(), offset);
David Srbecky1109fb32015-04-07 20:21:06 +01001724 m2l->cfi().Restore(DwarfFpReg(reg2));
1725 m2l->cfi().Restore(DwarfFpReg(reg1));
Andreas Gampef29ecd62014-07-29 00:35:00 -07001726 }
1727 }
1728}
1729
1730void Arm64Mir2Lir::UnspillRegs(RegStorage base, uint32_t core_reg_mask, uint32_t fp_reg_mask,
1731 int frame_size) {
Ian Rogersb28c1c02014-11-08 11:21:21 -08001732 DCHECK_EQ(base, rs_sp);
Andreas Gampef29ecd62014-07-29 00:35:00 -07001733 // Restore saves and drop stack frame.
1734 // 2 versions:
1735 //
1736 // 1. (Original): Try to address directly, then drop the whole frame.
1737 // Limitation: ldp is a 7b signed immediate.
1738 //
1739 // 2. (New): Drop the non-save-part. Then do similar to original, which is now guaranteed to be
1740 // in range. Then drop the rest.
1741 //
1742 // TODO: In methods with few spills but huge frame, it would be better to do non-immediate loads
1743 // in variant 1.
1744
1745 // "Magic" constant, 63 (max signed 7b) * 8.
1746 static constexpr int kMaxFramesizeForOffset = 63 * kArm64PointerSize;
1747
1748 const int num_core_spills = POPCOUNT(core_reg_mask);
1749 const int num_fp_spills = POPCOUNT(fp_reg_mask);
1750
1751 int early_drop = 0;
1752
1753 if (frame_size > kMaxFramesizeForOffset) {
1754 // Second variant. Drop the frame part.
1755
1756 // TODO: Always use the first formula, as num_fp_spills would be zero?
1757 if (fp_reg_mask != 0) {
1758 early_drop = frame_size - kArm64PointerSize * (num_fp_spills + num_core_spills);
1759 } else {
1760 early_drop = frame_size - kArm64PointerSize * num_core_spills;
1761 }
1762
1763 // Drop needs to be 16B aligned, so that SP keeps aligned.
1764 early_drop = RoundDown(early_drop, 16);
1765
1766 OpRegImm64(kOpAdd, rs_sp, early_drop);
David Srbecky1109fb32015-04-07 20:21:06 +01001767 cfi_.AdjustCFAOffset(-early_drop);
Andreas Gampef29ecd62014-07-29 00:35:00 -07001768 }
1769
1770 // Unspill.
1771 if (fp_reg_mask != 0) {
1772 int offset = frame_size - early_drop - kArm64PointerSize * (num_fp_spills + num_core_spills);
1773 UnSpillFPRegs(this, rs_sp, offset, fp_reg_mask);
1774 }
1775 if (core_reg_mask != 0) {
1776 int offset = frame_size - early_drop - kArm64PointerSize * num_core_spills;
1777 UnSpillCoreRegs(this, rs_sp, offset, core_reg_mask);
1778 }
1779
1780 // Drop the (rest of) the frame.
David Srbecky1109fb32015-04-07 20:21:06 +01001781 int adjust = frame_size - early_drop;
1782 OpRegImm64(kOpAdd, rs_sp, adjust);
1783 cfi_.AdjustCFAOffset(-adjust);
Andreas Gampef29ecd62014-07-29 00:35:00 -07001784}
1785
Serban Constantinescu23abec92014-07-02 16:13:38 +01001786bool Arm64Mir2Lir::GenInlinedReverseBits(CallInfo* info, OpSize size) {
Matteo Franchin4163c532014-07-15 15:20:27 +01001787 A64Opcode wide = IsWide(size) ? WIDE(0) : UNWIDE(0);
Serban Constantinescu23abec92014-07-02 16:13:38 +01001788 RegLocation rl_src_i = info->args[0];
Fred Shih37f05ef2014-07-16 18:38:08 -07001789 RegLocation rl_dest = IsWide(size) ? InlineTargetWide(info) : InlineTarget(info); // result reg
Serban Constantinescu23abec92014-07-02 16:13:38 +01001790 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
Ningsheng Jiana262f772014-11-25 16:48:07 +08001791 RegLocation rl_i = IsWide(size) ?
1792 LoadValueWide(rl_src_i, kCoreReg) : LoadValue(rl_src_i, kCoreReg);
Serban Constantinescu23abec92014-07-02 16:13:38 +01001793 NewLIR2(kA64Rbit2rr | wide, rl_result.reg.GetReg(), rl_i.reg.GetReg());
Fred Shih37f05ef2014-07-16 18:38:08 -07001794 IsWide(size) ? StoreValueWide(rl_dest, rl_result) : StoreValue(rl_dest, rl_result);
Serban Constantinescu23abec92014-07-02 16:13:38 +01001795 return true;
1796}
1797
Matteo Franchin43ec8732014-03-31 15:00:14 +01001798} // namespace art