blob: f7aa39f4c2a9352f549e2a0276a66e7a439ca1ab [file] [log] [blame]
Matteo Franchin43ec8732014-03-31 15:00:14 +01001/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/* This file contains codegen for the Thumb2 ISA. */
18
19#include "arm64_lir.h"
20#include "codegen_arm64.h"
21#include "dex/quick/mir_to_lir-inl.h"
buzbeeb5860fb2014-06-21 15:31:01 -070022#include "dex/reg_storage_eq.h"
Matteo Franchin43ec8732014-03-31 15:00:14 +010023#include "entrypoints/quick/quick_entrypoints.h"
24#include "mirror/array.h"
25
26namespace art {
27
28LIR* Arm64Mir2Lir::OpCmpBranch(ConditionCode cond, RegStorage src1, RegStorage src2, LIR* target) {
29 OpRegReg(kOpCmp, src1, src2);
30 return OpCondBranch(cond, target);
31}
32
Matteo Franchin43ec8732014-03-31 15:00:14 +010033LIR* Arm64Mir2Lir::OpIT(ConditionCode ccode, const char* guide) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +010034 LOG(FATAL) << "Unexpected use of OpIT for Arm64";
35 return NULL;
Matteo Franchin43ec8732014-03-31 15:00:14 +010036}
37
38void Arm64Mir2Lir::OpEndIT(LIR* it) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +010039 LOG(FATAL) << "Unexpected use of OpEndIT for Arm64";
Matteo Franchin43ec8732014-03-31 15:00:14 +010040}
41
42/*
43 * 64-bit 3way compare function.
Matteo Franchine45fb9e2014-05-06 10:10:30 +010044 * cmp xA, xB
Zheng Xu511c8a62014-06-03 16:22:23 +080045 * csinc wC, wzr, wzr, eq // wC = (xA == xB) ? 0 : 1
46 * csneg wC, wC, wC, ge // wC = (xA >= xB) ? wC : -wC
Matteo Franchin43ec8732014-03-31 15:00:14 +010047 */
Matteo Franchine45fb9e2014-05-06 10:10:30 +010048void Arm64Mir2Lir::GenCmpLong(RegLocation rl_dest, RegLocation rl_src1,
49 RegLocation rl_src2) {
50 RegLocation rl_result;
Matteo Franchin43ec8732014-03-31 15:00:14 +010051 rl_src1 = LoadValueWide(rl_src1, kCoreReg);
52 rl_src2 = LoadValueWide(rl_src2, kCoreReg);
Matteo Franchine45fb9e2014-05-06 10:10:30 +010053 rl_result = EvalLoc(rl_dest, kCoreReg, true);
Matteo Franchin43ec8732014-03-31 15:00:14 +010054
Matteo Franchine45fb9e2014-05-06 10:10:30 +010055 OpRegReg(kOpCmp, rl_src1.reg, rl_src2.reg);
Zheng Xu511c8a62014-06-03 16:22:23 +080056 NewLIR4(kA64Csinc4rrrc, rl_result.reg.GetReg(), rwzr, rwzr, kArmCondEq);
57 NewLIR4(kA64Csneg4rrrc, rl_result.reg.GetReg(), rl_result.reg.GetReg(),
58 rl_result.reg.GetReg(), kArmCondGe);
59 StoreValue(rl_dest, rl_result);
Serban Constantinescued65c5e2014-05-22 15:10:18 +010060}
61
62void Arm64Mir2Lir::GenShiftOpLong(Instruction::Code opcode, RegLocation rl_dest,
63 RegLocation rl_src1, RegLocation rl_shift) {
64 OpKind op = kOpBkpt;
65 switch (opcode) {
66 case Instruction::SHL_LONG:
67 case Instruction::SHL_LONG_2ADDR:
68 op = kOpLsl;
69 break;
70 case Instruction::SHR_LONG:
71 case Instruction::SHR_LONG_2ADDR:
72 op = kOpAsr;
73 break;
74 case Instruction::USHR_LONG:
75 case Instruction::USHR_LONG_2ADDR:
76 op = kOpLsr;
77 break;
78 default:
79 LOG(FATAL) << "Unexpected case: " << opcode;
80 }
Zheng Xue2eb29e2014-06-12 10:22:33 +080081 rl_shift = LoadValue(rl_shift, kCoreReg);
Serban Constantinescued65c5e2014-05-22 15:10:18 +010082 rl_src1 = LoadValueWide(rl_src1, kCoreReg);
83 RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true);
Zheng Xue2eb29e2014-06-12 10:22:33 +080084 OpRegRegReg(op, rl_result.reg, rl_src1.reg, As64BitReg(rl_shift.reg));
Serban Constantinescued65c5e2014-05-22 15:10:18 +010085 StoreValueWide(rl_dest, rl_result);
Matteo Franchin43ec8732014-03-31 15:00:14 +010086}
87
Matteo Franchin43ec8732014-03-31 15:00:14 +010088void Arm64Mir2Lir::GenSelect(BasicBlock* bb, MIR* mir) {
89 RegLocation rl_result;
90 RegLocation rl_src = mir_graph_->GetSrc(mir, 0);
91 RegLocation rl_dest = mir_graph_->GetDest(mir);
buzbeea0cd2d72014-06-01 09:33:49 -070092 RegisterClass src_reg_class = rl_src.ref ? kRefReg : kCoreReg;
93 RegisterClass result_reg_class = rl_dest.ref ? kRefReg : kCoreReg;
Andreas Gampe381f8ac2014-07-10 03:23:41 -070094
buzbeea0cd2d72014-06-01 09:33:49 -070095 rl_src = LoadValue(rl_src, src_reg_class);
Andreas Gampe381f8ac2014-07-10 03:23:41 -070096 // rl_src may be aliased with rl_result/rl_dest, so do compare early.
97 OpRegImm(kOpCmp, rl_src.reg, 0);
98
Serban Constantinescu05e27ff2014-05-28 13:21:45 +010099 ArmConditionCode code = ArmConditionEncoding(mir->meta.ccode);
100
Andreas Gampe381f8ac2014-07-10 03:23:41 -0700101 // The kMirOpSelect has two variants, one for constants and one for moves.
102 bool is_wide = rl_dest.ref || rl_dest.wide;
103
104 if (mir->ssa_rep->num_uses == 1) {
105 uint32_t true_val = mir->dalvikInsn.vB;
106 uint32_t false_val = mir->dalvikInsn.vC;
107
108 int opcode; // The opcode.
109 int left_op, right_op; // The operands.
110 bool rl_result_evaled = false;
111
112 // Check some simple cases.
113 // TODO: Improve this.
114 int zero_reg = (is_wide ? rs_xzr : rs_wzr).GetReg();
115
116 if ((true_val == 0 && false_val == 1) || (true_val == 1 && false_val == 0)) {
117 // CSInc cheap based on wzr.
118 if (true_val == 1) {
119 // Negate.
120 code = ArmConditionEncoding(NegateComparison(mir->meta.ccode));
121 }
122
123 left_op = right_op = zero_reg;
124 opcode = is_wide ? WIDE(kA64Csinc4rrrc) : kA64Csinc4rrrc;
125 } else if ((true_val == 0 && false_val == 0xFFFFFFFF) ||
126 (true_val == 0xFFFFFFFF && false_val == 0)) {
127 // CSneg cheap based on wzr.
128 if (true_val == 0xFFFFFFFF) {
129 // Negate.
130 code = ArmConditionEncoding(NegateComparison(mir->meta.ccode));
131 }
132
133 left_op = right_op = zero_reg;
Stuart Monteith873c3712014-07-11 16:31:28 +0100134 opcode = is_wide ? WIDE(kA64Csinv4rrrc) : kA64Csinv4rrrc;
Andreas Gampe381f8ac2014-07-10 03:23:41 -0700135 } else if (true_val == 0 || false_val == 0) {
136 // Csel half cheap based on wzr.
137 rl_result = EvalLoc(rl_dest, result_reg_class, true);
138 rl_result_evaled = true;
139 if (false_val == 0) {
140 // Negate.
141 code = ArmConditionEncoding(NegateComparison(mir->meta.ccode));
142 }
143 LoadConstantNoClobber(rl_result.reg, true_val == 0 ? false_val : true_val);
144 left_op = zero_reg;
145 right_op = rl_result.reg.GetReg();
146 opcode = is_wide ? WIDE(kA64Csel4rrrc) : kA64Csel4rrrc;
147 } else if (true_val == 1 || false_val == 1) {
148 // CSInc half cheap based on wzr.
149 rl_result = EvalLoc(rl_dest, result_reg_class, true);
150 rl_result_evaled = true;
151 if (true_val == 1) {
152 // Negate.
153 code = ArmConditionEncoding(NegateComparison(mir->meta.ccode));
154 }
155 LoadConstantNoClobber(rl_result.reg, true_val == 1 ? false_val : true_val);
156 left_op = rl_result.reg.GetReg();
157 right_op = zero_reg;
158 opcode = is_wide ? WIDE(kA64Csinc4rrrc) : kA64Csinc4rrrc;
159 } else if (true_val == 0xFFFFFFFF || false_val == 0xFFFFFFFF) {
160 // CSneg half cheap based on wzr.
161 rl_result = EvalLoc(rl_dest, result_reg_class, true);
162 rl_result_evaled = true;
163 if (true_val == 0xFFFFFFFF) {
164 // Negate.
165 code = ArmConditionEncoding(NegateComparison(mir->meta.ccode));
166 }
167 LoadConstantNoClobber(rl_result.reg, true_val == 0xFFFFFFFF ? false_val : true_val);
168 left_op = rl_result.reg.GetReg();
169 right_op = zero_reg;
Stuart Monteith873c3712014-07-11 16:31:28 +0100170 opcode = is_wide ? WIDE(kA64Csinv4rrrc) : kA64Csinv4rrrc;
Andreas Gampe381f8ac2014-07-10 03:23:41 -0700171 } else {
172 // Csel. The rest. Use rl_result and a temp.
173 // TODO: To minimize the constants being loaded, check whether one can be inexpensively
174 // loaded as n - 1 or ~n.
175 rl_result = EvalLoc(rl_dest, result_reg_class, true);
176 rl_result_evaled = true;
177 LoadConstantNoClobber(rl_result.reg, true_val);
178 RegStorage t_reg2 = AllocTypedTemp(false, result_reg_class);
179 if (rl_dest.wide) {
180 if (t_reg2.Is32Bit()) {
181 t_reg2 = As64BitReg(t_reg2);
182 }
183 }
184 LoadConstantNoClobber(t_reg2, false_val);
185
186 // Use csel.
187 left_op = rl_result.reg.GetReg();
188 right_op = t_reg2.GetReg();
189 opcode = is_wide ? WIDE(kA64Csel4rrrc) : kA64Csel4rrrc;
190 }
191
192 if (!rl_result_evaled) {
193 rl_result = EvalLoc(rl_dest, result_reg_class, true);
194 }
195
196 NewLIR4(opcode, rl_result.reg.GetReg(), left_op, right_op, code);
197 } else {
198 RegLocation rl_true = mir_graph_->reg_location_[mir->ssa_rep->uses[1]];
199 RegLocation rl_false = mir_graph_->reg_location_[mir->ssa_rep->uses[2]];
200
201 rl_true = LoadValue(rl_true, result_reg_class);
202 rl_false = LoadValue(rl_false, result_reg_class);
203 rl_result = EvalLoc(rl_dest, result_reg_class, true);
204
205 int opcode = is_wide ? WIDE(kA64Csel4rrrc) : kA64Csel4rrrc;
206 NewLIR4(opcode, rl_result.reg.GetReg(),
207 rl_true.reg.GetReg(), rl_false.reg.GetReg(), code);
208 }
Matteo Franchin43ec8732014-03-31 15:00:14 +0100209 StoreValue(rl_dest, rl_result);
210}
211
212void Arm64Mir2Lir::GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir) {
213 RegLocation rl_src1 = mir_graph_->GetSrcWide(mir, 0);
214 RegLocation rl_src2 = mir_graph_->GetSrcWide(mir, 2);
Serban Constantinescu05e27ff2014-05-28 13:21:45 +0100215 LIR* taken = &block_label_list_[bb->taken];
216 LIR* not_taken = &block_label_list_[bb->fall_through];
Matteo Franchin43ec8732014-03-31 15:00:14 +0100217 // Normalize such that if either operand is constant, src2 will be constant.
218 ConditionCode ccode = mir->meta.ccode;
219 if (rl_src1.is_const) {
220 std::swap(rl_src1, rl_src2);
221 ccode = FlipComparisonOrder(ccode);
222 }
Serban Constantinescu05e27ff2014-05-28 13:21:45 +0100223
Andreas Gampe381f8ac2014-07-10 03:23:41 -0700224 rl_src1 = LoadValueWide(rl_src1, kCoreReg);
225
Matteo Franchin43ec8732014-03-31 15:00:14 +0100226 if (rl_src2.is_const) {
Andreas Gampe381f8ac2014-07-10 03:23:41 -0700227 // TODO: Optimize for rl_src1.is_const? (Does happen in the boot image at the moment.)
228
Matteo Franchin43ec8732014-03-31 15:00:14 +0100229 int64_t val = mir_graph_->ConstantValueWide(rl_src2);
Serban Constantinescu05e27ff2014-05-28 13:21:45 +0100230 // Special handling using cbz & cbnz.
231 if (val == 0 && (ccode == kCondEq || ccode == kCondNe)) {
232 OpCmpImmBranch(ccode, rl_src1.reg, 0, taken);
233 OpCmpImmBranch(NegateComparison(ccode), rl_src1.reg, 0, not_taken);
234 return;
Andreas Gampe381f8ac2014-07-10 03:23:41 -0700235 }
236
Serban Constantinescu05e27ff2014-05-28 13:21:45 +0100237 // Only handle Imm if src2 is not already in a register.
Andreas Gampe381f8ac2014-07-10 03:23:41 -0700238 rl_src2 = UpdateLocWide(rl_src2);
239 if (rl_src2.location != kLocPhysReg) {
Serban Constantinescu05e27ff2014-05-28 13:21:45 +0100240 OpRegImm64(kOpCmp, rl_src1.reg, val);
241 OpCondBranch(ccode, taken);
242 OpCondBranch(NegateComparison(ccode), not_taken);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100243 return;
244 }
245 }
Serban Constantinescu05e27ff2014-05-28 13:21:45 +0100246
Matteo Franchin43ec8732014-03-31 15:00:14 +0100247 rl_src2 = LoadValueWide(rl_src2, kCoreReg);
Serban Constantinescu05e27ff2014-05-28 13:21:45 +0100248 OpRegReg(kOpCmp, rl_src1.reg, rl_src2.reg);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100249 OpCondBranch(ccode, taken);
Serban Constantinescu05e27ff2014-05-28 13:21:45 +0100250 OpCondBranch(NegateComparison(ccode), not_taken);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100251}
252
253/*
254 * Generate a register comparison to an immediate and branch. Caller
255 * is responsible for setting branch target field.
256 */
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100257LIR* Arm64Mir2Lir::OpCmpImmBranch(ConditionCode cond, RegStorage reg, int check_value,
258 LIR* target) {
Matteo Franchin43ec8732014-03-31 15:00:14 +0100259 LIR* branch;
260 ArmConditionCode arm_cond = ArmConditionEncoding(cond);
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100261 if (check_value == 0 && (arm_cond == kArmCondEq || arm_cond == kArmCondNe)) {
262 ArmOpcode opcode = (arm_cond == kArmCondEq) ? kA64Cbz2rt : kA64Cbnz2rt;
Serban Constantinescued65c5e2014-05-22 15:10:18 +0100263 ArmOpcode wide = reg.Is64Bit() ? WIDE(0) : UNWIDE(0);
264 branch = NewLIR2(opcode | wide, reg.GetReg(), 0);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100265 } else {
266 OpRegImm(kOpCmp, reg, check_value);
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100267 branch = NewLIR2(kA64B2ct, arm_cond, 0);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100268 }
269 branch->target = target;
270 return branch;
271}
272
Zheng Xu7c1c2632014-06-17 18:17:31 +0800273LIR* Arm64Mir2Lir::OpCmpMemImmBranch(ConditionCode cond, RegStorage temp_reg,
274 RegStorage base_reg, int offset, int check_value,
Nicolas Geoffray0025a862014-07-11 08:26:40 +0000275 LIR* target) {
Zheng Xu7c1c2632014-06-17 18:17:31 +0800276 // It is possible that temp register is 64-bit. (ArgReg or RefReg)
277 // Always compare 32-bit value no matter what temp_reg is.
278 if (temp_reg.Is64Bit()) {
279 temp_reg = As32BitReg(temp_reg);
280 }
281 Load32Disp(base_reg, offset, temp_reg);
282 LIR* branch = OpCmpImmBranch(cond, temp_reg, check_value, target);
283 return branch;
284}
285
Matteo Franchin43ec8732014-03-31 15:00:14 +0100286LIR* Arm64Mir2Lir::OpRegCopyNoInsert(RegStorage r_dest, RegStorage r_src) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100287 bool dest_is_fp = r_dest.IsFloat();
288 bool src_is_fp = r_src.IsFloat();
289 ArmOpcode opcode = kA64Brk1d;
Matteo Franchin43ec8732014-03-31 15:00:14 +0100290 LIR* res;
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100291
292 if (LIKELY(dest_is_fp == src_is_fp)) {
293 if (LIKELY(!dest_is_fp)) {
Andreas Gampe4b537a82014-06-30 22:24:53 -0700294 DCHECK_EQ(r_dest.Is64Bit(), r_src.Is64Bit());
295
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100296 // Core/core copy.
297 // Copies involving the sp register require a different instruction.
298 opcode = UNLIKELY(A64_REG_IS_SP(r_dest.GetReg())) ? kA64Add4RRdT : kA64Mov2rr;
299
300 // TODO(Arm64): kA64Add4RRdT formally has 4 args, but is used as a 2 args instruction.
301 // This currently works because the other arguments are set to 0 by default. We should
302 // rather introduce an alias kA64Mov2RR.
303
304 // core/core copy. Do a x/x copy only if both registers are x.
305 if (r_dest.Is64Bit() && r_src.Is64Bit()) {
306 opcode = WIDE(opcode);
307 }
308 } else {
309 // Float/float copy.
310 bool dest_is_double = r_dest.IsDouble();
311 bool src_is_double = r_src.IsDouble();
312
313 // We do not do float/double or double/float casts here.
314 DCHECK_EQ(dest_is_double, src_is_double);
315
316 // Homogeneous float/float copy.
317 opcode = (dest_is_double) ? FWIDE(kA64Fmov2ff) : kA64Fmov2ff;
318 }
319 } else {
320 // Inhomogeneous register copy.
321 if (dest_is_fp) {
322 if (r_dest.IsDouble()) {
323 opcode = kA64Fmov2Sx;
324 } else {
Andreas Gampe4b537a82014-06-30 22:24:53 -0700325 r_src = Check32BitReg(r_src);
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100326 opcode = kA64Fmov2sw;
327 }
328 } else {
329 if (r_src.IsDouble()) {
330 opcode = kA64Fmov2xS;
331 } else {
Andreas Gampe4b537a82014-06-30 22:24:53 -0700332 r_dest = Check32BitReg(r_dest);
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100333 opcode = kA64Fmov2ws;
334 }
335 }
Matteo Franchin43ec8732014-03-31 15:00:14 +0100336 }
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100337
Matteo Franchin43ec8732014-03-31 15:00:14 +0100338 res = RawLIR(current_dalvik_offset_, opcode, r_dest.GetReg(), r_src.GetReg());
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100339
Matteo Franchin43ec8732014-03-31 15:00:14 +0100340 if (!(cu_->disable_opt & (1 << kSafeOptimizations)) && r_dest == r_src) {
341 res->flags.is_nop = true;
342 }
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100343
Matteo Franchin43ec8732014-03-31 15:00:14 +0100344 return res;
345}
346
347void Arm64Mir2Lir::OpRegCopy(RegStorage r_dest, RegStorage r_src) {
348 if (r_dest != r_src) {
349 LIR* res = OpRegCopyNoInsert(r_dest, r_src);
350 AppendLIR(res);
351 }
352}
353
354void Arm64Mir2Lir::OpRegCopyWide(RegStorage r_dest, RegStorage r_src) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100355 OpRegCopy(r_dest, r_src);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100356}
357
358// Table of magic divisors
359struct MagicTable {
360 uint32_t magic;
361 uint32_t shift;
362 DividePattern pattern;
363};
364
365static const MagicTable magic_table[] = {
366 {0, 0, DivideNone}, // 0
367 {0, 0, DivideNone}, // 1
368 {0, 0, DivideNone}, // 2
369 {0x55555556, 0, Divide3}, // 3
370 {0, 0, DivideNone}, // 4
371 {0x66666667, 1, Divide5}, // 5
372 {0x2AAAAAAB, 0, Divide3}, // 6
373 {0x92492493, 2, Divide7}, // 7
374 {0, 0, DivideNone}, // 8
375 {0x38E38E39, 1, Divide5}, // 9
376 {0x66666667, 2, Divide5}, // 10
377 {0x2E8BA2E9, 1, Divide5}, // 11
378 {0x2AAAAAAB, 1, Divide5}, // 12
379 {0x4EC4EC4F, 2, Divide5}, // 13
380 {0x92492493, 3, Divide7}, // 14
381 {0x88888889, 3, Divide7}, // 15
382};
383
384// Integer division by constant via reciprocal multiply (Hacker's Delight, 10-4)
385bool Arm64Mir2Lir::SmallLiteralDivRem(Instruction::Code dalvik_opcode, bool is_div,
Matteo Franchinc61b3c92014-06-18 11:52:47 +0100386 RegLocation rl_src, RegLocation rl_dest, int lit) {
387 if ((lit < 0) || (lit >= static_cast<int>(arraysize(magic_table)))) {
Matteo Franchin43ec8732014-03-31 15:00:14 +0100388 return false;
389 }
390 DividePattern pattern = magic_table[lit].pattern;
391 if (pattern == DivideNone) {
392 return false;
393 }
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100394 // Tuning: add rem patterns
395 if (!is_div) {
396 return false;
397 }
Matteo Franchin43ec8732014-03-31 15:00:14 +0100398
399 RegStorage r_magic = AllocTemp();
400 LoadConstant(r_magic, magic_table[lit].magic);
401 rl_src = LoadValue(rl_src, kCoreReg);
402 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
Matteo Franchinc61b3c92014-06-18 11:52:47 +0100403 RegStorage r_long_mul = AllocTemp();
404 NewLIR4(kA64Smaddl4xwwx, As64BitReg(r_long_mul).GetReg(),
405 r_magic.GetReg(), rl_src.reg.GetReg(), rxzr);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100406 switch (pattern) {
407 case Divide3:
Matteo Franchinc61b3c92014-06-18 11:52:47 +0100408 OpRegRegImm(kOpLsr, As64BitReg(r_long_mul), As64BitReg(r_long_mul), 32);
409 OpRegRegRegShift(kOpSub, rl_result.reg, r_long_mul, rl_src.reg, EncodeShift(kA64Asr, 31));
Matteo Franchin43ec8732014-03-31 15:00:14 +0100410 break;
411 case Divide5:
Matteo Franchinc61b3c92014-06-18 11:52:47 +0100412 OpRegRegImm(kOpAsr, As64BitReg(r_long_mul), As64BitReg(r_long_mul),
413 32 + magic_table[lit].shift);
414 OpRegRegRegShift(kOpSub, rl_result.reg, r_long_mul, rl_src.reg, EncodeShift(kA64Asr, 31));
Matteo Franchin43ec8732014-03-31 15:00:14 +0100415 break;
416 case Divide7:
Matteo Franchinc61b3c92014-06-18 11:52:47 +0100417 OpRegRegRegShift(kOpAdd, As64BitReg(r_long_mul), As64BitReg(rl_src.reg),
418 As64BitReg(r_long_mul), EncodeShift(kA64Lsr, 32));
419 OpRegRegImm(kOpAsr, r_long_mul, r_long_mul, magic_table[lit].shift);
420 OpRegRegRegShift(kOpSub, rl_result.reg, r_long_mul, rl_src.reg, EncodeShift(kA64Asr, 31));
Matteo Franchin43ec8732014-03-31 15:00:14 +0100421 break;
422 default:
423 LOG(FATAL) << "Unexpected pattern: " << pattern;
424 }
Matteo Franchin43ec8732014-03-31 15:00:14 +0100425 StoreValue(rl_dest, rl_result);
426 return true;
427}
428
Matteo Franchinc61b3c92014-06-18 11:52:47 +0100429// Returns true if it added instructions to 'cu' to divide 'rl_src' by 'lit'
430// and store the result in 'rl_dest'.
431bool Arm64Mir2Lir::HandleEasyDivRem(Instruction::Code dalvik_opcode, bool is_div,
432 RegLocation rl_src, RegLocation rl_dest, int lit) {
433 if (lit < 2) {
434 return false;
435 }
436 if (!IsPowerOfTwo(lit)) {
437 return SmallLiteralDivRem(dalvik_opcode, is_div, rl_src, rl_dest, lit);
438 }
439 int k = LowestSetBit(lit);
440 if (k >= 30) {
441 // Avoid special cases.
442 return false;
443 }
444 rl_src = LoadValue(rl_src, kCoreReg);
445 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
446 if (is_div) {
447 RegStorage t_reg = AllocTemp();
448 if (lit == 2) {
449 // Division by 2 is by far the most common division by constant.
450 OpRegRegRegShift(kOpAdd, t_reg, rl_src.reg, rl_src.reg, EncodeShift(kA64Lsr, 32 - k));
451 OpRegRegImm(kOpAsr, rl_result.reg, t_reg, k);
452 } else {
453 OpRegRegImm(kOpAsr, t_reg, rl_src.reg, 31);
454 OpRegRegRegShift(kOpAdd, t_reg, rl_src.reg, t_reg, EncodeShift(kA64Lsr, 32 - k));
455 OpRegRegImm(kOpAsr, rl_result.reg, t_reg, k);
456 }
457 } else {
458 RegStorage t_reg = AllocTemp();
459 if (lit == 2) {
460 OpRegRegRegShift(kOpAdd, t_reg, rl_src.reg, rl_src.reg, EncodeShift(kA64Lsr, 32 - k));
461 OpRegRegImm(kOpAnd, t_reg, t_reg, lit - 1);
462 OpRegRegRegShift(kOpSub, rl_result.reg, t_reg, rl_src.reg, EncodeShift(kA64Lsr, 32 - k));
463 } else {
464 RegStorage t_reg2 = AllocTemp();
465 OpRegRegImm(kOpAsr, t_reg, rl_src.reg, 31);
466 OpRegRegRegShift(kOpAdd, t_reg2, rl_src.reg, t_reg, EncodeShift(kA64Lsr, 32 - k));
467 OpRegRegImm(kOpAnd, t_reg2, t_reg2, lit - 1);
468 OpRegRegRegShift(kOpSub, rl_result.reg, t_reg2, t_reg, EncodeShift(kA64Lsr, 32 - k));
469 }
470 }
471 StoreValue(rl_dest, rl_result);
472 return true;
473}
474
Matteo Franchin43ec8732014-03-31 15:00:14 +0100475bool Arm64Mir2Lir::EasyMultiply(RegLocation rl_src, RegLocation rl_dest, int lit) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100476 LOG(FATAL) << "Unexpected use of EasyMultiply for Arm64";
477 return false;
Matteo Franchin43ec8732014-03-31 15:00:14 +0100478}
479
480RegLocation Arm64Mir2Lir::GenDivRem(RegLocation rl_dest, RegLocation rl_src1,
Matteo Franchinc61b3c92014-06-18 11:52:47 +0100481 RegLocation rl_src2, bool is_div, bool check_zero) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100482 LOG(FATAL) << "Unexpected use of GenDivRem for Arm64";
Matteo Franchin43ec8732014-03-31 15:00:14 +0100483 return rl_dest;
484}
485
486RegLocation Arm64Mir2Lir::GenDivRemLit(RegLocation rl_dest, RegLocation rl_src1, int lit, bool is_div) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100487 LOG(FATAL) << "Unexpected use of GenDivRemLit for Arm64";
Matteo Franchin43ec8732014-03-31 15:00:14 +0100488 return rl_dest;
489}
490
491RegLocation Arm64Mir2Lir::GenDivRemLit(RegLocation rl_dest, RegStorage reg1, int lit, bool is_div) {
492 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
493
494 // Put the literal in a temp.
495 RegStorage lit_temp = AllocTemp();
496 LoadConstant(lit_temp, lit);
497 // Use the generic case for div/rem with arg2 in a register.
498 // TODO: The literal temp can be freed earlier during a modulus to reduce reg pressure.
499 rl_result = GenDivRem(rl_result, reg1, lit_temp, is_div);
500 FreeTemp(lit_temp);
501
502 return rl_result;
503}
504
Serban Constantinescued65c5e2014-05-22 15:10:18 +0100505RegLocation Arm64Mir2Lir::GenDivRem(RegLocation rl_dest, RegStorage r_src1, RegStorage r_src2,
Matteo Franchin43ec8732014-03-31 15:00:14 +0100506 bool is_div) {
Serban Constantinescued65c5e2014-05-22 15:10:18 +0100507 CHECK_EQ(r_src1.Is64Bit(), r_src2.Is64Bit());
508
Matteo Franchin43ec8732014-03-31 15:00:14 +0100509 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
510 if (is_div) {
Serban Constantinescued65c5e2014-05-22 15:10:18 +0100511 OpRegRegReg(kOpDiv, rl_result.reg, r_src1, r_src2);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100512 } else {
Serban Constantinescued65c5e2014-05-22 15:10:18 +0100513 // temp = r_src1 / r_src2
514 // dest = r_src1 - temp * r_src2
515 RegStorage temp;
516 ArmOpcode wide;
517 if (rl_result.reg.Is64Bit()) {
518 temp = AllocTempWide();
519 wide = WIDE(0);
520 } else {
521 temp = AllocTemp();
522 wide = UNWIDE(0);
523 }
524 OpRegRegReg(kOpDiv, temp, r_src1, r_src2);
525 NewLIR4(kA64Msub4rrrr | wide, rl_result.reg.GetReg(), temp.GetReg(),
526 r_src1.GetReg(), r_src2.GetReg());
Matteo Franchin43ec8732014-03-31 15:00:14 +0100527 FreeTemp(temp);
528 }
Matteo Franchin43ec8732014-03-31 15:00:14 +0100529 return rl_result;
530}
531
Serban Constantinescu169489b2014-06-11 16:43:35 +0100532bool Arm64Mir2Lir::GenInlinedAbsLong(CallInfo* info) {
533 RegLocation rl_src = info->args[0];
534 rl_src = LoadValueWide(rl_src, kCoreReg);
535 RegLocation rl_dest = InlineTargetWide(info);
536 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
537 RegStorage sign_reg = AllocTempWide();
538 // abs(x) = y<=x>>63, (x+y)^y.
539 OpRegRegImm(kOpAsr, sign_reg, rl_src.reg, 63);
540 OpRegRegReg(kOpAdd, rl_result.reg, rl_src.reg, sign_reg);
541 OpRegReg(kOpXor, rl_result.reg, sign_reg);
542 StoreValueWide(rl_dest, rl_result);
543 return true;
544}
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100545
Serban Constantinescu23abec92014-07-02 16:13:38 +0100546bool Arm64Mir2Lir::GenInlinedMinMax(CallInfo* info, bool is_min, bool is_long) {
Serban Constantinescu169489b2014-06-11 16:43:35 +0100547 DCHECK_EQ(cu_->instruction_set, kArm64);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100548 RegLocation rl_src1 = info->args[0];
Serban Constantinescu23abec92014-07-02 16:13:38 +0100549 RegLocation rl_src2 = (is_long) ? info->args[2] : info->args[1];
550 rl_src1 = (is_long) ? LoadValueWide(rl_src1, kCoreReg) : LoadValue(rl_src1, kCoreReg);
551 rl_src2 = (is_long) ? LoadValueWide(rl_src2, kCoreReg) : LoadValue(rl_src2, kCoreReg);
552 RegLocation rl_dest = (is_long) ? InlineTargetWide(info) : InlineTarget(info);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100553 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
554 OpRegReg(kOpCmp, rl_src1.reg, rl_src2.reg);
Serban Constantinescu23abec92014-07-02 16:13:38 +0100555 NewLIR4((is_long) ? WIDE(kA64Csel4rrrc) : kA64Csel4rrrc, rl_result.reg.GetReg(),
556 rl_src1.reg.GetReg(), rl_src2.reg.GetReg(), (is_min) ? kArmCondLt : kArmCondGt);
557 (is_long) ? StoreValueWide(rl_dest, rl_result) :StoreValue(rl_dest, rl_result);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100558 return true;
559}
560
561bool Arm64Mir2Lir::GenInlinedPeek(CallInfo* info, OpSize size) {
562 RegLocation rl_src_address = info->args[0]; // long address
Serban Constantinescu63fe93d2014-06-30 17:10:28 +0100563 RegLocation rl_dest = (size == k64) ? InlineTargetWide(info) : InlineTarget(info);
564 RegLocation rl_address = LoadValueWide(rl_src_address, kCoreReg);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100565 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
Serban Constantinescu169489b2014-06-11 16:43:35 +0100566
Andreas Gampe3c12c512014-06-24 18:46:29 +0000567 LoadBaseDisp(rl_address.reg, 0, rl_result.reg, size, kNotVolatile);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100568 if (size == k64) {
Matteo Franchin43ec8732014-03-31 15:00:14 +0100569 StoreValueWide(rl_dest, rl_result);
570 } else {
571 DCHECK(size == kSignedByte || size == kSignedHalf || size == k32);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100572 StoreValue(rl_dest, rl_result);
573 }
574 return true;
575}
576
577bool Arm64Mir2Lir::GenInlinedPoke(CallInfo* info, OpSize size) {
578 RegLocation rl_src_address = info->args[0]; // long address
Matteo Franchin43ec8732014-03-31 15:00:14 +0100579 RegLocation rl_src_value = info->args[2]; // [size] value
Serban Constantinescu63fe93d2014-06-30 17:10:28 +0100580 RegLocation rl_address = LoadValueWide(rl_src_address, kCoreReg);
Serban Constantinescu169489b2014-06-11 16:43:35 +0100581
582 RegLocation rl_value;
Matteo Franchin43ec8732014-03-31 15:00:14 +0100583 if (size == k64) {
Serban Constantinescu169489b2014-06-11 16:43:35 +0100584 rl_value = LoadValueWide(rl_src_value, kCoreReg);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100585 } else {
586 DCHECK(size == kSignedByte || size == kSignedHalf || size == k32);
Serban Constantinescu169489b2014-06-11 16:43:35 +0100587 rl_value = LoadValue(rl_src_value, kCoreReg);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100588 }
Andreas Gampe3c12c512014-06-24 18:46:29 +0000589 StoreBaseDisp(rl_address.reg, 0, rl_value.reg, size, kNotVolatile);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100590 return true;
591}
592
593void Arm64Mir2Lir::OpLea(RegStorage r_base, RegStorage reg1, RegStorage reg2, int scale, int offset) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100594 LOG(FATAL) << "Unexpected use of OpLea for Arm64";
Matteo Franchin43ec8732014-03-31 15:00:14 +0100595}
596
Andreas Gampe2f244e92014-05-08 03:35:25 -0700597void Arm64Mir2Lir::OpTlsCmp(ThreadOffset<4> offset, int val) {
598 UNIMPLEMENTED(FATAL) << "Should not be used.";
599}
600
601void Arm64Mir2Lir::OpTlsCmp(ThreadOffset<8> offset, int val) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100602 LOG(FATAL) << "Unexpected use of OpTlsCmp for Arm64";
Matteo Franchin43ec8732014-03-31 15:00:14 +0100603}
604
605bool Arm64Mir2Lir::GenInlinedCas(CallInfo* info, bool is_long, bool is_object) {
Serban Constantinescu169489b2014-06-11 16:43:35 +0100606 DCHECK_EQ(cu_->instruction_set, kArm64);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100607 // Unused - RegLocation rl_src_unsafe = info->args[0];
608 RegLocation rl_src_obj = info->args[1]; // Object - known non-null
609 RegLocation rl_src_offset = info->args[2]; // long low
Matteo Franchin43ec8732014-03-31 15:00:14 +0100610 RegLocation rl_src_expected = info->args[4]; // int, long or Object
611 // If is_long, high half is in info->args[5]
612 RegLocation rl_src_new_value = info->args[is_long ? 6 : 5]; // int, long or Object
613 // If is_long, high half is in info->args[7]
614 RegLocation rl_dest = InlineTarget(info); // boolean place for result
615
Serban Constantinescu169489b2014-06-11 16:43:35 +0100616 // Load Object and offset
buzbeea0cd2d72014-06-01 09:33:49 -0700617 RegLocation rl_object = LoadValue(rl_src_obj, kRefReg);
Serban Constantinescu63fe93d2014-06-30 17:10:28 +0100618 RegLocation rl_offset = LoadValueWide(rl_src_offset, kCoreReg);
Serban Constantinescu169489b2014-06-11 16:43:35 +0100619
Matteo Franchin43ec8732014-03-31 15:00:14 +0100620 RegLocation rl_new_value;
Serban Constantinescu169489b2014-06-11 16:43:35 +0100621 RegLocation rl_expected;
622 if (is_long) {
Matteo Franchin43ec8732014-03-31 15:00:14 +0100623 rl_new_value = LoadValueWide(rl_src_new_value, kCoreReg);
Serban Constantinescu169489b2014-06-11 16:43:35 +0100624 rl_expected = LoadValueWide(rl_src_expected, kCoreReg);
625 } else {
626 rl_new_value = LoadValue(rl_src_new_value, is_object ? kRefReg : kCoreReg);
627 rl_expected = LoadValue(rl_src_expected, is_object ? kRefReg : kCoreReg);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100628 }
629
630 if (is_object && !mir_graph_->IsConstantNullRef(rl_new_value)) {
631 // Mark card for object assuming new value is stored.
632 MarkGCCard(rl_new_value.reg, rl_object.reg);
633 }
634
Serban Constantinescu169489b2014-06-11 16:43:35 +0100635 RegStorage r_ptr = AllocTempRef();
Matteo Franchin43ec8732014-03-31 15:00:14 +0100636 OpRegRegReg(kOpAdd, r_ptr, rl_object.reg, rl_offset.reg);
637
638 // Free now unneeded rl_object and rl_offset to give more temps.
639 ClobberSReg(rl_object.s_reg_low);
640 FreeTemp(rl_object.reg);
641 ClobberSReg(rl_offset.s_reg_low);
642 FreeTemp(rl_offset.reg);
643
Matteo Franchin43ec8732014-03-31 15:00:14 +0100644 // do {
645 // tmp = [r_ptr] - expected;
646 // } while (tmp == 0 && failure([r_ptr] <- r_new_value));
647 // result = tmp != 0;
648
Serban Constantinescu169489b2014-06-11 16:43:35 +0100649 RegStorage r_tmp;
Serban Constantinescu63fe93d2014-06-30 17:10:28 +0100650 RegStorage r_tmp_stored;
651 RegStorage rl_new_value_stored = rl_new_value.reg;
652 ArmOpcode wide = UNWIDE(0);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100653 if (is_long) {
Serban Constantinescu63fe93d2014-06-30 17:10:28 +0100654 r_tmp_stored = r_tmp = AllocTempWide();
655 wide = WIDE(0);
Serban Constantinescu169489b2014-06-11 16:43:35 +0100656 } else if (is_object) {
Serban Constantinescu63fe93d2014-06-30 17:10:28 +0100657 // References use 64-bit registers, but are stored as compressed 32-bit values.
658 // This means r_tmp_stored != r_tmp.
Serban Constantinescu169489b2014-06-11 16:43:35 +0100659 r_tmp = AllocTempRef();
Serban Constantinescu63fe93d2014-06-30 17:10:28 +0100660 r_tmp_stored = As32BitReg(r_tmp);
661 rl_new_value_stored = As32BitReg(rl_new_value_stored);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100662 } else {
Serban Constantinescu63fe93d2014-06-30 17:10:28 +0100663 r_tmp_stored = r_tmp = AllocTemp();
Matteo Franchin43ec8732014-03-31 15:00:14 +0100664 }
665
Serban Constantinescu63fe93d2014-06-30 17:10:28 +0100666 RegStorage r_tmp32 = (r_tmp.Is32Bit()) ? r_tmp : As32BitReg(r_tmp);
Serban Constantinescu169489b2014-06-11 16:43:35 +0100667 LIR* loop = NewLIR0(kPseudoTargetLabel);
Serban Constantinescu63fe93d2014-06-30 17:10:28 +0100668 NewLIR2(kA64Ldaxr2rX | wide, r_tmp_stored.GetReg(), r_ptr.GetReg());
Serban Constantinescu169489b2014-06-11 16:43:35 +0100669 OpRegReg(kOpCmp, r_tmp, rl_expected.reg);
Vladimir Marko8dea81c2014-06-06 14:50:36 +0100670 DCHECK(last_lir_insn_->u.m.def_mask->HasBit(ResourceMask::kCCode));
Serban Constantinescu169489b2014-06-11 16:43:35 +0100671 LIR* early_exit = OpCondBranch(kCondNe, NULL);
Serban Constantinescu63fe93d2014-06-30 17:10:28 +0100672 NewLIR3(kA64Stlxr3wrX | wide, r_tmp32.GetReg(), rl_new_value_stored.GetReg(), r_ptr.GetReg());
673 NewLIR3(kA64Cmp3RdT, r_tmp32.GetReg(), 0, ENCODE_NO_SHIFT);
Serban Constantinescu169489b2014-06-11 16:43:35 +0100674 DCHECK(last_lir_insn_->u.m.def_mask->HasBit(ResourceMask::kCCode));
675 OpCondBranch(kCondNe, loop);
676
Serban Constantinescu63fe93d2014-06-30 17:10:28 +0100677 LIR* exit_loop = NewLIR0(kPseudoTargetLabel);
678 early_exit->target = exit_loop;
679
Serban Constantinescu169489b2014-06-11 16:43:35 +0100680 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
Serban Constantinescu63fe93d2014-06-30 17:10:28 +0100681 NewLIR4(kA64Csinc4rrrc, rl_result.reg.GetReg(), rwzr, rwzr, kArmCondNe);
Serban Constantinescu169489b2014-06-11 16:43:35 +0100682
Matteo Franchin43ec8732014-03-31 15:00:14 +0100683 FreeTemp(r_tmp); // Now unneeded.
Serban Constantinescu169489b2014-06-11 16:43:35 +0100684 FreeTemp(r_ptr); // Now unneeded.
Matteo Franchin43ec8732014-03-31 15:00:14 +0100685
686 StoreValue(rl_dest, rl_result);
687
Matteo Franchin43ec8732014-03-31 15:00:14 +0100688 return true;
689}
690
691LIR* Arm64Mir2Lir::OpPcRelLoad(RegStorage reg, LIR* target) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100692 return RawLIR(current_dalvik_offset_, WIDE(kA64Ldr2rp), reg.GetReg(), 0, 0, 0, 0, target);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100693}
694
695LIR* Arm64Mir2Lir::OpVldm(RegStorage r_base, int count) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100696 LOG(FATAL) << "Unexpected use of OpVldm for Arm64";
697 return NULL;
Matteo Franchin43ec8732014-03-31 15:00:14 +0100698}
699
700LIR* Arm64Mir2Lir::OpVstm(RegStorage r_base, int count) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100701 LOG(FATAL) << "Unexpected use of OpVstm for Arm64";
702 return NULL;
Matteo Franchin43ec8732014-03-31 15:00:14 +0100703}
704
705void Arm64Mir2Lir::GenMultiplyByTwoBitMultiplier(RegLocation rl_src,
706 RegLocation rl_result, int lit,
707 int first_bit, int second_bit) {
Serban Constantinescued65c5e2014-05-22 15:10:18 +0100708 OpRegRegRegShift(kOpAdd, rl_result.reg, rl_src.reg, rl_src.reg, EncodeShift(kA64Lsl, second_bit - first_bit));
Matteo Franchin43ec8732014-03-31 15:00:14 +0100709 if (first_bit != 0) {
710 OpRegRegImm(kOpLsl, rl_result.reg, rl_result.reg, first_bit);
711 }
712}
713
714void Arm64Mir2Lir::GenDivZeroCheckWide(RegStorage reg) {
Serban Constantinescued65c5e2014-05-22 15:10:18 +0100715 LOG(FATAL) << "Unexpected use of GenDivZero for Arm64";
Matteo Franchin43ec8732014-03-31 15:00:14 +0100716}
717
718// Test suspend flag, return target of taken suspend branch
719LIR* Arm64Mir2Lir::OpTestSuspend(LIR* target) {
Zheng Xubaa7c882014-06-30 14:26:50 +0800720 NewLIR3(kA64Subs3rRd, rwSUSPEND, rwSUSPEND, 1);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100721 return OpCondBranch((target == NULL) ? kCondEq : kCondNe, target);
722}
723
724// Decrement register and branch on condition
725LIR* Arm64Mir2Lir::OpDecAndBranch(ConditionCode c_code, RegStorage reg, LIR* target) {
buzbee33ae5582014-06-12 14:56:32 -0700726 // Combine sub & test using sub setflags encoding here. We need to make sure a
727 // subtract form that sets carry is used, so generate explicitly.
728 // TODO: might be best to add a new op, kOpSubs, and handle it generically.
729 ArmOpcode opcode = reg.Is64Bit() ? WIDE(kA64Subs3rRd) : UNWIDE(kA64Subs3rRd);
730 NewLIR3(opcode, reg.GetReg(), reg.GetReg(), 1); // For value == 1, this should set flags.
Vladimir Marko8dea81c2014-06-06 14:50:36 +0100731 DCHECK(last_lir_insn_->u.m.def_mask->HasBit(ResourceMask::kCCode));
Matteo Franchin43ec8732014-03-31 15:00:14 +0100732 return OpCondBranch(c_code, target);
733}
734
Andreas Gampeb14329f2014-05-15 11:16:06 -0700735bool Arm64Mir2Lir::GenMemBarrier(MemBarrierKind barrier_kind) {
Matteo Franchin43ec8732014-03-31 15:00:14 +0100736#if ANDROID_SMP != 0
737 // Start off with using the last LIR as the barrier. If it is not enough, then we will generate one.
738 LIR* barrier = last_lir_insn_;
739
740 int dmb_flavor;
741 // TODO: revisit Arm barrier kinds
742 switch (barrier_kind) {
Hans Boehm48f5c472014-06-27 14:50:10 -0700743 case kAnyStore: dmb_flavor = kISH; break;
744 case kLoadAny: dmb_flavor = kISH; break;
745 // We conjecture that kISHLD is insufficient. It is documented
746 // to provide LoadLoad | StoreStore ordering. But if this were used
747 // to implement volatile loads, we suspect that the lack of store
748 // atomicity on ARM would cause us to allow incorrect results for
749 // the canonical IRIW example. But we're not sure.
750 // We should be using acquire loads instead.
Matteo Franchin43ec8732014-03-31 15:00:14 +0100751 case kStoreStore: dmb_flavor = kISHST; break;
Hans Boehm48f5c472014-06-27 14:50:10 -0700752 case kAnyAny: dmb_flavor = kISH; break;
Matteo Franchin43ec8732014-03-31 15:00:14 +0100753 default:
754 LOG(FATAL) << "Unexpected MemBarrierKind: " << barrier_kind;
755 dmb_flavor = kSY; // quiet gcc.
756 break;
757 }
758
Andreas Gampeb14329f2014-05-15 11:16:06 -0700759 bool ret = false;
760
Matteo Franchin43ec8732014-03-31 15:00:14 +0100761 // If the same barrier already exists, don't generate another.
762 if (barrier == nullptr
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100763 || (barrier->opcode != kA64Dmb1B || barrier->operands[0] != dmb_flavor)) {
764 barrier = NewLIR1(kA64Dmb1B, dmb_flavor);
Andreas Gampeb14329f2014-05-15 11:16:06 -0700765 ret = true;
Matteo Franchin43ec8732014-03-31 15:00:14 +0100766 }
767
768 // At this point we must have a memory barrier. Mark it as a scheduling barrier as well.
769 DCHECK(!barrier->flags.use_def_invalid);
Vladimir Marko8dea81c2014-06-06 14:50:36 +0100770 barrier->u.m.def_mask = &kEncodeAll;
Andreas Gampeb14329f2014-05-15 11:16:06 -0700771 return ret;
772#else
773 return false;
Matteo Franchin43ec8732014-03-31 15:00:14 +0100774#endif
775}
776
Serban Constantinescued65c5e2014-05-22 15:10:18 +0100777void Arm64Mir2Lir::GenIntToLong(RegLocation rl_dest, RegLocation rl_src) {
778 RegLocation rl_result;
779
780 rl_src = LoadValue(rl_src, kCoreReg);
781 rl_result = EvalLocWide(rl_dest, kCoreReg, true);
Andreas Gampe4b537a82014-06-30 22:24:53 -0700782 NewLIR4(WIDE(kA64Sbfm4rrdd), rl_result.reg.GetReg(), As64BitReg(rl_src.reg).GetReg(), 0, 31);
Serban Constantinescued65c5e2014-05-22 15:10:18 +0100783 StoreValueWide(rl_dest, rl_result);
784}
785
786void Arm64Mir2Lir::GenDivRemLong(Instruction::Code opcode, RegLocation rl_dest,
787 RegLocation rl_src1, RegLocation rl_src2, bool is_div) {
788 RegLocation rl_result;
789 rl_src1 = LoadValueWide(rl_src1, kCoreReg);
790 rl_src2 = LoadValueWide(rl_src2, kCoreReg);
791 GenDivZeroCheck(rl_src2.reg);
792 rl_result = GenDivRem(rl_dest, rl_src1.reg, rl_src2.reg, is_div);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100793 StoreValueWide(rl_dest, rl_result);
794}
795
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100796void Arm64Mir2Lir::GenLongOp(OpKind op, RegLocation rl_dest, RegLocation rl_src1,
797 RegLocation rl_src2) {
798 RegLocation rl_result;
Serban Constantinescued65c5e2014-05-22 15:10:18 +0100799
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100800 rl_src1 = LoadValueWide(rl_src1, kCoreReg);
801 rl_src2 = LoadValueWide(rl_src2, kCoreReg);
802 rl_result = EvalLocWide(rl_dest, kCoreReg, true);
Serban Constantinescued65c5e2014-05-22 15:10:18 +0100803 OpRegRegRegShift(op, rl_result.reg, rl_src1.reg, rl_src2.reg, ENCODE_NO_SHIFT);
804 StoreValueWide(rl_dest, rl_result);
805}
806
807void Arm64Mir2Lir::GenNegLong(RegLocation rl_dest, RegLocation rl_src) {
808 RegLocation rl_result;
809
810 rl_src = LoadValueWide(rl_src, kCoreReg);
811 rl_result = EvalLocWide(rl_dest, kCoreReg, true);
812 OpRegRegShift(kOpNeg, rl_result.reg, rl_src.reg, ENCODE_NO_SHIFT);
813 StoreValueWide(rl_dest, rl_result);
814}
815
816void Arm64Mir2Lir::GenNotLong(RegLocation rl_dest, RegLocation rl_src) {
817 RegLocation rl_result;
818
819 rl_src = LoadValueWide(rl_src, kCoreReg);
820 rl_result = EvalLocWide(rl_dest, kCoreReg, true);
821 OpRegRegShift(kOpMvn, rl_result.reg, rl_src.reg, ENCODE_NO_SHIFT);
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100822 StoreValueWide(rl_dest, rl_result);
823}
824
Matteo Franchin43ec8732014-03-31 15:00:14 +0100825void Arm64Mir2Lir::GenMulLong(Instruction::Code opcode, RegLocation rl_dest,
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100826 RegLocation rl_src1, RegLocation rl_src2) {
827 GenLongOp(kOpMul, rl_dest, rl_src1, rl_src2);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100828}
829
830void Arm64Mir2Lir::GenAddLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1,
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100831 RegLocation rl_src2) {
832 GenLongOp(kOpAdd, rl_dest, rl_src1, rl_src2);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100833}
834
835void Arm64Mir2Lir::GenSubLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1,
836 RegLocation rl_src2) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100837 GenLongOp(kOpSub, rl_dest, rl_src1, rl_src2);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100838}
839
840void Arm64Mir2Lir::GenAndLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1,
841 RegLocation rl_src2) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100842 GenLongOp(kOpAnd, rl_dest, rl_src1, rl_src2);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100843}
844
845void Arm64Mir2Lir::GenOrLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1,
846 RegLocation rl_src2) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100847 GenLongOp(kOpOr, rl_dest, rl_src1, rl_src2);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100848}
849
850void Arm64Mir2Lir::GenXorLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1,
851 RegLocation rl_src2) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100852 GenLongOp(kOpXor, rl_dest, rl_src1, rl_src2);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100853}
854
855/*
856 * Generate array load
857 */
858void Arm64Mir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array,
859 RegLocation rl_index, RegLocation rl_dest, int scale) {
860 RegisterClass reg_class = RegClassBySize(size);
861 int len_offset = mirror::Array::LengthOffset().Int32Value();
862 int data_offset;
863 RegLocation rl_result;
864 bool constant_index = rl_index.is_const;
buzbeea0cd2d72014-06-01 09:33:49 -0700865 rl_array = LoadValue(rl_array, kRefReg);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100866 if (!constant_index) {
867 rl_index = LoadValue(rl_index, kCoreReg);
868 }
869
870 if (rl_dest.wide) {
871 data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Int32Value();
872 } else {
873 data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Int32Value();
874 }
875
876 // If index is constant, just fold it into the data offset
877 if (constant_index) {
878 data_offset += mir_graph_->ConstantValue(rl_index) << scale;
879 }
880
881 /* null object? */
882 GenNullCheck(rl_array.reg, opt_flags);
883
884 bool needs_range_check = (!(opt_flags & MIR_IGNORE_RANGE_CHECK));
885 RegStorage reg_len;
886 if (needs_range_check) {
887 reg_len = AllocTemp();
888 /* Get len */
889 Load32Disp(rl_array.reg, len_offset, reg_len);
890 MarkPossibleNullPointerException(opt_flags);
891 } else {
892 ForceImplicitNullCheck(rl_array.reg, opt_flags);
893 }
894 if (rl_dest.wide || rl_dest.fp || constant_index) {
895 RegStorage reg_ptr;
896 if (constant_index) {
897 reg_ptr = rl_array.reg; // NOTE: must not alter reg_ptr in constant case.
898 } else {
899 // No special indexed operation, lea + load w/ displacement
buzbeea0cd2d72014-06-01 09:33:49 -0700900 reg_ptr = AllocTempRef();
buzbee33ae5582014-06-12 14:56:32 -0700901 OpRegRegRegShift(kOpAdd, reg_ptr, rl_array.reg, As64BitReg(rl_index.reg),
902 EncodeShift(kA64Lsl, scale));
Matteo Franchin43ec8732014-03-31 15:00:14 +0100903 FreeTemp(rl_index.reg);
904 }
905 rl_result = EvalLoc(rl_dest, reg_class, true);
906
907 if (needs_range_check) {
908 if (constant_index) {
909 GenArrayBoundsCheck(mir_graph_->ConstantValue(rl_index), reg_len);
910 } else {
911 GenArrayBoundsCheck(rl_index.reg, reg_len);
912 }
913 FreeTemp(reg_len);
914 }
Andreas Gampe3c12c512014-06-24 18:46:29 +0000915 if (rl_result.ref) {
916 LoadRefDisp(reg_ptr, data_offset, rl_result.reg, kNotVolatile);
917 } else {
918 LoadBaseDisp(reg_ptr, data_offset, rl_result.reg, size, kNotVolatile);
919 }
Vladimir Marko455759b2014-05-06 20:49:36 +0100920 MarkPossibleNullPointerException(opt_flags);
921 if (!constant_index) {
922 FreeTemp(reg_ptr);
923 }
Matteo Franchin43ec8732014-03-31 15:00:14 +0100924 if (rl_dest.wide) {
Matteo Franchin43ec8732014-03-31 15:00:14 +0100925 StoreValueWide(rl_dest, rl_result);
926 } else {
Matteo Franchin43ec8732014-03-31 15:00:14 +0100927 StoreValue(rl_dest, rl_result);
928 }
929 } else {
930 // Offset base, then use indexed load
buzbeea0cd2d72014-06-01 09:33:49 -0700931 RegStorage reg_ptr = AllocTempRef();
Matteo Franchin43ec8732014-03-31 15:00:14 +0100932 OpRegRegImm(kOpAdd, reg_ptr, rl_array.reg, data_offset);
933 FreeTemp(rl_array.reg);
934 rl_result = EvalLoc(rl_dest, reg_class, true);
935
936 if (needs_range_check) {
937 GenArrayBoundsCheck(rl_index.reg, reg_len);
938 FreeTemp(reg_len);
939 }
Andreas Gampe3c12c512014-06-24 18:46:29 +0000940 if (rl_result.ref) {
Matteo Franchin255e0142014-07-04 13:50:41 +0100941 LoadRefIndexed(reg_ptr, As64BitReg(rl_index.reg), rl_result.reg, scale);
Andreas Gampe3c12c512014-06-24 18:46:29 +0000942 } else {
943 LoadBaseIndexed(reg_ptr, As64BitReg(rl_index.reg), rl_result.reg, scale, size);
944 }
Matteo Franchin43ec8732014-03-31 15:00:14 +0100945 MarkPossibleNullPointerException(opt_flags);
946 FreeTemp(reg_ptr);
947 StoreValue(rl_dest, rl_result);
948 }
949}
950
951/*
952 * Generate array store
953 *
954 */
955void Arm64Mir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array,
956 RegLocation rl_index, RegLocation rl_src, int scale, bool card_mark) {
957 RegisterClass reg_class = RegClassBySize(size);
958 int len_offset = mirror::Array::LengthOffset().Int32Value();
959 bool constant_index = rl_index.is_const;
960
961 int data_offset;
962 if (size == k64 || size == kDouble) {
963 data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Int32Value();
964 } else {
965 data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Int32Value();
966 }
967
968 // If index is constant, just fold it into the data offset.
969 if (constant_index) {
970 data_offset += mir_graph_->ConstantValue(rl_index) << scale;
971 }
972
buzbeea0cd2d72014-06-01 09:33:49 -0700973 rl_array = LoadValue(rl_array, kRefReg);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100974 if (!constant_index) {
975 rl_index = LoadValue(rl_index, kCoreReg);
976 }
977
978 RegStorage reg_ptr;
979 bool allocated_reg_ptr_temp = false;
980 if (constant_index) {
981 reg_ptr = rl_array.reg;
982 } else if (IsTemp(rl_array.reg) && !card_mark) {
983 Clobber(rl_array.reg);
984 reg_ptr = rl_array.reg;
985 } else {
986 allocated_reg_ptr_temp = true;
buzbeea0cd2d72014-06-01 09:33:49 -0700987 reg_ptr = AllocTempRef();
Matteo Franchin43ec8732014-03-31 15:00:14 +0100988 }
989
990 /* null object? */
991 GenNullCheck(rl_array.reg, opt_flags);
992
993 bool needs_range_check = (!(opt_flags & MIR_IGNORE_RANGE_CHECK));
994 RegStorage reg_len;
995 if (needs_range_check) {
996 reg_len = AllocTemp();
997 // NOTE: max live temps(4) here.
998 /* Get len */
999 Load32Disp(rl_array.reg, len_offset, reg_len);
1000 MarkPossibleNullPointerException(opt_flags);
1001 } else {
1002 ForceImplicitNullCheck(rl_array.reg, opt_flags);
1003 }
1004 /* at this point, reg_ptr points to array, 2 live temps */
1005 if (rl_src.wide || rl_src.fp || constant_index) {
1006 if (rl_src.wide) {
1007 rl_src = LoadValueWide(rl_src, reg_class);
1008 } else {
1009 rl_src = LoadValue(rl_src, reg_class);
1010 }
1011 if (!constant_index) {
buzbee33ae5582014-06-12 14:56:32 -07001012 OpRegRegRegShift(kOpAdd, reg_ptr, rl_array.reg, As64BitReg(rl_index.reg),
1013 EncodeShift(kA64Lsl, scale));
Matteo Franchin43ec8732014-03-31 15:00:14 +01001014 }
1015 if (needs_range_check) {
1016 if (constant_index) {
1017 GenArrayBoundsCheck(mir_graph_->ConstantValue(rl_index), reg_len);
1018 } else {
1019 GenArrayBoundsCheck(rl_index.reg, reg_len);
1020 }
1021 FreeTemp(reg_len);
1022 }
Andreas Gampe3c12c512014-06-24 18:46:29 +00001023 if (rl_src.ref) {
1024 StoreRefDisp(reg_ptr, data_offset, rl_src.reg, kNotVolatile);
1025 } else {
1026 StoreBaseDisp(reg_ptr, data_offset, rl_src.reg, size, kNotVolatile);
1027 }
Matteo Franchin43ec8732014-03-31 15:00:14 +01001028 MarkPossibleNullPointerException(opt_flags);
1029 } else {
1030 /* reg_ptr -> array data */
1031 OpRegRegImm(kOpAdd, reg_ptr, rl_array.reg, data_offset);
1032 rl_src = LoadValue(rl_src, reg_class);
1033 if (needs_range_check) {
1034 GenArrayBoundsCheck(rl_index.reg, reg_len);
1035 FreeTemp(reg_len);
1036 }
Andreas Gampe3c12c512014-06-24 18:46:29 +00001037 if (rl_src.ref) {
Matteo Franchin255e0142014-07-04 13:50:41 +01001038 StoreRefIndexed(reg_ptr, As64BitReg(rl_index.reg), rl_src.reg, scale);
Andreas Gampe3c12c512014-06-24 18:46:29 +00001039 } else {
1040 StoreBaseIndexed(reg_ptr, As64BitReg(rl_index.reg), rl_src.reg, scale, size);
1041 }
Matteo Franchin43ec8732014-03-31 15:00:14 +01001042 MarkPossibleNullPointerException(opt_flags);
1043 }
1044 if (allocated_reg_ptr_temp) {
1045 FreeTemp(reg_ptr);
1046 }
1047 if (card_mark) {
1048 MarkGCCard(rl_src.reg, rl_array.reg);
1049 }
1050}
1051
Matteo Franchin43ec8732014-03-31 15:00:14 +01001052void Arm64Mir2Lir::GenShiftImmOpLong(Instruction::Code opcode,
1053 RegLocation rl_dest, RegLocation rl_src, RegLocation rl_shift) {
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001054 OpKind op = kOpBkpt;
Matteo Franchin43ec8732014-03-31 15:00:14 +01001055 // Per spec, we only care about low 6 bits of shift amount.
1056 int shift_amount = mir_graph_->ConstantValue(rl_shift) & 0x3f;
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001057 rl_src = LoadValueWide(rl_src, kCoreReg);
Matteo Franchin43ec8732014-03-31 15:00:14 +01001058 if (shift_amount == 0) {
1059 StoreValueWide(rl_dest, rl_src);
1060 return;
1061 }
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001062
1063 RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true);
Matteo Franchin43ec8732014-03-31 15:00:14 +01001064 switch (opcode) {
1065 case Instruction::SHL_LONG:
1066 case Instruction::SHL_LONG_2ADDR:
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001067 op = kOpLsl;
Matteo Franchin43ec8732014-03-31 15:00:14 +01001068 break;
1069 case Instruction::SHR_LONG:
1070 case Instruction::SHR_LONG_2ADDR:
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001071 op = kOpAsr;
Matteo Franchin43ec8732014-03-31 15:00:14 +01001072 break;
1073 case Instruction::USHR_LONG:
1074 case Instruction::USHR_LONG_2ADDR:
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001075 op = kOpLsr;
Matteo Franchin43ec8732014-03-31 15:00:14 +01001076 break;
1077 default:
1078 LOG(FATAL) << "Unexpected case";
1079 }
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001080 OpRegRegImm(op, rl_result.reg, rl_src.reg, shift_amount);
Matteo Franchin43ec8732014-03-31 15:00:14 +01001081 StoreValueWide(rl_dest, rl_result);
1082}
1083
Matteo Franchine45fb9e2014-05-06 10:10:30 +01001084void Arm64Mir2Lir::GenArithImmOpLong(Instruction::Code opcode, RegLocation rl_dest,
1085 RegLocation rl_src1, RegLocation rl_src2) {
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001086 if ((opcode == Instruction::SUB_LONG) || (opcode == Instruction::SUB_LONG_2ADDR)) {
Matteo Franchin43ec8732014-03-31 15:00:14 +01001087 if (!rl_src2.is_const) {
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001088 return GenArithOpLong(opcode, rl_dest, rl_src1, rl_src2);
Matteo Franchin43ec8732014-03-31 15:00:14 +01001089 }
1090 } else {
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001091 // Associativity.
Matteo Franchin43ec8732014-03-31 15:00:14 +01001092 if (!rl_src2.is_const) {
1093 DCHECK(rl_src1.is_const);
1094 std::swap(rl_src1, rl_src2);
1095 }
1096 }
Matteo Franchin43ec8732014-03-31 15:00:14 +01001097 DCHECK(rl_src2.is_const);
Matteo Franchin43ec8732014-03-31 15:00:14 +01001098
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001099 OpKind op = kOpBkpt;
1100 int64_t val = mir_graph_->ConstantValueWide(rl_src2);
1101
Matteo Franchin43ec8732014-03-31 15:00:14 +01001102 switch (opcode) {
1103 case Instruction::ADD_LONG:
1104 case Instruction::ADD_LONG_2ADDR:
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001105 op = kOpAdd;
1106 break;
Matteo Franchin43ec8732014-03-31 15:00:14 +01001107 case Instruction::SUB_LONG:
1108 case Instruction::SUB_LONG_2ADDR:
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001109 op = kOpSub;
Matteo Franchin43ec8732014-03-31 15:00:14 +01001110 break;
1111 case Instruction::AND_LONG:
1112 case Instruction::AND_LONG_2ADDR:
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001113 op = kOpAnd;
Matteo Franchin43ec8732014-03-31 15:00:14 +01001114 break;
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001115 case Instruction::OR_LONG:
1116 case Instruction::OR_LONG_2ADDR:
1117 op = kOpOr;
Matteo Franchin43ec8732014-03-31 15:00:14 +01001118 break;
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001119 case Instruction::XOR_LONG:
1120 case Instruction::XOR_LONG_2ADDR:
1121 op = kOpXor;
1122 break;
Matteo Franchin43ec8732014-03-31 15:00:14 +01001123 default:
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001124 LOG(FATAL) << "Unexpected opcode";
Matteo Franchin43ec8732014-03-31 15:00:14 +01001125 }
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001126
1127 rl_src1 = LoadValueWide(rl_src1, kCoreReg);
1128 RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true);
Zheng Xue2eb29e2014-06-12 10:22:33 +08001129 OpRegRegImm64(op, rl_result.reg, rl_src1.reg, val);
Matteo Franchin43ec8732014-03-31 15:00:14 +01001130 StoreValueWide(rl_dest, rl_result);
1131}
1132
Matteo Franchine45fb9e2014-05-06 10:10:30 +01001133/**
1134 * @brief Split a register list in pairs or registers.
1135 *
1136 * Given a list of registers in @p reg_mask, split the list in pairs. Use as follows:
1137 * @code
1138 * int reg1 = -1, reg2 = -1;
1139 * while (reg_mask) {
1140 * reg_mask = GenPairWise(reg_mask, & reg1, & reg2);
1141 * if (UNLIKELY(reg2 < 0)) {
1142 * // Single register in reg1.
1143 * } else {
1144 * // Pair in reg1, reg2.
1145 * }
1146 * }
1147 * @endcode
1148 */
1149uint32_t Arm64Mir2Lir::GenPairWise(uint32_t reg_mask, int* reg1, int* reg2) {
1150 // Find first register.
1151 int first_bit_set = __builtin_ctz(reg_mask) + 1;
1152 int reg = *reg1 + first_bit_set;
1153 reg_mask >>= first_bit_set;
1154
1155 if (LIKELY(reg_mask)) {
1156 // Save the first register, find the second and use the pair opcode.
1157 int second_bit_set = __builtin_ctz(reg_mask) + 1;
1158 *reg2 = reg;
1159 reg_mask >>= second_bit_set;
1160 *reg1 = reg + second_bit_set;
1161 return reg_mask;
1162 }
1163
1164 // Use the single opcode, as we just have one register.
1165 *reg1 = reg;
1166 *reg2 = -1;
1167 return reg_mask;
1168}
1169
1170void Arm64Mir2Lir::UnSpillCoreRegs(RegStorage base, int offset, uint32_t reg_mask) {
1171 int reg1 = -1, reg2 = -1;
Matteo Franchinbc6d1972014-05-13 12:33:28 +01001172 const int reg_log2_size = 3;
Matteo Franchine45fb9e2014-05-06 10:10:30 +01001173
Matteo Franchinbc6d1972014-05-13 12:33:28 +01001174 for (offset = (offset >> reg_log2_size); reg_mask; offset += 2) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +01001175 reg_mask = GenPairWise(reg_mask, & reg1, & reg2);
1176 if (UNLIKELY(reg2 < 0)) {
Matteo Franchinbc6d1972014-05-13 12:33:28 +01001177 NewLIR3(WIDE(kA64Ldr3rXD), RegStorage::Solo64(reg1).GetReg(), base.GetReg(), offset);
Matteo Franchine45fb9e2014-05-06 10:10:30 +01001178 } else {
buzbeeb5860fb2014-06-21 15:31:01 -07001179 DCHECK_LE(offset, 63);
Matteo Franchinbc6d1972014-05-13 12:33:28 +01001180 NewLIR4(WIDE(kA64Ldp4rrXD), RegStorage::Solo64(reg2).GetReg(),
1181 RegStorage::Solo64(reg1).GetReg(), base.GetReg(), offset);
Matteo Franchine45fb9e2014-05-06 10:10:30 +01001182 }
1183 }
1184}
1185
1186void Arm64Mir2Lir::SpillCoreRegs(RegStorage base, int offset, uint32_t reg_mask) {
1187 int reg1 = -1, reg2 = -1;
Matteo Franchinbc6d1972014-05-13 12:33:28 +01001188 const int reg_log2_size = 3;
Matteo Franchine45fb9e2014-05-06 10:10:30 +01001189
Matteo Franchinbc6d1972014-05-13 12:33:28 +01001190 for (offset = (offset >> reg_log2_size); reg_mask; offset += 2) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +01001191 reg_mask = GenPairWise(reg_mask, & reg1, & reg2);
1192 if (UNLIKELY(reg2 < 0)) {
Matteo Franchinbc6d1972014-05-13 12:33:28 +01001193 NewLIR3(WIDE(kA64Str3rXD), RegStorage::Solo64(reg1).GetReg(), base.GetReg(), offset);
Matteo Franchine45fb9e2014-05-06 10:10:30 +01001194 } else {
Matteo Franchinbc6d1972014-05-13 12:33:28 +01001195 NewLIR4(WIDE(kA64Stp4rrXD), RegStorage::Solo64(reg2).GetReg(),
1196 RegStorage::Solo64(reg1).GetReg(), base.GetReg(), offset);
1197 }
1198 }
1199}
1200
1201void Arm64Mir2Lir::UnSpillFPRegs(RegStorage base, int offset, uint32_t reg_mask) {
1202 int reg1 = -1, reg2 = -1;
1203 const int reg_log2_size = 3;
1204
1205 for (offset = (offset >> reg_log2_size); reg_mask; offset += 2) {
1206 reg_mask = GenPairWise(reg_mask, & reg1, & reg2);
1207 if (UNLIKELY(reg2 < 0)) {
1208 NewLIR3(FWIDE(kA64Ldr3fXD), RegStorage::FloatSolo64(reg1).GetReg(), base.GetReg(), offset);
1209 } else {
1210 NewLIR4(WIDE(kA64Ldp4ffXD), RegStorage::FloatSolo64(reg2).GetReg(),
1211 RegStorage::FloatSolo64(reg1).GetReg(), base.GetReg(), offset);
1212 }
1213 }
1214}
1215
1216// TODO(Arm64): consider using ld1 and st1?
1217void Arm64Mir2Lir::SpillFPRegs(RegStorage base, int offset, uint32_t reg_mask) {
1218 int reg1 = -1, reg2 = -1;
1219 const int reg_log2_size = 3;
1220
1221 for (offset = (offset >> reg_log2_size); reg_mask; offset += 2) {
1222 reg_mask = GenPairWise(reg_mask, & reg1, & reg2);
1223 if (UNLIKELY(reg2 < 0)) {
1224 NewLIR3(FWIDE(kA64Str3fXD), RegStorage::FloatSolo64(reg1).GetReg(), base.GetReg(), offset);
1225 } else {
1226 NewLIR4(WIDE(kA64Stp4ffXD), RegStorage::FloatSolo64(reg2).GetReg(),
1227 RegStorage::FloatSolo64(reg1).GetReg(), base.GetReg(), offset);
Matteo Franchine45fb9e2014-05-06 10:10:30 +01001228 }
1229 }
1230}
1231
Serban Constantinescu23abec92014-07-02 16:13:38 +01001232bool Arm64Mir2Lir::GenInlinedReverseBits(CallInfo* info, OpSize size) {
1233 ArmOpcode wide = (size == k64) ? WIDE(0) : UNWIDE(0);
1234 RegLocation rl_src_i = info->args[0];
1235 RegLocation rl_dest = (size == k64) ? InlineTargetWide(info) : InlineTarget(info); // result reg
1236 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
1237 RegLocation rl_i = (size == k64) ? LoadValueWide(rl_src_i, kCoreReg) : LoadValue(rl_src_i, kCoreReg);
1238 NewLIR2(kA64Rbit2rr | wide, rl_result.reg.GetReg(), rl_i.reg.GetReg());
1239 (size == k64) ? StoreValueWide(rl_dest, rl_result) : StoreValue(rl_dest, rl_result);
1240 return true;
1241}
1242
Matteo Franchin43ec8732014-03-31 15:00:14 +01001243} // namespace art