blob: 56fb2dd0184af8aa64d6fb8b687bd1ec4edf48af [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"
22#include "entrypoints/quick/quick_entrypoints.h"
23#include "mirror/array.h"
24
25namespace art {
26
27LIR* Arm64Mir2Lir::OpCmpBranch(ConditionCode cond, RegStorage src1, RegStorage src2, LIR* target) {
28 OpRegReg(kOpCmp, src1, src2);
29 return OpCondBranch(cond, target);
30}
31
Matteo Franchin43ec8732014-03-31 15:00:14 +010032LIR* Arm64Mir2Lir::OpIT(ConditionCode ccode, const char* guide) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +010033 LOG(FATAL) << "Unexpected use of OpIT for Arm64";
34 return NULL;
Matteo Franchin43ec8732014-03-31 15:00:14 +010035}
36
37void Arm64Mir2Lir::OpEndIT(LIR* it) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +010038 LOG(FATAL) << "Unexpected use of OpEndIT for Arm64";
Matteo Franchin43ec8732014-03-31 15:00:14 +010039}
40
41/*
42 * 64-bit 3way compare function.
Matteo Franchine45fb9e2014-05-06 10:10:30 +010043 * cmp xA, xB
Zheng Xu511c8a62014-06-03 16:22:23 +080044 * csinc wC, wzr, wzr, eq // wC = (xA == xB) ? 0 : 1
45 * csneg wC, wC, wC, ge // wC = (xA >= xB) ? wC : -wC
Matteo Franchin43ec8732014-03-31 15:00:14 +010046 */
Matteo Franchine45fb9e2014-05-06 10:10:30 +010047void Arm64Mir2Lir::GenCmpLong(RegLocation rl_dest, RegLocation rl_src1,
48 RegLocation rl_src2) {
49 RegLocation rl_result;
Matteo Franchin43ec8732014-03-31 15:00:14 +010050 rl_src1 = LoadValueWide(rl_src1, kCoreReg);
51 rl_src2 = LoadValueWide(rl_src2, kCoreReg);
Matteo Franchine45fb9e2014-05-06 10:10:30 +010052 rl_result = EvalLoc(rl_dest, kCoreReg, true);
Matteo Franchin43ec8732014-03-31 15:00:14 +010053
Matteo Franchine45fb9e2014-05-06 10:10:30 +010054 OpRegReg(kOpCmp, rl_src1.reg, rl_src2.reg);
Zheng Xu511c8a62014-06-03 16:22:23 +080055 NewLIR4(kA64Csinc4rrrc, rl_result.reg.GetReg(), rwzr, rwzr, kArmCondEq);
56 NewLIR4(kA64Csneg4rrrc, rl_result.reg.GetReg(), rl_result.reg.GetReg(),
57 rl_result.reg.GetReg(), kArmCondGe);
58 StoreValue(rl_dest, rl_result);
Serban Constantinescued65c5e2014-05-22 15:10:18 +010059}
60
61void Arm64Mir2Lir::GenShiftOpLong(Instruction::Code opcode, RegLocation rl_dest,
62 RegLocation rl_src1, RegLocation rl_shift) {
63 OpKind op = kOpBkpt;
64 switch (opcode) {
65 case Instruction::SHL_LONG:
66 case Instruction::SHL_LONG_2ADDR:
67 op = kOpLsl;
68 break;
69 case Instruction::SHR_LONG:
70 case Instruction::SHR_LONG_2ADDR:
71 op = kOpAsr;
72 break;
73 case Instruction::USHR_LONG:
74 case Instruction::USHR_LONG_2ADDR:
75 op = kOpLsr;
76 break;
77 default:
78 LOG(FATAL) << "Unexpected case: " << opcode;
79 }
Zheng Xue2eb29e2014-06-12 10:22:33 +080080 rl_shift = LoadValue(rl_shift, kCoreReg);
Serban Constantinescued65c5e2014-05-22 15:10:18 +010081 rl_src1 = LoadValueWide(rl_src1, kCoreReg);
82 RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true);
Zheng Xue2eb29e2014-06-12 10:22:33 +080083 OpRegRegReg(op, rl_result.reg, rl_src1.reg, As64BitReg(rl_shift.reg));
Serban Constantinescued65c5e2014-05-22 15:10:18 +010084 StoreValueWide(rl_dest, rl_result);
Matteo Franchin43ec8732014-03-31 15:00:14 +010085}
86
Matteo Franchin43ec8732014-03-31 15:00:14 +010087void Arm64Mir2Lir::GenSelect(BasicBlock* bb, MIR* mir) {
88 RegLocation rl_result;
89 RegLocation rl_src = mir_graph_->GetSrc(mir, 0);
90 RegLocation rl_dest = mir_graph_->GetDest(mir);
buzbeea0cd2d72014-06-01 09:33:49 -070091 RegisterClass src_reg_class = rl_src.ref ? kRefReg : kCoreReg;
92 RegisterClass result_reg_class = rl_dest.ref ? kRefReg : kCoreReg;
93 rl_src = LoadValue(rl_src, src_reg_class);
Serban Constantinescu05e27ff2014-05-28 13:21:45 +010094 ArmConditionCode code = ArmConditionEncoding(mir->meta.ccode);
95
96 RegLocation rl_true = mir_graph_->reg_location_[mir->ssa_rep->uses[1]];
97 RegLocation rl_false = mir_graph_->reg_location_[mir->ssa_rep->uses[2]];
buzbeea0cd2d72014-06-01 09:33:49 -070098 rl_true = LoadValue(rl_true, result_reg_class);
99 rl_false = LoadValue(rl_false, result_reg_class);
100 rl_result = EvalLoc(rl_dest, result_reg_class, true);
Serban Constantinescu05e27ff2014-05-28 13:21:45 +0100101 OpRegImm(kOpCmp, rl_src.reg, 0);
102 NewLIR4(kA64Csel4rrrc, rl_result.reg.GetReg(), rl_true.reg.GetReg(),
103 rl_false.reg.GetReg(), code);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100104 StoreValue(rl_dest, rl_result);
105}
106
107void Arm64Mir2Lir::GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir) {
108 RegLocation rl_src1 = mir_graph_->GetSrcWide(mir, 0);
109 RegLocation rl_src2 = mir_graph_->GetSrcWide(mir, 2);
Serban Constantinescu05e27ff2014-05-28 13:21:45 +0100110 LIR* taken = &block_label_list_[bb->taken];
111 LIR* not_taken = &block_label_list_[bb->fall_through];
112 rl_src1 = LoadValueWide(rl_src1, kCoreReg);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100113 // Normalize such that if either operand is constant, src2 will be constant.
114 ConditionCode ccode = mir->meta.ccode;
115 if (rl_src1.is_const) {
116 std::swap(rl_src1, rl_src2);
117 ccode = FlipComparisonOrder(ccode);
118 }
Serban Constantinescu05e27ff2014-05-28 13:21:45 +0100119
Matteo Franchin43ec8732014-03-31 15:00:14 +0100120 if (rl_src2.is_const) {
Serban Constantinescu05e27ff2014-05-28 13:21:45 +0100121 rl_src2 = UpdateLocWide(rl_src2);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100122 int64_t val = mir_graph_->ConstantValueWide(rl_src2);
Serban Constantinescu05e27ff2014-05-28 13:21:45 +0100123 // Special handling using cbz & cbnz.
124 if (val == 0 && (ccode == kCondEq || ccode == kCondNe)) {
125 OpCmpImmBranch(ccode, rl_src1.reg, 0, taken);
126 OpCmpImmBranch(NegateComparison(ccode), rl_src1.reg, 0, not_taken);
127 return;
128 // Only handle Imm if src2 is not already in a register.
129 } else if (rl_src2.location != kLocPhysReg) {
130 OpRegImm64(kOpCmp, rl_src1.reg, val);
131 OpCondBranch(ccode, taken);
132 OpCondBranch(NegateComparison(ccode), not_taken);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100133 return;
134 }
135 }
Serban Constantinescu05e27ff2014-05-28 13:21:45 +0100136
Matteo Franchin43ec8732014-03-31 15:00:14 +0100137 rl_src2 = LoadValueWide(rl_src2, kCoreReg);
Serban Constantinescu05e27ff2014-05-28 13:21:45 +0100138 OpRegReg(kOpCmp, rl_src1.reg, rl_src2.reg);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100139 OpCondBranch(ccode, taken);
Serban Constantinescu05e27ff2014-05-28 13:21:45 +0100140 OpCondBranch(NegateComparison(ccode), not_taken);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100141}
142
143/*
144 * Generate a register comparison to an immediate and branch. Caller
145 * is responsible for setting branch target field.
146 */
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100147LIR* Arm64Mir2Lir::OpCmpImmBranch(ConditionCode cond, RegStorage reg, int check_value,
148 LIR* target) {
Matteo Franchin43ec8732014-03-31 15:00:14 +0100149 LIR* branch;
150 ArmConditionCode arm_cond = ArmConditionEncoding(cond);
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100151 if (check_value == 0 && (arm_cond == kArmCondEq || arm_cond == kArmCondNe)) {
152 ArmOpcode opcode = (arm_cond == kArmCondEq) ? kA64Cbz2rt : kA64Cbnz2rt;
Serban Constantinescued65c5e2014-05-22 15:10:18 +0100153 ArmOpcode wide = reg.Is64Bit() ? WIDE(0) : UNWIDE(0);
154 branch = NewLIR2(opcode | wide, reg.GetReg(), 0);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100155 } else {
156 OpRegImm(kOpCmp, reg, check_value);
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100157 branch = NewLIR2(kA64B2ct, arm_cond, 0);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100158 }
159 branch->target = target;
160 return branch;
161}
162
Zheng Xu7c1c2632014-06-17 18:17:31 +0800163LIR* Arm64Mir2Lir::OpCmpMemImmBranch(ConditionCode cond, RegStorage temp_reg,
164 RegStorage base_reg, int offset, int check_value,
165 LIR* target) {
166 // It is possible that temp register is 64-bit. (ArgReg or RefReg)
167 // Always compare 32-bit value no matter what temp_reg is.
168 if (temp_reg.Is64Bit()) {
169 temp_reg = As32BitReg(temp_reg);
170 }
171 Load32Disp(base_reg, offset, temp_reg);
172 LIR* branch = OpCmpImmBranch(cond, temp_reg, check_value, target);
173 return branch;
174}
175
Matteo Franchin43ec8732014-03-31 15:00:14 +0100176LIR* Arm64Mir2Lir::OpRegCopyNoInsert(RegStorage r_dest, RegStorage r_src) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100177 bool dest_is_fp = r_dest.IsFloat();
178 bool src_is_fp = r_src.IsFloat();
179 ArmOpcode opcode = kA64Brk1d;
Matteo Franchin43ec8732014-03-31 15:00:14 +0100180 LIR* res;
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100181
182 if (LIKELY(dest_is_fp == src_is_fp)) {
183 if (LIKELY(!dest_is_fp)) {
184 // Core/core copy.
185 // Copies involving the sp register require a different instruction.
186 opcode = UNLIKELY(A64_REG_IS_SP(r_dest.GetReg())) ? kA64Add4RRdT : kA64Mov2rr;
187
188 // TODO(Arm64): kA64Add4RRdT formally has 4 args, but is used as a 2 args instruction.
189 // This currently works because the other arguments are set to 0 by default. We should
190 // rather introduce an alias kA64Mov2RR.
191
192 // core/core copy. Do a x/x copy only if both registers are x.
193 if (r_dest.Is64Bit() && r_src.Is64Bit()) {
194 opcode = WIDE(opcode);
195 }
196 } else {
197 // Float/float copy.
198 bool dest_is_double = r_dest.IsDouble();
199 bool src_is_double = r_src.IsDouble();
200
201 // We do not do float/double or double/float casts here.
202 DCHECK_EQ(dest_is_double, src_is_double);
203
204 // Homogeneous float/float copy.
205 opcode = (dest_is_double) ? FWIDE(kA64Fmov2ff) : kA64Fmov2ff;
206 }
207 } else {
208 // Inhomogeneous register copy.
209 if (dest_is_fp) {
210 if (r_dest.IsDouble()) {
211 opcode = kA64Fmov2Sx;
212 } else {
213 DCHECK(r_src.IsSingle());
214 opcode = kA64Fmov2sw;
215 }
216 } else {
217 if (r_src.IsDouble()) {
218 opcode = kA64Fmov2xS;
219 } else {
220 DCHECK(r_dest.Is32Bit());
221 opcode = kA64Fmov2ws;
222 }
223 }
Matteo Franchin43ec8732014-03-31 15:00:14 +0100224 }
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100225
Matteo Franchin43ec8732014-03-31 15:00:14 +0100226 res = RawLIR(current_dalvik_offset_, opcode, r_dest.GetReg(), r_src.GetReg());
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100227
Matteo Franchin43ec8732014-03-31 15:00:14 +0100228 if (!(cu_->disable_opt & (1 << kSafeOptimizations)) && r_dest == r_src) {
229 res->flags.is_nop = true;
230 }
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100231
Matteo Franchin43ec8732014-03-31 15:00:14 +0100232 return res;
233}
234
235void Arm64Mir2Lir::OpRegCopy(RegStorage r_dest, RegStorage r_src) {
236 if (r_dest != r_src) {
237 LIR* res = OpRegCopyNoInsert(r_dest, r_src);
238 AppendLIR(res);
239 }
240}
241
242void Arm64Mir2Lir::OpRegCopyWide(RegStorage r_dest, RegStorage r_src) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100243 OpRegCopy(r_dest, r_src);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100244}
245
246// Table of magic divisors
247struct MagicTable {
248 uint32_t magic;
249 uint32_t shift;
250 DividePattern pattern;
251};
252
253static const MagicTable magic_table[] = {
254 {0, 0, DivideNone}, // 0
255 {0, 0, DivideNone}, // 1
256 {0, 0, DivideNone}, // 2
257 {0x55555556, 0, Divide3}, // 3
258 {0, 0, DivideNone}, // 4
259 {0x66666667, 1, Divide5}, // 5
260 {0x2AAAAAAB, 0, Divide3}, // 6
261 {0x92492493, 2, Divide7}, // 7
262 {0, 0, DivideNone}, // 8
263 {0x38E38E39, 1, Divide5}, // 9
264 {0x66666667, 2, Divide5}, // 10
265 {0x2E8BA2E9, 1, Divide5}, // 11
266 {0x2AAAAAAB, 1, Divide5}, // 12
267 {0x4EC4EC4F, 2, Divide5}, // 13
268 {0x92492493, 3, Divide7}, // 14
269 {0x88888889, 3, Divide7}, // 15
270};
271
272// Integer division by constant via reciprocal multiply (Hacker's Delight, 10-4)
273bool Arm64Mir2Lir::SmallLiteralDivRem(Instruction::Code dalvik_opcode, bool is_div,
Matteo Franchinc61b3c92014-06-18 11:52:47 +0100274 RegLocation rl_src, RegLocation rl_dest, int lit) {
275 if ((lit < 0) || (lit >= static_cast<int>(arraysize(magic_table)))) {
Matteo Franchin43ec8732014-03-31 15:00:14 +0100276 return false;
277 }
278 DividePattern pattern = magic_table[lit].pattern;
279 if (pattern == DivideNone) {
280 return false;
281 }
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100282 // Tuning: add rem patterns
283 if (!is_div) {
284 return false;
285 }
Matteo Franchin43ec8732014-03-31 15:00:14 +0100286
287 RegStorage r_magic = AllocTemp();
288 LoadConstant(r_magic, magic_table[lit].magic);
289 rl_src = LoadValue(rl_src, kCoreReg);
290 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
Matteo Franchinc61b3c92014-06-18 11:52:47 +0100291 RegStorage r_long_mul = AllocTemp();
292 NewLIR4(kA64Smaddl4xwwx, As64BitReg(r_long_mul).GetReg(),
293 r_magic.GetReg(), rl_src.reg.GetReg(), rxzr);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100294 switch (pattern) {
295 case Divide3:
Matteo Franchinc61b3c92014-06-18 11:52:47 +0100296 OpRegRegImm(kOpLsr, As64BitReg(r_long_mul), As64BitReg(r_long_mul), 32);
297 OpRegRegRegShift(kOpSub, rl_result.reg, r_long_mul, rl_src.reg, EncodeShift(kA64Asr, 31));
Matteo Franchin43ec8732014-03-31 15:00:14 +0100298 break;
299 case Divide5:
Matteo Franchinc61b3c92014-06-18 11:52:47 +0100300 OpRegRegImm(kOpAsr, As64BitReg(r_long_mul), As64BitReg(r_long_mul),
301 32 + magic_table[lit].shift);
302 OpRegRegRegShift(kOpSub, rl_result.reg, r_long_mul, rl_src.reg, EncodeShift(kA64Asr, 31));
Matteo Franchin43ec8732014-03-31 15:00:14 +0100303 break;
304 case Divide7:
Matteo Franchinc61b3c92014-06-18 11:52:47 +0100305 OpRegRegRegShift(kOpAdd, As64BitReg(r_long_mul), As64BitReg(rl_src.reg),
306 As64BitReg(r_long_mul), EncodeShift(kA64Lsr, 32));
307 OpRegRegImm(kOpAsr, r_long_mul, r_long_mul, magic_table[lit].shift);
308 OpRegRegRegShift(kOpSub, rl_result.reg, r_long_mul, rl_src.reg, EncodeShift(kA64Asr, 31));
Matteo Franchin43ec8732014-03-31 15:00:14 +0100309 break;
310 default:
311 LOG(FATAL) << "Unexpected pattern: " << pattern;
312 }
Matteo Franchin43ec8732014-03-31 15:00:14 +0100313 StoreValue(rl_dest, rl_result);
314 return true;
315}
316
Matteo Franchinc61b3c92014-06-18 11:52:47 +0100317// Returns true if it added instructions to 'cu' to divide 'rl_src' by 'lit'
318// and store the result in 'rl_dest'.
319bool Arm64Mir2Lir::HandleEasyDivRem(Instruction::Code dalvik_opcode, bool is_div,
320 RegLocation rl_src, RegLocation rl_dest, int lit) {
321 if (lit < 2) {
322 return false;
323 }
324 if (!IsPowerOfTwo(lit)) {
325 return SmallLiteralDivRem(dalvik_opcode, is_div, rl_src, rl_dest, lit);
326 }
327 int k = LowestSetBit(lit);
328 if (k >= 30) {
329 // Avoid special cases.
330 return false;
331 }
332 rl_src = LoadValue(rl_src, kCoreReg);
333 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
334 if (is_div) {
335 RegStorage t_reg = AllocTemp();
336 if (lit == 2) {
337 // Division by 2 is by far the most common division by constant.
338 OpRegRegRegShift(kOpAdd, t_reg, rl_src.reg, rl_src.reg, EncodeShift(kA64Lsr, 32 - k));
339 OpRegRegImm(kOpAsr, rl_result.reg, t_reg, k);
340 } else {
341 OpRegRegImm(kOpAsr, t_reg, rl_src.reg, 31);
342 OpRegRegRegShift(kOpAdd, t_reg, rl_src.reg, t_reg, EncodeShift(kA64Lsr, 32 - k));
343 OpRegRegImm(kOpAsr, rl_result.reg, t_reg, k);
344 }
345 } else {
346 RegStorage t_reg = AllocTemp();
347 if (lit == 2) {
348 OpRegRegRegShift(kOpAdd, t_reg, rl_src.reg, rl_src.reg, EncodeShift(kA64Lsr, 32 - k));
349 OpRegRegImm(kOpAnd, t_reg, t_reg, lit - 1);
350 OpRegRegRegShift(kOpSub, rl_result.reg, t_reg, rl_src.reg, EncodeShift(kA64Lsr, 32 - k));
351 } else {
352 RegStorage t_reg2 = AllocTemp();
353 OpRegRegImm(kOpAsr, t_reg, rl_src.reg, 31);
354 OpRegRegRegShift(kOpAdd, t_reg2, rl_src.reg, t_reg, EncodeShift(kA64Lsr, 32 - k));
355 OpRegRegImm(kOpAnd, t_reg2, t_reg2, lit - 1);
356 OpRegRegRegShift(kOpSub, rl_result.reg, t_reg2, t_reg, EncodeShift(kA64Lsr, 32 - k));
357 }
358 }
359 StoreValue(rl_dest, rl_result);
360 return true;
361}
362
Matteo Franchin43ec8732014-03-31 15:00:14 +0100363bool Arm64Mir2Lir::EasyMultiply(RegLocation rl_src, RegLocation rl_dest, int lit) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100364 LOG(FATAL) << "Unexpected use of EasyMultiply for Arm64";
365 return false;
Matteo Franchin43ec8732014-03-31 15:00:14 +0100366}
367
368RegLocation Arm64Mir2Lir::GenDivRem(RegLocation rl_dest, RegLocation rl_src1,
Matteo Franchinc61b3c92014-06-18 11:52:47 +0100369 RegLocation rl_src2, bool is_div, bool check_zero) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100370 LOG(FATAL) << "Unexpected use of GenDivRem for Arm64";
Matteo Franchin43ec8732014-03-31 15:00:14 +0100371 return rl_dest;
372}
373
374RegLocation Arm64Mir2Lir::GenDivRemLit(RegLocation rl_dest, RegLocation rl_src1, int lit, bool is_div) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100375 LOG(FATAL) << "Unexpected use of GenDivRemLit for Arm64";
Matteo Franchin43ec8732014-03-31 15:00:14 +0100376 return rl_dest;
377}
378
379RegLocation Arm64Mir2Lir::GenDivRemLit(RegLocation rl_dest, RegStorage reg1, int lit, bool is_div) {
380 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
381
382 // Put the literal in a temp.
383 RegStorage lit_temp = AllocTemp();
384 LoadConstant(lit_temp, lit);
385 // Use the generic case for div/rem with arg2 in a register.
386 // TODO: The literal temp can be freed earlier during a modulus to reduce reg pressure.
387 rl_result = GenDivRem(rl_result, reg1, lit_temp, is_div);
388 FreeTemp(lit_temp);
389
390 return rl_result;
391}
392
Serban Constantinescued65c5e2014-05-22 15:10:18 +0100393RegLocation Arm64Mir2Lir::GenDivRem(RegLocation rl_dest, RegStorage r_src1, RegStorage r_src2,
Matteo Franchin43ec8732014-03-31 15:00:14 +0100394 bool is_div) {
Serban Constantinescued65c5e2014-05-22 15:10:18 +0100395 CHECK_EQ(r_src1.Is64Bit(), r_src2.Is64Bit());
396
Matteo Franchin43ec8732014-03-31 15:00:14 +0100397 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
398 if (is_div) {
Serban Constantinescued65c5e2014-05-22 15:10:18 +0100399 OpRegRegReg(kOpDiv, rl_result.reg, r_src1, r_src2);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100400 } else {
Serban Constantinescued65c5e2014-05-22 15:10:18 +0100401 // temp = r_src1 / r_src2
402 // dest = r_src1 - temp * r_src2
403 RegStorage temp;
404 ArmOpcode wide;
405 if (rl_result.reg.Is64Bit()) {
406 temp = AllocTempWide();
407 wide = WIDE(0);
408 } else {
409 temp = AllocTemp();
410 wide = UNWIDE(0);
411 }
412 OpRegRegReg(kOpDiv, temp, r_src1, r_src2);
413 NewLIR4(kA64Msub4rrrr | wide, rl_result.reg.GetReg(), temp.GetReg(),
414 r_src1.GetReg(), r_src2.GetReg());
Matteo Franchin43ec8732014-03-31 15:00:14 +0100415 FreeTemp(temp);
416 }
Matteo Franchin43ec8732014-03-31 15:00:14 +0100417 return rl_result;
418}
419
Serban Constantinescu169489b2014-06-11 16:43:35 +0100420bool Arm64Mir2Lir::GenInlinedAbsLong(CallInfo* info) {
421 RegLocation rl_src = info->args[0];
422 rl_src = LoadValueWide(rl_src, kCoreReg);
423 RegLocation rl_dest = InlineTargetWide(info);
424 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
425 RegStorage sign_reg = AllocTempWide();
426 // abs(x) = y<=x>>63, (x+y)^y.
427 OpRegRegImm(kOpAsr, sign_reg, rl_src.reg, 63);
428 OpRegRegReg(kOpAdd, rl_result.reg, rl_src.reg, sign_reg);
429 OpRegReg(kOpXor, rl_result.reg, sign_reg);
430 StoreValueWide(rl_dest, rl_result);
431 return true;
432}
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100433
Serban Constantinescu169489b2014-06-11 16:43:35 +0100434bool Arm64Mir2Lir::GenInlinedMinMaxInt(CallInfo* info, bool is_min) {
435 DCHECK_EQ(cu_->instruction_set, kArm64);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100436 RegLocation rl_src1 = info->args[0];
437 RegLocation rl_src2 = info->args[1];
438 rl_src1 = LoadValue(rl_src1, kCoreReg);
439 rl_src2 = LoadValue(rl_src2, kCoreReg);
440 RegLocation rl_dest = InlineTarget(info);
441 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
442 OpRegReg(kOpCmp, rl_src1.reg, rl_src2.reg);
Serban Constantinescu169489b2014-06-11 16:43:35 +0100443 NewLIR4(kA64Csel4rrrc, rl_result.reg.GetReg(), rl_src1.reg.GetReg(),
444 rl_src2.reg.GetReg(), (is_min) ? kArmCondLt : kArmCondGt);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100445 StoreValue(rl_dest, rl_result);
446 return true;
447}
448
449bool Arm64Mir2Lir::GenInlinedPeek(CallInfo* info, OpSize size) {
450 RegLocation rl_src_address = info->args[0]; // long address
Serban Constantinescu169489b2014-06-11 16:43:35 +0100451 rl_src_address = NarrowRegLoc(rl_src_address); // ignore high half in info->args[1] ?
Matteo Franchin43ec8732014-03-31 15:00:14 +0100452 RegLocation rl_dest = InlineTarget(info);
Serban Constantinescu169489b2014-06-11 16:43:35 +0100453 RegLocation rl_address = LoadValue(rl_src_address, kCoreReg); // kRefReg
Matteo Franchin43ec8732014-03-31 15:00:14 +0100454 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
Serban Constantinescu169489b2014-06-11 16:43:35 +0100455
Andreas Gampe3c12c512014-06-24 18:46:29 +0000456 LoadBaseDisp(rl_address.reg, 0, rl_result.reg, size, kNotVolatile);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100457 if (size == k64) {
Matteo Franchin43ec8732014-03-31 15:00:14 +0100458 StoreValueWide(rl_dest, rl_result);
459 } else {
460 DCHECK(size == kSignedByte || size == kSignedHalf || size == k32);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100461 StoreValue(rl_dest, rl_result);
462 }
463 return true;
464}
465
466bool Arm64Mir2Lir::GenInlinedPoke(CallInfo* info, OpSize size) {
467 RegLocation rl_src_address = info->args[0]; // long address
468 rl_src_address = NarrowRegLoc(rl_src_address); // ignore high half in info->args[1]
469 RegLocation rl_src_value = info->args[2]; // [size] value
Serban Constantinescu169489b2014-06-11 16:43:35 +0100470 RegLocation rl_address = LoadValue(rl_src_address, kCoreReg); // kRefReg
471
472 RegLocation rl_value;
Matteo Franchin43ec8732014-03-31 15:00:14 +0100473 if (size == k64) {
Serban Constantinescu169489b2014-06-11 16:43:35 +0100474 rl_value = LoadValueWide(rl_src_value, kCoreReg);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100475 } else {
476 DCHECK(size == kSignedByte || size == kSignedHalf || size == k32);
Serban Constantinescu169489b2014-06-11 16:43:35 +0100477 rl_value = LoadValue(rl_src_value, kCoreReg);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100478 }
Andreas Gampe3c12c512014-06-24 18:46:29 +0000479 StoreBaseDisp(rl_address.reg, 0, rl_value.reg, size, kNotVolatile);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100480 return true;
481}
482
483void Arm64Mir2Lir::OpLea(RegStorage r_base, RegStorage reg1, RegStorage reg2, int scale, int offset) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100484 LOG(FATAL) << "Unexpected use of OpLea for Arm64";
Matteo Franchin43ec8732014-03-31 15:00:14 +0100485}
486
Andreas Gampe2f244e92014-05-08 03:35:25 -0700487void Arm64Mir2Lir::OpTlsCmp(ThreadOffset<4> offset, int val) {
488 UNIMPLEMENTED(FATAL) << "Should not be used.";
489}
490
491void Arm64Mir2Lir::OpTlsCmp(ThreadOffset<8> offset, int val) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100492 LOG(FATAL) << "Unexpected use of OpTlsCmp for Arm64";
Matteo Franchin43ec8732014-03-31 15:00:14 +0100493}
494
495bool Arm64Mir2Lir::GenInlinedCas(CallInfo* info, bool is_long, bool is_object) {
Serban Constantinescu169489b2014-06-11 16:43:35 +0100496 DCHECK_EQ(cu_->instruction_set, kArm64);
497 ArmOpcode wide = is_long ? WIDE(0) : UNWIDE(0);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100498 // Unused - RegLocation rl_src_unsafe = info->args[0];
499 RegLocation rl_src_obj = info->args[1]; // Object - known non-null
500 RegLocation rl_src_offset = info->args[2]; // long low
Serban Constantinescu169489b2014-06-11 16:43:35 +0100501 rl_src_offset = NarrowRegLoc(rl_src_offset); // ignore high half in info->args[3] //TODO: do we really need this
Matteo Franchin43ec8732014-03-31 15:00:14 +0100502 RegLocation rl_src_expected = info->args[4]; // int, long or Object
503 // If is_long, high half is in info->args[5]
504 RegLocation rl_src_new_value = info->args[is_long ? 6 : 5]; // int, long or Object
505 // If is_long, high half is in info->args[7]
506 RegLocation rl_dest = InlineTarget(info); // boolean place for result
507
Serban Constantinescu169489b2014-06-11 16:43:35 +0100508 // Load Object and offset
buzbeea0cd2d72014-06-01 09:33:49 -0700509 RegLocation rl_object = LoadValue(rl_src_obj, kRefReg);
Serban Constantinescu169489b2014-06-11 16:43:35 +0100510 RegLocation rl_offset = LoadValue(rl_src_offset, kRefReg);
511
Matteo Franchin43ec8732014-03-31 15:00:14 +0100512 RegLocation rl_new_value;
Serban Constantinescu169489b2014-06-11 16:43:35 +0100513 RegLocation rl_expected;
514 if (is_long) {
Matteo Franchin43ec8732014-03-31 15:00:14 +0100515 rl_new_value = LoadValueWide(rl_src_new_value, kCoreReg);
Serban Constantinescu169489b2014-06-11 16:43:35 +0100516 rl_expected = LoadValueWide(rl_src_expected, kCoreReg);
517 } else {
518 rl_new_value = LoadValue(rl_src_new_value, is_object ? kRefReg : kCoreReg);
519 rl_expected = LoadValue(rl_src_expected, is_object ? kRefReg : kCoreReg);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100520 }
521
522 if (is_object && !mir_graph_->IsConstantNullRef(rl_new_value)) {
523 // Mark card for object assuming new value is stored.
524 MarkGCCard(rl_new_value.reg, rl_object.reg);
525 }
526
Serban Constantinescu169489b2014-06-11 16:43:35 +0100527 RegStorage r_ptr = AllocTempRef();
Matteo Franchin43ec8732014-03-31 15:00:14 +0100528 OpRegRegReg(kOpAdd, r_ptr, rl_object.reg, rl_offset.reg);
529
530 // Free now unneeded rl_object and rl_offset to give more temps.
531 ClobberSReg(rl_object.s_reg_low);
532 FreeTemp(rl_object.reg);
533 ClobberSReg(rl_offset.s_reg_low);
534 FreeTemp(rl_offset.reg);
535
Matteo Franchin43ec8732014-03-31 15:00:14 +0100536 // do {
537 // tmp = [r_ptr] - expected;
538 // } while (tmp == 0 && failure([r_ptr] <- r_new_value));
539 // result = tmp != 0;
540
Serban Constantinescu169489b2014-06-11 16:43:35 +0100541 RegStorage r_tmp;
Matteo Franchin43ec8732014-03-31 15:00:14 +0100542 if (is_long) {
Serban Constantinescu169489b2014-06-11 16:43:35 +0100543 r_tmp = AllocTempWide();
544 } else if (is_object) {
545 r_tmp = AllocTempRef();
Matteo Franchin43ec8732014-03-31 15:00:14 +0100546 } else {
Serban Constantinescu169489b2014-06-11 16:43:35 +0100547 r_tmp = AllocTemp();
Matteo Franchin43ec8732014-03-31 15:00:14 +0100548 }
549
Serban Constantinescu169489b2014-06-11 16:43:35 +0100550 LIR* loop = NewLIR0(kPseudoTargetLabel);
551 NewLIR2(kA64Ldaxr2rX | wide, r_tmp.GetReg(), r_ptr.GetReg());
552 OpRegReg(kOpCmp, r_tmp, rl_expected.reg);
Vladimir Marko8dea81c2014-06-06 14:50:36 +0100553 DCHECK(last_lir_insn_->u.m.def_mask->HasBit(ResourceMask::kCCode));
Serban Constantinescu169489b2014-06-11 16:43:35 +0100554 LIR* early_exit = OpCondBranch(kCondNe, NULL);
555
556 NewLIR3(kA64Stlxr3wrX | wide, As32BitReg(r_tmp).GetReg(), rl_new_value.reg.GetReg(), r_ptr.GetReg());
557 NewLIR3(kA64Cmp3RdT, As32BitReg(r_tmp).GetReg(), 0, ENCODE_NO_SHIFT);
558 DCHECK(last_lir_insn_->u.m.def_mask->HasBit(ResourceMask::kCCode));
559 OpCondBranch(kCondNe, loop);
560
561 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
562 LIR* exit = NewLIR4(kA64Csinc4rrrc, rl_result.reg.GetReg(), rwzr, rwzr, kArmCondNe);
563 early_exit->target = exit;
564
Matteo Franchin43ec8732014-03-31 15:00:14 +0100565 FreeTemp(r_tmp); // Now unneeded.
Serban Constantinescu169489b2014-06-11 16:43:35 +0100566 FreeTemp(r_ptr); // Now unneeded.
Matteo Franchin43ec8732014-03-31 15:00:14 +0100567
568 StoreValue(rl_dest, rl_result);
569
Matteo Franchin43ec8732014-03-31 15:00:14 +0100570 return true;
571}
572
573LIR* Arm64Mir2Lir::OpPcRelLoad(RegStorage reg, LIR* target) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100574 return RawLIR(current_dalvik_offset_, WIDE(kA64Ldr2rp), reg.GetReg(), 0, 0, 0, 0, target);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100575}
576
577LIR* Arm64Mir2Lir::OpVldm(RegStorage r_base, int count) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100578 LOG(FATAL) << "Unexpected use of OpVldm for Arm64";
579 return NULL;
Matteo Franchin43ec8732014-03-31 15:00:14 +0100580}
581
582LIR* Arm64Mir2Lir::OpVstm(RegStorage r_base, int count) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100583 LOG(FATAL) << "Unexpected use of OpVstm for Arm64";
584 return NULL;
Matteo Franchin43ec8732014-03-31 15:00:14 +0100585}
586
587void Arm64Mir2Lir::GenMultiplyByTwoBitMultiplier(RegLocation rl_src,
588 RegLocation rl_result, int lit,
589 int first_bit, int second_bit) {
Serban Constantinescued65c5e2014-05-22 15:10:18 +0100590 OpRegRegRegShift(kOpAdd, rl_result.reg, rl_src.reg, rl_src.reg, EncodeShift(kA64Lsl, second_bit - first_bit));
Matteo Franchin43ec8732014-03-31 15:00:14 +0100591 if (first_bit != 0) {
592 OpRegRegImm(kOpLsl, rl_result.reg, rl_result.reg, first_bit);
593 }
594}
595
596void Arm64Mir2Lir::GenDivZeroCheckWide(RegStorage reg) {
Serban Constantinescued65c5e2014-05-22 15:10:18 +0100597 LOG(FATAL) << "Unexpected use of GenDivZero for Arm64";
Matteo Franchin43ec8732014-03-31 15:00:14 +0100598}
599
600// Test suspend flag, return target of taken suspend branch
601LIR* Arm64Mir2Lir::OpTestSuspend(LIR* target) {
Zheng Xubaa7c882014-06-30 14:26:50 +0800602 NewLIR3(kA64Subs3rRd, rwSUSPEND, rwSUSPEND, 1);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100603 return OpCondBranch((target == NULL) ? kCondEq : kCondNe, target);
604}
605
606// Decrement register and branch on condition
607LIR* Arm64Mir2Lir::OpDecAndBranch(ConditionCode c_code, RegStorage reg, LIR* target) {
buzbee33ae5582014-06-12 14:56:32 -0700608 // Combine sub & test using sub setflags encoding here. We need to make sure a
609 // subtract form that sets carry is used, so generate explicitly.
610 // TODO: might be best to add a new op, kOpSubs, and handle it generically.
611 ArmOpcode opcode = reg.Is64Bit() ? WIDE(kA64Subs3rRd) : UNWIDE(kA64Subs3rRd);
612 NewLIR3(opcode, reg.GetReg(), reg.GetReg(), 1); // For value == 1, this should set flags.
Vladimir Marko8dea81c2014-06-06 14:50:36 +0100613 DCHECK(last_lir_insn_->u.m.def_mask->HasBit(ResourceMask::kCCode));
Matteo Franchin43ec8732014-03-31 15:00:14 +0100614 return OpCondBranch(c_code, target);
615}
616
Andreas Gampeb14329f2014-05-15 11:16:06 -0700617bool Arm64Mir2Lir::GenMemBarrier(MemBarrierKind barrier_kind) {
Matteo Franchin43ec8732014-03-31 15:00:14 +0100618#if ANDROID_SMP != 0
619 // Start off with using the last LIR as the barrier. If it is not enough, then we will generate one.
620 LIR* barrier = last_lir_insn_;
621
622 int dmb_flavor;
623 // TODO: revisit Arm barrier kinds
624 switch (barrier_kind) {
625 case kLoadStore: dmb_flavor = kISH; break;
626 case kLoadLoad: dmb_flavor = kISH; break;
627 case kStoreStore: dmb_flavor = kISHST; break;
628 case kStoreLoad: dmb_flavor = kISH; break;
629 default:
630 LOG(FATAL) << "Unexpected MemBarrierKind: " << barrier_kind;
631 dmb_flavor = kSY; // quiet gcc.
632 break;
633 }
634
Andreas Gampeb14329f2014-05-15 11:16:06 -0700635 bool ret = false;
636
Matteo Franchin43ec8732014-03-31 15:00:14 +0100637 // If the same barrier already exists, don't generate another.
638 if (barrier == nullptr
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100639 || (barrier->opcode != kA64Dmb1B || barrier->operands[0] != dmb_flavor)) {
640 barrier = NewLIR1(kA64Dmb1B, dmb_flavor);
Andreas Gampeb14329f2014-05-15 11:16:06 -0700641 ret = true;
Matteo Franchin43ec8732014-03-31 15:00:14 +0100642 }
643
644 // At this point we must have a memory barrier. Mark it as a scheduling barrier as well.
645 DCHECK(!barrier->flags.use_def_invalid);
Vladimir Marko8dea81c2014-06-06 14:50:36 +0100646 barrier->u.m.def_mask = &kEncodeAll;
Andreas Gampeb14329f2014-05-15 11:16:06 -0700647 return ret;
648#else
649 return false;
Matteo Franchin43ec8732014-03-31 15:00:14 +0100650#endif
651}
652
Serban Constantinescued65c5e2014-05-22 15:10:18 +0100653void Arm64Mir2Lir::GenIntToLong(RegLocation rl_dest, RegLocation rl_src) {
654 RegLocation rl_result;
655
656 rl_src = LoadValue(rl_src, kCoreReg);
657 rl_result = EvalLocWide(rl_dest, kCoreReg, true);
658 NewLIR4(WIDE(kA64Sbfm4rrdd), rl_result.reg.GetReg(), rl_src.reg.GetReg(), 0, 31);
659 StoreValueWide(rl_dest, rl_result);
660}
661
662void Arm64Mir2Lir::GenDivRemLong(Instruction::Code opcode, RegLocation rl_dest,
663 RegLocation rl_src1, RegLocation rl_src2, bool is_div) {
664 RegLocation rl_result;
665 rl_src1 = LoadValueWide(rl_src1, kCoreReg);
666 rl_src2 = LoadValueWide(rl_src2, kCoreReg);
667 GenDivZeroCheck(rl_src2.reg);
668 rl_result = GenDivRem(rl_dest, rl_src1.reg, rl_src2.reg, is_div);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100669 StoreValueWide(rl_dest, rl_result);
670}
671
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100672void Arm64Mir2Lir::GenLongOp(OpKind op, RegLocation rl_dest, RegLocation rl_src1,
673 RegLocation rl_src2) {
674 RegLocation rl_result;
Serban Constantinescued65c5e2014-05-22 15:10:18 +0100675
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100676 rl_src1 = LoadValueWide(rl_src1, kCoreReg);
677 rl_src2 = LoadValueWide(rl_src2, kCoreReg);
678 rl_result = EvalLocWide(rl_dest, kCoreReg, true);
Serban Constantinescued65c5e2014-05-22 15:10:18 +0100679 OpRegRegRegShift(op, rl_result.reg, rl_src1.reg, rl_src2.reg, ENCODE_NO_SHIFT);
680 StoreValueWide(rl_dest, rl_result);
681}
682
683void Arm64Mir2Lir::GenNegLong(RegLocation rl_dest, RegLocation rl_src) {
684 RegLocation rl_result;
685
686 rl_src = LoadValueWide(rl_src, kCoreReg);
687 rl_result = EvalLocWide(rl_dest, kCoreReg, true);
688 OpRegRegShift(kOpNeg, rl_result.reg, rl_src.reg, ENCODE_NO_SHIFT);
689 StoreValueWide(rl_dest, rl_result);
690}
691
692void Arm64Mir2Lir::GenNotLong(RegLocation rl_dest, RegLocation rl_src) {
693 RegLocation rl_result;
694
695 rl_src = LoadValueWide(rl_src, kCoreReg);
696 rl_result = EvalLocWide(rl_dest, kCoreReg, true);
697 OpRegRegShift(kOpMvn, rl_result.reg, rl_src.reg, ENCODE_NO_SHIFT);
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100698 StoreValueWide(rl_dest, rl_result);
699}
700
Matteo Franchin43ec8732014-03-31 15:00:14 +0100701void Arm64Mir2Lir::GenMulLong(Instruction::Code opcode, RegLocation rl_dest,
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100702 RegLocation rl_src1, RegLocation rl_src2) {
703 GenLongOp(kOpMul, rl_dest, rl_src1, rl_src2);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100704}
705
706void Arm64Mir2Lir::GenAddLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1,
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100707 RegLocation rl_src2) {
708 GenLongOp(kOpAdd, rl_dest, rl_src1, rl_src2);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100709}
710
711void Arm64Mir2Lir::GenSubLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1,
712 RegLocation rl_src2) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100713 GenLongOp(kOpSub, rl_dest, rl_src1, rl_src2);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100714}
715
716void Arm64Mir2Lir::GenAndLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1,
717 RegLocation rl_src2) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100718 GenLongOp(kOpAnd, rl_dest, rl_src1, rl_src2);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100719}
720
721void Arm64Mir2Lir::GenOrLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1,
722 RegLocation rl_src2) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100723 GenLongOp(kOpOr, rl_dest, rl_src1, rl_src2);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100724}
725
726void Arm64Mir2Lir::GenXorLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1,
727 RegLocation rl_src2) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100728 GenLongOp(kOpXor, rl_dest, rl_src1, rl_src2);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100729}
730
731/*
732 * Generate array load
733 */
734void Arm64Mir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array,
735 RegLocation rl_index, RegLocation rl_dest, int scale) {
736 RegisterClass reg_class = RegClassBySize(size);
737 int len_offset = mirror::Array::LengthOffset().Int32Value();
738 int data_offset;
739 RegLocation rl_result;
740 bool constant_index = rl_index.is_const;
buzbeea0cd2d72014-06-01 09:33:49 -0700741 rl_array = LoadValue(rl_array, kRefReg);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100742 if (!constant_index) {
743 rl_index = LoadValue(rl_index, kCoreReg);
744 }
745
746 if (rl_dest.wide) {
747 data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Int32Value();
748 } else {
749 data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Int32Value();
750 }
751
752 // If index is constant, just fold it into the data offset
753 if (constant_index) {
754 data_offset += mir_graph_->ConstantValue(rl_index) << scale;
755 }
756
757 /* null object? */
758 GenNullCheck(rl_array.reg, opt_flags);
759
760 bool needs_range_check = (!(opt_flags & MIR_IGNORE_RANGE_CHECK));
761 RegStorage reg_len;
762 if (needs_range_check) {
763 reg_len = AllocTemp();
764 /* Get len */
765 Load32Disp(rl_array.reg, len_offset, reg_len);
766 MarkPossibleNullPointerException(opt_flags);
767 } else {
768 ForceImplicitNullCheck(rl_array.reg, opt_flags);
769 }
770 if (rl_dest.wide || rl_dest.fp || constant_index) {
771 RegStorage reg_ptr;
772 if (constant_index) {
773 reg_ptr = rl_array.reg; // NOTE: must not alter reg_ptr in constant case.
774 } else {
775 // No special indexed operation, lea + load w/ displacement
buzbeea0cd2d72014-06-01 09:33:49 -0700776 reg_ptr = AllocTempRef();
buzbee33ae5582014-06-12 14:56:32 -0700777 OpRegRegRegShift(kOpAdd, reg_ptr, rl_array.reg, As64BitReg(rl_index.reg),
778 EncodeShift(kA64Lsl, scale));
Matteo Franchin43ec8732014-03-31 15:00:14 +0100779 FreeTemp(rl_index.reg);
780 }
781 rl_result = EvalLoc(rl_dest, reg_class, true);
782
783 if (needs_range_check) {
784 if (constant_index) {
785 GenArrayBoundsCheck(mir_graph_->ConstantValue(rl_index), reg_len);
786 } else {
787 GenArrayBoundsCheck(rl_index.reg, reg_len);
788 }
789 FreeTemp(reg_len);
790 }
Andreas Gampe3c12c512014-06-24 18:46:29 +0000791 if (rl_result.ref) {
792 LoadRefDisp(reg_ptr, data_offset, rl_result.reg, kNotVolatile);
793 } else {
794 LoadBaseDisp(reg_ptr, data_offset, rl_result.reg, size, kNotVolatile);
795 }
Vladimir Marko455759b2014-05-06 20:49:36 +0100796 MarkPossibleNullPointerException(opt_flags);
797 if (!constant_index) {
798 FreeTemp(reg_ptr);
799 }
Matteo Franchin43ec8732014-03-31 15:00:14 +0100800 if (rl_dest.wide) {
Matteo Franchin43ec8732014-03-31 15:00:14 +0100801 StoreValueWide(rl_dest, rl_result);
802 } else {
Matteo Franchin43ec8732014-03-31 15:00:14 +0100803 StoreValue(rl_dest, rl_result);
804 }
805 } else {
806 // Offset base, then use indexed load
buzbeea0cd2d72014-06-01 09:33:49 -0700807 RegStorage reg_ptr = AllocTempRef();
Matteo Franchin43ec8732014-03-31 15:00:14 +0100808 OpRegRegImm(kOpAdd, reg_ptr, rl_array.reg, data_offset);
809 FreeTemp(rl_array.reg);
810 rl_result = EvalLoc(rl_dest, reg_class, true);
811
812 if (needs_range_check) {
813 GenArrayBoundsCheck(rl_index.reg, reg_len);
814 FreeTemp(reg_len);
815 }
Andreas Gampe3c12c512014-06-24 18:46:29 +0000816 if (rl_result.ref) {
817 LoadRefIndexed(reg_ptr, As64BitReg(rl_index.reg), rl_result.reg);
818 } else {
819 LoadBaseIndexed(reg_ptr, As64BitReg(rl_index.reg), rl_result.reg, scale, size);
820 }
Matteo Franchin43ec8732014-03-31 15:00:14 +0100821 MarkPossibleNullPointerException(opt_flags);
822 FreeTemp(reg_ptr);
823 StoreValue(rl_dest, rl_result);
824 }
825}
826
827/*
828 * Generate array store
829 *
830 */
831void Arm64Mir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array,
832 RegLocation rl_index, RegLocation rl_src, int scale, bool card_mark) {
833 RegisterClass reg_class = RegClassBySize(size);
834 int len_offset = mirror::Array::LengthOffset().Int32Value();
835 bool constant_index = rl_index.is_const;
836
837 int data_offset;
838 if (size == k64 || size == kDouble) {
839 data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Int32Value();
840 } else {
841 data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Int32Value();
842 }
843
844 // If index is constant, just fold it into the data offset.
845 if (constant_index) {
846 data_offset += mir_graph_->ConstantValue(rl_index) << scale;
847 }
848
buzbeea0cd2d72014-06-01 09:33:49 -0700849 rl_array = LoadValue(rl_array, kRefReg);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100850 if (!constant_index) {
851 rl_index = LoadValue(rl_index, kCoreReg);
852 }
853
854 RegStorage reg_ptr;
855 bool allocated_reg_ptr_temp = false;
856 if (constant_index) {
857 reg_ptr = rl_array.reg;
858 } else if (IsTemp(rl_array.reg) && !card_mark) {
859 Clobber(rl_array.reg);
860 reg_ptr = rl_array.reg;
861 } else {
862 allocated_reg_ptr_temp = true;
buzbeea0cd2d72014-06-01 09:33:49 -0700863 reg_ptr = AllocTempRef();
Matteo Franchin43ec8732014-03-31 15:00:14 +0100864 }
865
866 /* null object? */
867 GenNullCheck(rl_array.reg, opt_flags);
868
869 bool needs_range_check = (!(opt_flags & MIR_IGNORE_RANGE_CHECK));
870 RegStorage reg_len;
871 if (needs_range_check) {
872 reg_len = AllocTemp();
873 // NOTE: max live temps(4) here.
874 /* Get len */
875 Load32Disp(rl_array.reg, len_offset, reg_len);
876 MarkPossibleNullPointerException(opt_flags);
877 } else {
878 ForceImplicitNullCheck(rl_array.reg, opt_flags);
879 }
880 /* at this point, reg_ptr points to array, 2 live temps */
881 if (rl_src.wide || rl_src.fp || constant_index) {
882 if (rl_src.wide) {
883 rl_src = LoadValueWide(rl_src, reg_class);
884 } else {
885 rl_src = LoadValue(rl_src, reg_class);
886 }
887 if (!constant_index) {
buzbee33ae5582014-06-12 14:56:32 -0700888 OpRegRegRegShift(kOpAdd, reg_ptr, rl_array.reg, As64BitReg(rl_index.reg),
889 EncodeShift(kA64Lsl, scale));
Matteo Franchin43ec8732014-03-31 15:00:14 +0100890 }
891 if (needs_range_check) {
892 if (constant_index) {
893 GenArrayBoundsCheck(mir_graph_->ConstantValue(rl_index), reg_len);
894 } else {
895 GenArrayBoundsCheck(rl_index.reg, reg_len);
896 }
897 FreeTemp(reg_len);
898 }
Andreas Gampe3c12c512014-06-24 18:46:29 +0000899 if (rl_src.ref) {
900 StoreRefDisp(reg_ptr, data_offset, rl_src.reg, kNotVolatile);
901 } else {
902 StoreBaseDisp(reg_ptr, data_offset, rl_src.reg, size, kNotVolatile);
903 }
Matteo Franchin43ec8732014-03-31 15:00:14 +0100904 MarkPossibleNullPointerException(opt_flags);
905 } else {
906 /* reg_ptr -> array data */
907 OpRegRegImm(kOpAdd, reg_ptr, rl_array.reg, data_offset);
908 rl_src = LoadValue(rl_src, reg_class);
909 if (needs_range_check) {
910 GenArrayBoundsCheck(rl_index.reg, reg_len);
911 FreeTemp(reg_len);
912 }
Andreas Gampe3c12c512014-06-24 18:46:29 +0000913 if (rl_src.ref) {
914 StoreRefIndexed(reg_ptr, As64BitReg(rl_index.reg), rl_src.reg);
915 } else {
916 StoreBaseIndexed(reg_ptr, As64BitReg(rl_index.reg), rl_src.reg, scale, size);
917 }
Matteo Franchin43ec8732014-03-31 15:00:14 +0100918 MarkPossibleNullPointerException(opt_flags);
919 }
920 if (allocated_reg_ptr_temp) {
921 FreeTemp(reg_ptr);
922 }
923 if (card_mark) {
924 MarkGCCard(rl_src.reg, rl_array.reg);
925 }
926}
927
Matteo Franchin43ec8732014-03-31 15:00:14 +0100928void Arm64Mir2Lir::GenShiftImmOpLong(Instruction::Code opcode,
929 RegLocation rl_dest, RegLocation rl_src, RegLocation rl_shift) {
Serban Constantinescued65c5e2014-05-22 15:10:18 +0100930 OpKind op = kOpBkpt;
Matteo Franchin43ec8732014-03-31 15:00:14 +0100931 // Per spec, we only care about low 6 bits of shift amount.
932 int shift_amount = mir_graph_->ConstantValue(rl_shift) & 0x3f;
Serban Constantinescued65c5e2014-05-22 15:10:18 +0100933 rl_src = LoadValueWide(rl_src, kCoreReg);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100934 if (shift_amount == 0) {
935 StoreValueWide(rl_dest, rl_src);
936 return;
937 }
Serban Constantinescued65c5e2014-05-22 15:10:18 +0100938
939 RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100940 switch (opcode) {
941 case Instruction::SHL_LONG:
942 case Instruction::SHL_LONG_2ADDR:
Serban Constantinescued65c5e2014-05-22 15:10:18 +0100943 op = kOpLsl;
Matteo Franchin43ec8732014-03-31 15:00:14 +0100944 break;
945 case Instruction::SHR_LONG:
946 case Instruction::SHR_LONG_2ADDR:
Serban Constantinescued65c5e2014-05-22 15:10:18 +0100947 op = kOpAsr;
Matteo Franchin43ec8732014-03-31 15:00:14 +0100948 break;
949 case Instruction::USHR_LONG:
950 case Instruction::USHR_LONG_2ADDR:
Serban Constantinescued65c5e2014-05-22 15:10:18 +0100951 op = kOpLsr;
Matteo Franchin43ec8732014-03-31 15:00:14 +0100952 break;
953 default:
954 LOG(FATAL) << "Unexpected case";
955 }
Serban Constantinescued65c5e2014-05-22 15:10:18 +0100956 OpRegRegImm(op, rl_result.reg, rl_src.reg, shift_amount);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100957 StoreValueWide(rl_dest, rl_result);
958}
959
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100960void Arm64Mir2Lir::GenArithImmOpLong(Instruction::Code opcode, RegLocation rl_dest,
961 RegLocation rl_src1, RegLocation rl_src2) {
Serban Constantinescued65c5e2014-05-22 15:10:18 +0100962 if ((opcode == Instruction::SUB_LONG) || (opcode == Instruction::SUB_LONG_2ADDR)) {
Matteo Franchin43ec8732014-03-31 15:00:14 +0100963 if (!rl_src2.is_const) {
Serban Constantinescued65c5e2014-05-22 15:10:18 +0100964 return GenArithOpLong(opcode, rl_dest, rl_src1, rl_src2);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100965 }
966 } else {
Serban Constantinescued65c5e2014-05-22 15:10:18 +0100967 // Associativity.
Matteo Franchin43ec8732014-03-31 15:00:14 +0100968 if (!rl_src2.is_const) {
969 DCHECK(rl_src1.is_const);
970 std::swap(rl_src1, rl_src2);
971 }
972 }
Matteo Franchin43ec8732014-03-31 15:00:14 +0100973 DCHECK(rl_src2.is_const);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100974
Serban Constantinescued65c5e2014-05-22 15:10:18 +0100975 OpKind op = kOpBkpt;
976 int64_t val = mir_graph_->ConstantValueWide(rl_src2);
977
Matteo Franchin43ec8732014-03-31 15:00:14 +0100978 switch (opcode) {
979 case Instruction::ADD_LONG:
980 case Instruction::ADD_LONG_2ADDR:
Serban Constantinescued65c5e2014-05-22 15:10:18 +0100981 op = kOpAdd;
982 break;
Matteo Franchin43ec8732014-03-31 15:00:14 +0100983 case Instruction::SUB_LONG:
984 case Instruction::SUB_LONG_2ADDR:
Serban Constantinescued65c5e2014-05-22 15:10:18 +0100985 op = kOpSub;
Matteo Franchin43ec8732014-03-31 15:00:14 +0100986 break;
987 case Instruction::AND_LONG:
988 case Instruction::AND_LONG_2ADDR:
Serban Constantinescued65c5e2014-05-22 15:10:18 +0100989 op = kOpAnd;
Matteo Franchin43ec8732014-03-31 15:00:14 +0100990 break;
Serban Constantinescued65c5e2014-05-22 15:10:18 +0100991 case Instruction::OR_LONG:
992 case Instruction::OR_LONG_2ADDR:
993 op = kOpOr;
Matteo Franchin43ec8732014-03-31 15:00:14 +0100994 break;
Serban Constantinescued65c5e2014-05-22 15:10:18 +0100995 case Instruction::XOR_LONG:
996 case Instruction::XOR_LONG_2ADDR:
997 op = kOpXor;
998 break;
Matteo Franchin43ec8732014-03-31 15:00:14 +0100999 default:
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001000 LOG(FATAL) << "Unexpected opcode";
Matteo Franchin43ec8732014-03-31 15:00:14 +01001001 }
Serban Constantinescued65c5e2014-05-22 15:10:18 +01001002
1003 rl_src1 = LoadValueWide(rl_src1, kCoreReg);
1004 RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true);
Zheng Xue2eb29e2014-06-12 10:22:33 +08001005 OpRegRegImm64(op, rl_result.reg, rl_src1.reg, val);
Matteo Franchin43ec8732014-03-31 15:00:14 +01001006 StoreValueWide(rl_dest, rl_result);
1007}
1008
Matteo Franchine45fb9e2014-05-06 10:10:30 +01001009/**
1010 * @brief Split a register list in pairs or registers.
1011 *
1012 * Given a list of registers in @p reg_mask, split the list in pairs. Use as follows:
1013 * @code
1014 * int reg1 = -1, reg2 = -1;
1015 * while (reg_mask) {
1016 * reg_mask = GenPairWise(reg_mask, & reg1, & reg2);
1017 * if (UNLIKELY(reg2 < 0)) {
1018 * // Single register in reg1.
1019 * } else {
1020 * // Pair in reg1, reg2.
1021 * }
1022 * }
1023 * @endcode
1024 */
1025uint32_t Arm64Mir2Lir::GenPairWise(uint32_t reg_mask, int* reg1, int* reg2) {
1026 // Find first register.
1027 int first_bit_set = __builtin_ctz(reg_mask) + 1;
1028 int reg = *reg1 + first_bit_set;
1029 reg_mask >>= first_bit_set;
1030
1031 if (LIKELY(reg_mask)) {
1032 // Save the first register, find the second and use the pair opcode.
1033 int second_bit_set = __builtin_ctz(reg_mask) + 1;
1034 *reg2 = reg;
1035 reg_mask >>= second_bit_set;
1036 *reg1 = reg + second_bit_set;
1037 return reg_mask;
1038 }
1039
1040 // Use the single opcode, as we just have one register.
1041 *reg1 = reg;
1042 *reg2 = -1;
1043 return reg_mask;
1044}
1045
1046void Arm64Mir2Lir::UnSpillCoreRegs(RegStorage base, int offset, uint32_t reg_mask) {
1047 int reg1 = -1, reg2 = -1;
Matteo Franchinbc6d1972014-05-13 12:33:28 +01001048 const int reg_log2_size = 3;
Matteo Franchine45fb9e2014-05-06 10:10:30 +01001049
Matteo Franchinbc6d1972014-05-13 12:33:28 +01001050 for (offset = (offset >> reg_log2_size); reg_mask; offset += 2) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +01001051 reg_mask = GenPairWise(reg_mask, & reg1, & reg2);
1052 if (UNLIKELY(reg2 < 0)) {
Matteo Franchinbc6d1972014-05-13 12:33:28 +01001053 NewLIR3(WIDE(kA64Ldr3rXD), RegStorage::Solo64(reg1).GetReg(), base.GetReg(), offset);
Matteo Franchine45fb9e2014-05-06 10:10:30 +01001054 } else {
Matteo Franchinbc6d1972014-05-13 12:33:28 +01001055 NewLIR4(WIDE(kA64Ldp4rrXD), RegStorage::Solo64(reg2).GetReg(),
1056 RegStorage::Solo64(reg1).GetReg(), base.GetReg(), offset);
Matteo Franchine45fb9e2014-05-06 10:10:30 +01001057 }
1058 }
1059}
1060
1061void Arm64Mir2Lir::SpillCoreRegs(RegStorage base, int offset, uint32_t reg_mask) {
1062 int reg1 = -1, reg2 = -1;
Matteo Franchinbc6d1972014-05-13 12:33:28 +01001063 const int reg_log2_size = 3;
Matteo Franchine45fb9e2014-05-06 10:10:30 +01001064
Matteo Franchinbc6d1972014-05-13 12:33:28 +01001065 for (offset = (offset >> reg_log2_size); reg_mask; offset += 2) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +01001066 reg_mask = GenPairWise(reg_mask, & reg1, & reg2);
1067 if (UNLIKELY(reg2 < 0)) {
Matteo Franchinbc6d1972014-05-13 12:33:28 +01001068 NewLIR3(WIDE(kA64Str3rXD), RegStorage::Solo64(reg1).GetReg(), base.GetReg(), offset);
Matteo Franchine45fb9e2014-05-06 10:10:30 +01001069 } else {
Matteo Franchinbc6d1972014-05-13 12:33:28 +01001070 NewLIR4(WIDE(kA64Stp4rrXD), RegStorage::Solo64(reg2).GetReg(),
1071 RegStorage::Solo64(reg1).GetReg(), base.GetReg(), offset);
1072 }
1073 }
1074}
1075
1076void Arm64Mir2Lir::UnSpillFPRegs(RegStorage base, int offset, uint32_t reg_mask) {
1077 int reg1 = -1, reg2 = -1;
1078 const int reg_log2_size = 3;
1079
1080 for (offset = (offset >> reg_log2_size); reg_mask; offset += 2) {
1081 reg_mask = GenPairWise(reg_mask, & reg1, & reg2);
1082 if (UNLIKELY(reg2 < 0)) {
1083 NewLIR3(FWIDE(kA64Ldr3fXD), RegStorage::FloatSolo64(reg1).GetReg(), base.GetReg(), offset);
1084 } else {
1085 NewLIR4(WIDE(kA64Ldp4ffXD), RegStorage::FloatSolo64(reg2).GetReg(),
1086 RegStorage::FloatSolo64(reg1).GetReg(), base.GetReg(), offset);
1087 }
1088 }
1089}
1090
1091// TODO(Arm64): consider using ld1 and st1?
1092void Arm64Mir2Lir::SpillFPRegs(RegStorage base, int offset, uint32_t reg_mask) {
1093 int reg1 = -1, reg2 = -1;
1094 const int reg_log2_size = 3;
1095
1096 for (offset = (offset >> reg_log2_size); reg_mask; offset += 2) {
1097 reg_mask = GenPairWise(reg_mask, & reg1, & reg2);
1098 if (UNLIKELY(reg2 < 0)) {
1099 NewLIR3(FWIDE(kA64Str3fXD), RegStorage::FloatSolo64(reg1).GetReg(), base.GetReg(), offset);
1100 } else {
1101 NewLIR4(WIDE(kA64Stp4ffXD), RegStorage::FloatSolo64(reg2).GetReg(),
1102 RegStorage::FloatSolo64(reg1).GetReg(), base.GetReg(), offset);
Matteo Franchine45fb9e2014-05-06 10:10:30 +01001103 }
1104 }
1105}
1106
Matteo Franchin43ec8732014-03-31 15:00:14 +01001107} // namespace art