blob: 9b0fa62b329bb2efc27cc28ea01bd27bcf15c0da [file] [log] [blame]
Brian Carlstrom7940e442013-07-12 13:46:57 -07001/*
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 "arm_lir.h"
20#include "codegen_arm.h"
21#include "dex/quick/mir_to_lir-inl.h"
Ian Rogers166db042013-07-26 12:05:57 -070022#include "entrypoints/quick/quick_entrypoints.h"
Brian Carlstrom7940e442013-07-12 13:46:57 -070023#include "mirror/array.h"
Brian Carlstrom7940e442013-07-12 13:46:57 -070024
25namespace art {
26
27LIR* ArmMir2Lir::OpCmpBranch(ConditionCode cond, int src1,
Brian Carlstrom2ce745c2013-07-17 17:44:30 -070028 int src2, LIR* target) {
Brian Carlstrom7940e442013-07-12 13:46:57 -070029 OpRegReg(kOpCmp, src1, src2);
30 return OpCondBranch(cond, target);
31}
32
33/*
34 * Generate a Thumb2 IT instruction, which can nullify up to
35 * four subsequent instructions based on a condition and its
36 * inverse. The condition applies to the first instruction, which
37 * is executed if the condition is met. The string "guide" consists
38 * of 0 to 3 chars, and applies to the 2nd through 4th instruction.
39 * A "T" means the instruction is executed if the condition is
40 * met, and an "E" means the instruction is executed if the condition
41 * is not met.
42 */
Brian Carlstrom2ce745c2013-07-17 17:44:30 -070043LIR* ArmMir2Lir::OpIT(ConditionCode ccode, const char* guide) {
Brian Carlstrom7940e442013-07-12 13:46:57 -070044 int mask;
45 int mask3 = 0;
46 int mask2 = 0;
47 int mask1 = 0;
48 ArmConditionCode code = ArmConditionEncoding(ccode);
49 int cond_bit = code & 1;
50 int alt_bit = cond_bit ^ 1;
51
Brian Carlstrom7934ac22013-07-26 10:54:15 -070052 // Note: case fallthroughs intentional
Brian Carlstrom7940e442013-07-12 13:46:57 -070053 switch (strlen(guide)) {
54 case 3:
55 mask1 = (guide[2] == 'T') ? cond_bit : alt_bit;
56 case 2:
57 mask2 = (guide[1] == 'T') ? cond_bit : alt_bit;
58 case 1:
59 mask3 = (guide[0] == 'T') ? cond_bit : alt_bit;
60 break;
61 case 0:
62 break;
63 default:
64 LOG(FATAL) << "OAT: bad case in OpIT";
65 }
66 mask = (mask3 << 3) | (mask2 << 2) | (mask1 << 1) |
67 (1 << (3 - strlen(guide)));
68 return NewLIR2(kThumb2It, code, mask);
69}
70
71/*
72 * 64-bit 3way compare function.
73 * mov rX, #-1
74 * cmp op1hi, op2hi
75 * blt done
76 * bgt flip
77 * sub rX, op1lo, op2lo (treat as unsigned)
78 * beq done
79 * ite hi
80 * mov(hi) rX, #-1
81 * mov(!hi) rX, #1
82 * flip:
83 * neg rX
84 * done:
85 */
86void ArmMir2Lir::GenCmpLong(RegLocation rl_dest, RegLocation rl_src1,
Brian Carlstrom2ce745c2013-07-17 17:44:30 -070087 RegLocation rl_src2) {
Brian Carlstrom7940e442013-07-12 13:46:57 -070088 LIR* target1;
89 LIR* target2;
90 rl_src1 = LoadValueWide(rl_src1, kCoreReg);
91 rl_src2 = LoadValueWide(rl_src2, kCoreReg);
92 int t_reg = AllocTemp();
93 LoadConstant(t_reg, -1);
94 OpRegReg(kOpCmp, rl_src1.high_reg, rl_src2.high_reg);
95 LIR* branch1 = OpCondBranch(kCondLt, NULL);
96 LIR* branch2 = OpCondBranch(kCondGt, NULL);
97 OpRegRegReg(kOpSub, t_reg, rl_src1.low_reg, rl_src2.low_reg);
98 LIR* branch3 = OpCondBranch(kCondEq, NULL);
99
100 OpIT(kCondHi, "E");
101 NewLIR2(kThumb2MovImmShift, t_reg, ModifiedImmediate(-1));
102 LoadConstant(t_reg, 1);
103 GenBarrier();
104
105 target2 = NewLIR0(kPseudoTargetLabel);
106 OpRegReg(kOpNeg, t_reg, t_reg);
107
108 target1 = NewLIR0(kPseudoTargetLabel);
109
Brian Carlstrom7934ac22013-07-26 10:54:15 -0700110 RegLocation rl_temp = LocCReturn(); // Just using as template, will change
Brian Carlstrom7940e442013-07-12 13:46:57 -0700111 rl_temp.low_reg = t_reg;
112 StoreValue(rl_dest, rl_temp);
113 FreeTemp(t_reg);
114
115 branch1->target = target1;
116 branch2->target = target2;
117 branch3->target = branch1->target;
118}
119
120void ArmMir2Lir::GenFusedLongCmpImmBranch(BasicBlock* bb, RegLocation rl_src1,
Brian Carlstrom2ce745c2013-07-17 17:44:30 -0700121 int64_t val, ConditionCode ccode) {
Brian Carlstrom7940e442013-07-12 13:46:57 -0700122 int32_t val_lo = Low32Bits(val);
123 int32_t val_hi = High32Bits(val);
Brian Carlstrom42748892013-07-18 18:04:08 -0700124 DCHECK_GE(ModifiedImmediate(val_lo), 0);
125 DCHECK_GE(ModifiedImmediate(val_hi), 0);
Brian Carlstrom7940e442013-07-12 13:46:57 -0700126 LIR* taken = &block_label_list_[bb->taken->id];
127 LIR* not_taken = &block_label_list_[bb->fall_through->id];
128 rl_src1 = LoadValueWide(rl_src1, kCoreReg);
129 int32_t low_reg = rl_src1.low_reg;
130 int32_t high_reg = rl_src1.high_reg;
131
Brian Carlstromdf629502013-07-17 22:39:56 -0700132 switch (ccode) {
Brian Carlstrom7940e442013-07-12 13:46:57 -0700133 case kCondEq:
134 case kCondNe:
135 LIR* target;
136 ConditionCode condition;
137 if (ccode == kCondEq) {
138 target = not_taken;
139 condition = kCondEq;
140 } else {
141 target = taken;
142 condition = kCondNe;
143 }
144 if (val == 0) {
145 int t_reg = AllocTemp();
146 NewLIR4(kThumb2OrrRRRs, t_reg, low_reg, high_reg, 0);
147 FreeTemp(t_reg);
148 OpCondBranch(condition, taken);
149 return;
150 }
151 OpCmpImmBranch(kCondNe, high_reg, val_hi, target);
152 break;
153 case kCondLt:
154 OpCmpImmBranch(kCondLt, high_reg, val_hi, taken);
155 OpCmpImmBranch(kCondGt, high_reg, val_hi, not_taken);
156 ccode = kCondCc;
157 break;
158 case kCondLe:
159 OpCmpImmBranch(kCondLt, high_reg, val_hi, taken);
160 OpCmpImmBranch(kCondGt, high_reg, val_hi, not_taken);
161 ccode = kCondLs;
162 break;
163 case kCondGt:
164 OpCmpImmBranch(kCondGt, high_reg, val_hi, taken);
165 OpCmpImmBranch(kCondLt, high_reg, val_hi, not_taken);
166 ccode = kCondHi;
167 break;
168 case kCondGe:
169 OpCmpImmBranch(kCondGt, high_reg, val_hi, taken);
170 OpCmpImmBranch(kCondLt, high_reg, val_hi, not_taken);
171 ccode = kCondCs;
172 break;
173 default:
174 LOG(FATAL) << "Unexpected ccode: " << ccode;
175 }
176 OpCmpImmBranch(ccode, low_reg, val_lo, taken);
177}
178
Brian Carlstrom2ce745c2013-07-17 17:44:30 -0700179void ArmMir2Lir::GenSelect(BasicBlock* bb, MIR* mir) {
Brian Carlstrom7940e442013-07-12 13:46:57 -0700180 RegLocation rl_result;
181 RegLocation rl_src = mir_graph_->GetSrc(mir, 0);
182 // Temporary debugging code
183 int dest_sreg = mir->ssa_rep->defs[0];
184 if ((dest_sreg < 0) || (dest_sreg >= mir_graph_->GetNumSSARegs())) {
185 LOG(INFO) << "Bad target sreg: " << dest_sreg << ", in "
Brian Carlstromb1eba212013-07-17 18:07:19 -0700186 << PrettyMethod(cu_->method_idx, *cu_->dex_file);
Brian Carlstrom7940e442013-07-12 13:46:57 -0700187 LOG(INFO) << "at dex offset 0x" << std::hex << mir->offset;
188 LOG(INFO) << "vreg = " << mir_graph_->SRegToVReg(dest_sreg);
189 LOG(INFO) << "num uses = " << mir->ssa_rep->num_uses;
190 if (mir->ssa_rep->num_uses == 1) {
191 LOG(INFO) << "CONST case, vals = " << mir->dalvikInsn.vB << ", " << mir->dalvikInsn.vC;
192 } else {
193 LOG(INFO) << "MOVE case, operands = " << mir->ssa_rep->uses[1] << ", "
194 << mir->ssa_rep->uses[2];
195 }
196 CHECK(false) << "Invalid target sreg on Select.";
197 }
198 // End temporary debugging code
199 RegLocation rl_dest = mir_graph_->GetDest(mir);
200 rl_src = LoadValue(rl_src, kCoreReg);
201 if (mir->ssa_rep->num_uses == 1) {
202 // CONST case
203 int true_val = mir->dalvikInsn.vB;
204 int false_val = mir->dalvikInsn.vC;
205 rl_result = EvalLoc(rl_dest, kCoreReg, true);
206 if ((true_val == 1) && (false_val == 0)) {
207 OpRegRegImm(kOpRsub, rl_result.low_reg, rl_src.low_reg, 1);
208 OpIT(kCondCc, "");
209 LoadConstant(rl_result.low_reg, 0);
Brian Carlstrom7934ac22013-07-26 10:54:15 -0700210 GenBarrier(); // Add a scheduling barrier to keep the IT shadow intact
Brian Carlstrom7940e442013-07-12 13:46:57 -0700211 } else if (InexpensiveConstantInt(true_val) && InexpensiveConstantInt(false_val)) {
212 OpRegImm(kOpCmp, rl_src.low_reg, 0);
213 OpIT(kCondEq, "E");
214 LoadConstant(rl_result.low_reg, true_val);
215 LoadConstant(rl_result.low_reg, false_val);
Brian Carlstrom7934ac22013-07-26 10:54:15 -0700216 GenBarrier(); // Add a scheduling barrier to keep the IT shadow intact
Brian Carlstrom7940e442013-07-12 13:46:57 -0700217 } else {
218 // Unlikely case - could be tuned.
219 int t_reg1 = AllocTemp();
220 int t_reg2 = AllocTemp();
221 LoadConstant(t_reg1, true_val);
222 LoadConstant(t_reg2, false_val);
223 OpRegImm(kOpCmp, rl_src.low_reg, 0);
224 OpIT(kCondEq, "E");
225 OpRegCopy(rl_result.low_reg, t_reg1);
226 OpRegCopy(rl_result.low_reg, t_reg2);
Brian Carlstrom7934ac22013-07-26 10:54:15 -0700227 GenBarrier(); // Add a scheduling barrier to keep the IT shadow intact
Brian Carlstrom7940e442013-07-12 13:46:57 -0700228 }
229 } else {
230 // MOVE case
231 RegLocation rl_true = mir_graph_->reg_location_[mir->ssa_rep->uses[1]];
232 RegLocation rl_false = mir_graph_->reg_location_[mir->ssa_rep->uses[2]];
233 rl_true = LoadValue(rl_true, kCoreReg);
234 rl_false = LoadValue(rl_false, kCoreReg);
235 rl_result = EvalLoc(rl_dest, kCoreReg, true);
236 OpRegImm(kOpCmp, rl_src.low_reg, 0);
buzbee252254b2013-09-08 16:20:53 -0700237 if (rl_result.low_reg == rl_true.low_reg) { // Is the "true" case already in place?
238 OpIT(kCondNe, "");
239 OpRegCopy(rl_result.low_reg, rl_false.low_reg);
240 } else if (rl_result.low_reg == rl_false.low_reg) { // False case in place?
241 OpIT(kCondEq, "");
242 OpRegCopy(rl_result.low_reg, rl_true.low_reg);
243 } else { // Normal - select between the two.
244 OpIT(kCondEq, "E");
245 OpRegCopy(rl_result.low_reg, rl_true.low_reg);
246 OpRegCopy(rl_result.low_reg, rl_false.low_reg);
247 }
Brian Carlstrom7934ac22013-07-26 10:54:15 -0700248 GenBarrier(); // Add a scheduling barrier to keep the IT shadow intact
Brian Carlstrom7940e442013-07-12 13:46:57 -0700249 }
250 StoreValue(rl_dest, rl_result);
251}
252
Brian Carlstrom2ce745c2013-07-17 17:44:30 -0700253void ArmMir2Lir::GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir) {
Brian Carlstrom7940e442013-07-12 13:46:57 -0700254 RegLocation rl_src1 = mir_graph_->GetSrcWide(mir, 0);
255 RegLocation rl_src2 = mir_graph_->GetSrcWide(mir, 2);
256 // Normalize such that if either operand is constant, src2 will be constant.
257 ConditionCode ccode = static_cast<ConditionCode>(mir->dalvikInsn.arg[0]);
258 if (rl_src1.is_const) {
259 RegLocation rl_temp = rl_src1;
260 rl_src1 = rl_src2;
261 rl_src2 = rl_temp;
262 ccode = FlipComparisonOrder(ccode);
263 }
264 if (rl_src2.is_const) {
265 RegLocation rl_temp = UpdateLocWide(rl_src2);
266 // Do special compare/branch against simple const operand if not already in registers.
267 int64_t val = mir_graph_->ConstantValueWide(rl_src2);
268 if ((rl_temp.location != kLocPhysReg) &&
269 ((ModifiedImmediate(Low32Bits(val)) >= 0) && (ModifiedImmediate(High32Bits(val)) >= 0))) {
270 GenFusedLongCmpImmBranch(bb, rl_src1, val, ccode);
271 return;
272 }
273 }
274 LIR* taken = &block_label_list_[bb->taken->id];
275 LIR* not_taken = &block_label_list_[bb->fall_through->id];
276 rl_src1 = LoadValueWide(rl_src1, kCoreReg);
277 rl_src2 = LoadValueWide(rl_src2, kCoreReg);
278 OpRegReg(kOpCmp, rl_src1.high_reg, rl_src2.high_reg);
Brian Carlstromdf629502013-07-17 22:39:56 -0700279 switch (ccode) {
Brian Carlstrom7940e442013-07-12 13:46:57 -0700280 case kCondEq:
281 OpCondBranch(kCondNe, not_taken);
282 break;
283 case kCondNe:
284 OpCondBranch(kCondNe, taken);
285 break;
286 case kCondLt:
287 OpCondBranch(kCondLt, taken);
288 OpCondBranch(kCondGt, not_taken);
289 ccode = kCondCc;
290 break;
291 case kCondLe:
292 OpCondBranch(kCondLt, taken);
293 OpCondBranch(kCondGt, not_taken);
294 ccode = kCondLs;
295 break;
296 case kCondGt:
297 OpCondBranch(kCondGt, taken);
298 OpCondBranch(kCondLt, not_taken);
299 ccode = kCondHi;
300 break;
301 case kCondGe:
302 OpCondBranch(kCondGt, taken);
303 OpCondBranch(kCondLt, not_taken);
304 ccode = kCondCs;
305 break;
306 default:
307 LOG(FATAL) << "Unexpected ccode: " << ccode;
308 }
309 OpRegReg(kOpCmp, rl_src1.low_reg, rl_src2.low_reg);
310 OpCondBranch(ccode, taken);
311}
312
313/*
314 * Generate a register comparison to an immediate and branch. Caller
315 * is responsible for setting branch target field.
316 */
317LIR* ArmMir2Lir::OpCmpImmBranch(ConditionCode cond, int reg, int check_value,
Brian Carlstrom2ce745c2013-07-17 17:44:30 -0700318 LIR* target) {
Brian Carlstrom7940e442013-07-12 13:46:57 -0700319 LIR* branch;
320 int mod_imm;
321 ArmConditionCode arm_cond = ArmConditionEncoding(cond);
buzbeeb48819d2013-09-14 16:15:25 -0700322 /*
323 * A common use of OpCmpImmBranch is for null checks, and using the Thumb 16-bit
324 * compare-and-branch if zero is ideal if it will reach. However, because null checks
325 * branch forward to a launch pad, they will frequently not reach - and thus have to
326 * be converted to a long form during assembly (which will trigger another assembly
327 * pass). Here we estimate the branch distance for checks, and if large directly
328 * generate the long form in an attempt to avoid an extra assembly pass.
329 * TODO: consider interspersing launchpads in code following unconditional branches.
330 */
331 bool skip = ((target != NULL) && (target->opcode == kPseudoThrowTarget));
332 skip &= ((cu_->code_item->insns_size_in_code_units_ - current_dalvik_offset_) > 64);
333 if (!skip && (ARM_LOWREG(reg)) && (check_value == 0) &&
Brian Carlstrom7940e442013-07-12 13:46:57 -0700334 ((arm_cond == kArmCondEq) || (arm_cond == kArmCondNe))) {
335 branch = NewLIR2((arm_cond == kArmCondEq) ? kThumb2Cbz : kThumb2Cbnz,
336 reg, 0);
337 } else {
338 mod_imm = ModifiedImmediate(check_value);
339 if (ARM_LOWREG(reg) && ((check_value & 0xff) == check_value)) {
340 NewLIR2(kThumbCmpRI8, reg, check_value);
341 } else if (mod_imm >= 0) {
342 NewLIR2(kThumb2CmpRI12, reg, mod_imm);
343 } else {
344 int t_reg = AllocTemp();
345 LoadConstant(t_reg, check_value);
346 OpRegReg(kOpCmp, reg, t_reg);
347 }
348 branch = NewLIR2(kThumbBCond, 0, arm_cond);
349 }
350 branch->target = target;
351 return branch;
352}
353
Brian Carlstrom2ce745c2013-07-17 17:44:30 -0700354LIR* ArmMir2Lir::OpRegCopyNoInsert(int r_dest, int r_src) {
Brian Carlstrom7940e442013-07-12 13:46:57 -0700355 LIR* res;
356 int opcode;
357 if (ARM_FPREG(r_dest) || ARM_FPREG(r_src))
358 return OpFpRegCopy(r_dest, r_src);
359 if (ARM_LOWREG(r_dest) && ARM_LOWREG(r_src))
360 opcode = kThumbMovRR;
361 else if (!ARM_LOWREG(r_dest) && !ARM_LOWREG(r_src))
362 opcode = kThumbMovRR_H2H;
363 else if (ARM_LOWREG(r_dest))
364 opcode = kThumbMovRR_H2L;
365 else
366 opcode = kThumbMovRR_L2H;
367 res = RawLIR(current_dalvik_offset_, opcode, r_dest, r_src);
368 if (!(cu_->disable_opt & (1 << kSafeOptimizations)) && r_dest == r_src) {
369 res->flags.is_nop = true;
370 }
371 return res;
372}
373
Brian Carlstrom2ce745c2013-07-17 17:44:30 -0700374LIR* ArmMir2Lir::OpRegCopy(int r_dest, int r_src) {
Brian Carlstrom7940e442013-07-12 13:46:57 -0700375 LIR* res = OpRegCopyNoInsert(r_dest, r_src);
376 AppendLIR(res);
377 return res;
378}
379
380void ArmMir2Lir::OpRegCopyWide(int dest_lo, int dest_hi, int src_lo,
Brian Carlstrom2ce745c2013-07-17 17:44:30 -0700381 int src_hi) {
Brian Carlstrom7940e442013-07-12 13:46:57 -0700382 bool dest_fp = ARM_FPREG(dest_lo) && ARM_FPREG(dest_hi);
383 bool src_fp = ARM_FPREG(src_lo) && ARM_FPREG(src_hi);
384 DCHECK_EQ(ARM_FPREG(src_lo), ARM_FPREG(src_hi));
385 DCHECK_EQ(ARM_FPREG(dest_lo), ARM_FPREG(dest_hi));
386 if (dest_fp) {
387 if (src_fp) {
388 OpRegCopy(S2d(dest_lo, dest_hi), S2d(src_lo, src_hi));
389 } else {
390 NewLIR3(kThumb2Fmdrr, S2d(dest_lo, dest_hi), src_lo, src_hi);
391 }
392 } else {
393 if (src_fp) {
394 NewLIR3(kThumb2Fmrrd, dest_lo, dest_hi, S2d(src_lo, src_hi));
395 } else {
396 // Handle overlap
397 if (src_hi == dest_lo) {
398 OpRegCopy(dest_hi, src_hi);
399 OpRegCopy(dest_lo, src_lo);
400 } else {
401 OpRegCopy(dest_lo, src_lo);
402 OpRegCopy(dest_hi, src_hi);
403 }
404 }
405 }
406}
407
408// Table of magic divisors
409struct MagicTable {
410 uint32_t magic;
411 uint32_t shift;
412 DividePattern pattern;
413};
414
415static const MagicTable magic_table[] = {
416 {0, 0, DivideNone}, // 0
417 {0, 0, DivideNone}, // 1
418 {0, 0, DivideNone}, // 2
419 {0x55555556, 0, Divide3}, // 3
420 {0, 0, DivideNone}, // 4
421 {0x66666667, 1, Divide5}, // 5
422 {0x2AAAAAAB, 0, Divide3}, // 6
423 {0x92492493, 2, Divide7}, // 7
424 {0, 0, DivideNone}, // 8
425 {0x38E38E39, 1, Divide5}, // 9
426 {0x66666667, 2, Divide5}, // 10
427 {0x2E8BA2E9, 1, Divide5}, // 11
428 {0x2AAAAAAB, 1, Divide5}, // 12
429 {0x4EC4EC4F, 2, Divide5}, // 13
430 {0x92492493, 3, Divide7}, // 14
431 {0x88888889, 3, Divide7}, // 15
432};
433
434// Integer division by constant via reciprocal multiply (Hacker's Delight, 10-4)
buzbee11b63d12013-08-27 07:34:17 -0700435bool ArmMir2Lir::SmallLiteralDivRem(Instruction::Code dalvik_opcode, bool is_div,
Brian Carlstrom2ce745c2013-07-17 17:44:30 -0700436 RegLocation rl_src, RegLocation rl_dest, int lit) {
Brian Carlstrom7940e442013-07-12 13:46:57 -0700437 if ((lit < 0) || (lit >= static_cast<int>(sizeof(magic_table)/sizeof(magic_table[0])))) {
438 return false;
439 }
440 DividePattern pattern = magic_table[lit].pattern;
441 if (pattern == DivideNone) {
442 return false;
443 }
444 // Tuning: add rem patterns
buzbee11b63d12013-08-27 07:34:17 -0700445 if (!is_div) {
Brian Carlstrom7940e442013-07-12 13:46:57 -0700446 return false;
447 }
448
449 int r_magic = AllocTemp();
450 LoadConstant(r_magic, magic_table[lit].magic);
451 rl_src = LoadValue(rl_src, kCoreReg);
452 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
453 int r_hi = AllocTemp();
454 int r_lo = AllocTemp();
455 NewLIR4(kThumb2Smull, r_lo, r_hi, r_magic, rl_src.low_reg);
Brian Carlstromdf629502013-07-17 22:39:56 -0700456 switch (pattern) {
Brian Carlstrom7940e442013-07-12 13:46:57 -0700457 case Divide3:
458 OpRegRegRegShift(kOpSub, rl_result.low_reg, r_hi,
459 rl_src.low_reg, EncodeShift(kArmAsr, 31));
460 break;
461 case Divide5:
462 OpRegRegImm(kOpAsr, r_lo, rl_src.low_reg, 31);
463 OpRegRegRegShift(kOpRsub, rl_result.low_reg, r_lo, r_hi,
464 EncodeShift(kArmAsr, magic_table[lit].shift));
465 break;
466 case Divide7:
467 OpRegReg(kOpAdd, r_hi, rl_src.low_reg);
468 OpRegRegImm(kOpAsr, r_lo, rl_src.low_reg, 31);
469 OpRegRegRegShift(kOpRsub, rl_result.low_reg, r_lo, r_hi,
470 EncodeShift(kArmAsr, magic_table[lit].shift));
471 break;
472 default:
473 LOG(FATAL) << "Unexpected pattern: " << pattern;
474 }
475 StoreValue(rl_dest, rl_result);
476 return true;
477}
478
479LIR* ArmMir2Lir::GenRegMemCheck(ConditionCode c_code,
Brian Carlstrom2ce745c2013-07-17 17:44:30 -0700480 int reg1, int base, int offset, ThrowKind kind) {
Brian Carlstrom7940e442013-07-12 13:46:57 -0700481 LOG(FATAL) << "Unexpected use of GenRegMemCheck for Arm";
482 return NULL;
483}
484
485RegLocation ArmMir2Lir::GenDivRemLit(RegLocation rl_dest, int reg1, int lit,
Brian Carlstrom2ce745c2013-07-17 17:44:30 -0700486 bool is_div) {
Brian Carlstrom7940e442013-07-12 13:46:57 -0700487 LOG(FATAL) << "Unexpected use of GenDivRemLit for Arm";
488 return rl_dest;
489}
490
491RegLocation ArmMir2Lir::GenDivRem(RegLocation rl_dest, int reg1, int reg2,
Brian Carlstrom2ce745c2013-07-17 17:44:30 -0700492 bool is_div) {
Brian Carlstrom7940e442013-07-12 13:46:57 -0700493 LOG(FATAL) << "Unexpected use of GenDivRem for Arm";
494 return rl_dest;
495}
496
Brian Carlstrom2ce745c2013-07-17 17:44:30 -0700497bool ArmMir2Lir::GenInlinedMinMaxInt(CallInfo* info, bool is_min) {
Brian Carlstrom7940e442013-07-12 13:46:57 -0700498 DCHECK_EQ(cu_->instruction_set, kThumb2);
499 RegLocation rl_src1 = info->args[0];
500 RegLocation rl_src2 = info->args[1];
501 rl_src1 = LoadValue(rl_src1, kCoreReg);
502 rl_src2 = LoadValue(rl_src2, kCoreReg);
503 RegLocation rl_dest = InlineTarget(info);
504 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
505 OpRegReg(kOpCmp, rl_src1.low_reg, rl_src2.low_reg);
506 OpIT((is_min) ? kCondGt : kCondLt, "E");
507 OpRegReg(kOpMov, rl_result.low_reg, rl_src2.low_reg);
508 OpRegReg(kOpMov, rl_result.low_reg, rl_src1.low_reg);
509 GenBarrier();
510 StoreValue(rl_dest, rl_result);
511 return true;
512}
513
Brian Carlstrom2ce745c2013-07-17 17:44:30 -0700514void ArmMir2Lir::OpLea(int rBase, int reg1, int reg2, int scale, int offset) {
Brian Carlstrom7940e442013-07-12 13:46:57 -0700515 LOG(FATAL) << "Unexpected use of OpLea for Arm";
516}
517
Ian Rogers468532e2013-08-05 10:56:33 -0700518void ArmMir2Lir::OpTlsCmp(ThreadOffset offset, int val) {
Brian Carlstrom7940e442013-07-12 13:46:57 -0700519 LOG(FATAL) << "Unexpected use of OpTlsCmp for Arm";
520}
521
522bool ArmMir2Lir::GenInlinedCas32(CallInfo* info, bool need_write_barrier) {
523 DCHECK_EQ(cu_->instruction_set, kThumb2);
524 // Unused - RegLocation rl_src_unsafe = info->args[0];
525 RegLocation rl_src_obj= info->args[1]; // Object - known non-null
526 RegLocation rl_src_offset= info->args[2]; // long low
527 rl_src_offset.wide = 0; // ignore high half in info->args[3]
528 RegLocation rl_src_expected= info->args[4]; // int or Object
529 RegLocation rl_src_new_value= info->args[5]; // int or Object
530 RegLocation rl_dest = InlineTarget(info); // boolean place for result
531
532
533 // Release store semantics, get the barrier out of the way. TODO: revisit
534 GenMemBarrier(kStoreLoad);
535
536 RegLocation rl_object = LoadValue(rl_src_obj, kCoreReg);
537 RegLocation rl_new_value = LoadValue(rl_src_new_value, kCoreReg);
538
539 if (need_write_barrier && !mir_graph_->IsConstantNullRef(rl_new_value)) {
540 // Mark card for object assuming new value is stored.
541 MarkGCCard(rl_new_value.low_reg, rl_object.low_reg);
542 }
543
544 RegLocation rl_offset = LoadValue(rl_src_offset, kCoreReg);
545
546 int r_ptr = AllocTemp();
547 OpRegRegReg(kOpAdd, r_ptr, rl_object.low_reg, rl_offset.low_reg);
548
549 // Free now unneeded rl_object and rl_offset to give more temps.
550 ClobberSReg(rl_object.s_reg_low);
551 FreeTemp(rl_object.low_reg);
552 ClobberSReg(rl_offset.s_reg_low);
553 FreeTemp(rl_offset.low_reg);
554
Jeff Hao2de2aa12013-09-12 17:20:31 -0700555 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
556 LoadConstant(rl_result.low_reg, 0); // r_result := 0
Brian Carlstrom7940e442013-07-12 13:46:57 -0700557
Jeff Hao2de2aa12013-09-12 17:20:31 -0700558 // while ([r_ptr] == rExpected && r_result == 0) {
Brian Carlstrom7940e442013-07-12 13:46:57 -0700559 // [r_ptr] <- r_new_value && r_result := success ? 0 : 1
560 // r_result ^= 1
Brian Carlstrom7940e442013-07-12 13:46:57 -0700561 // }
Jeff Hao2de2aa12013-09-12 17:20:31 -0700562 int r_old_value = AllocTemp();
563 LIR* target = NewLIR0(kPseudoTargetLabel);
564 NewLIR3(kThumb2Ldrex, r_old_value, r_ptr, 0);
565
566 RegLocation rl_expected = LoadValue(rl_src_expected, kCoreReg);
Brian Carlstrom7940e442013-07-12 13:46:57 -0700567 OpRegReg(kOpCmp, r_old_value, rl_expected.low_reg);
568 FreeTemp(r_old_value); // Now unneeded.
Jeff Hao2de2aa12013-09-12 17:20:31 -0700569 OpIT(kCondEq, "TT");
570 NewLIR4(kThumb2Strex /* eq */, rl_result.low_reg, rl_new_value.low_reg, r_ptr, 0);
Brian Carlstrom7940e442013-07-12 13:46:57 -0700571 FreeTemp(r_ptr); // Now unneeded.
Jeff Hao2de2aa12013-09-12 17:20:31 -0700572 OpRegImm(kOpXor /* eq */, rl_result.low_reg, 1);
573 OpRegImm(kOpCmp /* eq */, rl_result.low_reg, 0);
574 OpCondBranch(kCondEq, target);
Brian Carlstrom7940e442013-07-12 13:46:57 -0700575
576 StoreValue(rl_dest, rl_result);
577
578 return true;
579}
580
Brian Carlstrom2ce745c2013-07-17 17:44:30 -0700581LIR* ArmMir2Lir::OpPcRelLoad(int reg, LIR* target) {
Brian Carlstrom7940e442013-07-12 13:46:57 -0700582 return RawLIR(current_dalvik_offset_, kThumb2LdrPcRel12, reg, 0, 0, 0, 0, target);
583}
584
Brian Carlstrom2ce745c2013-07-17 17:44:30 -0700585LIR* ArmMir2Lir::OpVldm(int rBase, int count) {
Brian Carlstrom7940e442013-07-12 13:46:57 -0700586 return NewLIR3(kThumb2Vldms, rBase, fr0, count);
587}
588
Brian Carlstrom2ce745c2013-07-17 17:44:30 -0700589LIR* ArmMir2Lir::OpVstm(int rBase, int count) {
Brian Carlstrom7940e442013-07-12 13:46:57 -0700590 return NewLIR3(kThumb2Vstms, rBase, fr0, count);
591}
592
593void ArmMir2Lir::GenMultiplyByTwoBitMultiplier(RegLocation rl_src,
594 RegLocation rl_result, int lit,
Brian Carlstrom2ce745c2013-07-17 17:44:30 -0700595 int first_bit, int second_bit) {
Brian Carlstrom7940e442013-07-12 13:46:57 -0700596 OpRegRegRegShift(kOpAdd, rl_result.low_reg, rl_src.low_reg, rl_src.low_reg,
597 EncodeShift(kArmLsl, second_bit - first_bit));
598 if (first_bit != 0) {
599 OpRegRegImm(kOpLsl, rl_result.low_reg, rl_result.low_reg, first_bit);
600 }
601}
602
Brian Carlstrom2ce745c2013-07-17 17:44:30 -0700603void ArmMir2Lir::GenDivZeroCheck(int reg_lo, int reg_hi) {
Brian Carlstrom7940e442013-07-12 13:46:57 -0700604 int t_reg = AllocTemp();
605 NewLIR4(kThumb2OrrRRRs, t_reg, reg_lo, reg_hi, 0);
606 FreeTemp(t_reg);
607 GenCheck(kCondEq, kThrowDivZero);
608}
609
610// Test suspend flag, return target of taken suspend branch
Brian Carlstrom2ce745c2013-07-17 17:44:30 -0700611LIR* ArmMir2Lir::OpTestSuspend(LIR* target) {
Brian Carlstrom7940e442013-07-12 13:46:57 -0700612 NewLIR2(kThumbSubRI8, rARM_SUSPEND, 1);
613 return OpCondBranch((target == NULL) ? kCondEq : kCondNe, target);
614}
615
616// Decrement register and branch on condition
Brian Carlstrom2ce745c2013-07-17 17:44:30 -0700617LIR* ArmMir2Lir::OpDecAndBranch(ConditionCode c_code, int reg, LIR* target) {
Brian Carlstrom7940e442013-07-12 13:46:57 -0700618 // Combine sub & test using sub setflags encoding here
619 NewLIR3(kThumb2SubsRRI12, reg, reg, 1);
620 return OpCondBranch(c_code, target);
621}
622
Brian Carlstrom2ce745c2013-07-17 17:44:30 -0700623void ArmMir2Lir::GenMemBarrier(MemBarrierKind barrier_kind) {
Brian Carlstrom7940e442013-07-12 13:46:57 -0700624#if ANDROID_SMP != 0
625 int dmb_flavor;
626 // TODO: revisit Arm barrier kinds
627 switch (barrier_kind) {
628 case kLoadStore: dmb_flavor = kSY; break;
629 case kLoadLoad: dmb_flavor = kSY; break;
630 case kStoreStore: dmb_flavor = kST; break;
631 case kStoreLoad: dmb_flavor = kSY; break;
632 default:
633 LOG(FATAL) << "Unexpected MemBarrierKind: " << barrier_kind;
634 dmb_flavor = kSY; // quiet gcc.
635 break;
636 }
637 LIR* dmb = NewLIR1(kThumb2Dmb, dmb_flavor);
buzbeeb48819d2013-09-14 16:15:25 -0700638 dmb->u.m.def_mask = ENCODE_ALL;
Brian Carlstrom7940e442013-07-12 13:46:57 -0700639#endif
640}
641
Brian Carlstrom2ce745c2013-07-17 17:44:30 -0700642void ArmMir2Lir::GenNegLong(RegLocation rl_dest, RegLocation rl_src) {
Brian Carlstrom7940e442013-07-12 13:46:57 -0700643 rl_src = LoadValueWide(rl_src, kCoreReg);
644 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
645 int z_reg = AllocTemp();
646 LoadConstantNoClobber(z_reg, 0);
647 // Check for destructive overlap
648 if (rl_result.low_reg == rl_src.high_reg) {
649 int t_reg = AllocTemp();
650 OpRegRegReg(kOpSub, rl_result.low_reg, z_reg, rl_src.low_reg);
651 OpRegRegReg(kOpSbc, rl_result.high_reg, z_reg, t_reg);
652 FreeTemp(t_reg);
653 } else {
654 OpRegRegReg(kOpSub, rl_result.low_reg, z_reg, rl_src.low_reg);
655 OpRegRegReg(kOpSbc, rl_result.high_reg, z_reg, rl_src.high_reg);
656 }
657 FreeTemp(z_reg);
658 StoreValueWide(rl_dest, rl_result);
659}
660
661
662 /*
663 * Check to see if a result pair has a misaligned overlap with an operand pair. This
664 * is not usual for dx to generate, but it is legal (for now). In a future rev of
665 * dex, we'll want to make this case illegal.
666 */
Brian Carlstrom2ce745c2013-07-17 17:44:30 -0700667bool ArmMir2Lir::BadOverlap(RegLocation rl_src, RegLocation rl_dest) {
Brian Carlstrom7940e442013-07-12 13:46:57 -0700668 DCHECK(rl_src.wide);
669 DCHECK(rl_dest.wide);
670 return (abs(mir_graph_->SRegToVReg(rl_src.s_reg_low) - mir_graph_->SRegToVReg(rl_dest.s_reg_low)) == 1);
671}
672
673void ArmMir2Lir::GenMulLong(RegLocation rl_dest, RegLocation rl_src1,
Brian Carlstrom2ce745c2013-07-17 17:44:30 -0700674 RegLocation rl_src2) {
Brian Carlstrom7940e442013-07-12 13:46:57 -0700675 /*
676 * To pull off inline multiply, we have a worst-case requirement of 8 temporary
677 * registers. Normally for Arm, we get 5. We can get to 6 by including
678 * lr in the temp set. The only problematic case is all operands and result are
679 * distinct, and none have been promoted. In that case, we can succeed by aggressively
680 * freeing operand temp registers after they are no longer needed. All other cases
681 * can proceed normally. We'll just punt on the case of the result having a misaligned
682 * overlap with either operand and send that case to a runtime handler.
683 */
684 RegLocation rl_result;
685 if (BadOverlap(rl_src1, rl_dest) || (BadOverlap(rl_src2, rl_dest))) {
Ian Rogers468532e2013-08-05 10:56:33 -0700686 ThreadOffset func_offset = QUICK_ENTRYPOINT_OFFSET(pLmul);
Brian Carlstrom7940e442013-07-12 13:46:57 -0700687 FlushAllRegs();
688 CallRuntimeHelperRegLocationRegLocation(func_offset, rl_src1, rl_src2, false);
689 rl_result = GetReturnWide(false);
690 StoreValueWide(rl_dest, rl_result);
691 return;
692 }
693 // Temporarily add LR to the temp pool, and assign it to tmp1
694 MarkTemp(rARM_LR);
695 FreeTemp(rARM_LR);
696 int tmp1 = rARM_LR;
697 LockTemp(rARM_LR);
698
699 rl_src1 = LoadValueWide(rl_src1, kCoreReg);
700 rl_src2 = LoadValueWide(rl_src2, kCoreReg);
701
702 bool special_case = true;
703 // If operands are the same, or any pair has been promoted we're not the special case.
704 if ((rl_src1.s_reg_low == rl_src2.s_reg_low) ||
705 (!IsTemp(rl_src1.low_reg) && !IsTemp(rl_src1.high_reg)) ||
706 (!IsTemp(rl_src2.low_reg) && !IsTemp(rl_src2.high_reg))) {
707 special_case = false;
708 }
709 // Tuning: if rl_dest has been promoted and is *not* either operand, could use directly.
710 int res_lo = AllocTemp();
711 int res_hi;
712 if (rl_src1.low_reg == rl_src2.low_reg) {
713 res_hi = AllocTemp();
714 NewLIR3(kThumb2MulRRR, tmp1, rl_src1.low_reg, rl_src1.high_reg);
715 NewLIR4(kThumb2Umull, res_lo, res_hi, rl_src1.low_reg, rl_src1.low_reg);
716 OpRegRegRegShift(kOpAdd, res_hi, res_hi, tmp1, EncodeShift(kArmLsl, 1));
717 } else {
718 // In the special case, all temps are now allocated
719 NewLIR3(kThumb2MulRRR, tmp1, rl_src2.low_reg, rl_src1.high_reg);
720 if (special_case) {
721 DCHECK_NE(rl_src1.low_reg, rl_src2.low_reg);
722 DCHECK_NE(rl_src1.high_reg, rl_src2.high_reg);
723 FreeTemp(rl_src1.high_reg);
724 }
725 res_hi = AllocTemp();
726
727 NewLIR4(kThumb2Umull, res_lo, res_hi, rl_src2.low_reg, rl_src1.low_reg);
728 NewLIR4(kThumb2Mla, tmp1, rl_src1.low_reg, rl_src2.high_reg, tmp1);
729 NewLIR4(kThumb2AddRRR, res_hi, tmp1, res_hi, 0);
730 if (special_case) {
731 FreeTemp(rl_src1.low_reg);
732 Clobber(rl_src1.low_reg);
733 Clobber(rl_src1.high_reg);
734 }
735 }
736 FreeTemp(tmp1);
Brian Carlstrom7934ac22013-07-26 10:54:15 -0700737 rl_result = GetReturnWide(false); // Just using as a template.
Brian Carlstrom7940e442013-07-12 13:46:57 -0700738 rl_result.low_reg = res_lo;
739 rl_result.high_reg = res_hi;
740 StoreValueWide(rl_dest, rl_result);
741 // Now, restore lr to its non-temp status.
742 Clobber(rARM_LR);
743 UnmarkTemp(rARM_LR);
744}
745
746void ArmMir2Lir::GenAddLong(RegLocation rl_dest, RegLocation rl_src1,
Brian Carlstrom2ce745c2013-07-17 17:44:30 -0700747 RegLocation rl_src2) {
Brian Carlstrom7940e442013-07-12 13:46:57 -0700748 LOG(FATAL) << "Unexpected use of GenAddLong for Arm";
749}
750
751void ArmMir2Lir::GenSubLong(RegLocation rl_dest, RegLocation rl_src1,
Brian Carlstrom2ce745c2013-07-17 17:44:30 -0700752 RegLocation rl_src2) {
Brian Carlstrom7940e442013-07-12 13:46:57 -0700753 LOG(FATAL) << "Unexpected use of GenSubLong for Arm";
754}
755
756void ArmMir2Lir::GenAndLong(RegLocation rl_dest, RegLocation rl_src1,
Brian Carlstrom2ce745c2013-07-17 17:44:30 -0700757 RegLocation rl_src2) {
Brian Carlstrom7940e442013-07-12 13:46:57 -0700758 LOG(FATAL) << "Unexpected use of GenAndLong for Arm";
759}
760
761void ArmMir2Lir::GenOrLong(RegLocation rl_dest, RegLocation rl_src1,
Brian Carlstrom2ce745c2013-07-17 17:44:30 -0700762 RegLocation rl_src2) {
Brian Carlstrom7940e442013-07-12 13:46:57 -0700763 LOG(FATAL) << "Unexpected use of GenOrLong for Arm";
764}
765
766void ArmMir2Lir::GenXorLong(RegLocation rl_dest, RegLocation rl_src1,
Brian Carlstrom2ce745c2013-07-17 17:44:30 -0700767 RegLocation rl_src2) {
Brian Carlstrom7940e442013-07-12 13:46:57 -0700768 LOG(FATAL) << "Unexpected use of genXoLong for Arm";
769}
770
771/*
772 * Generate array load
773 */
774void ArmMir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array,
Brian Carlstrom2ce745c2013-07-17 17:44:30 -0700775 RegLocation rl_index, RegLocation rl_dest, int scale) {
Brian Carlstrom7940e442013-07-12 13:46:57 -0700776 RegisterClass reg_class = oat_reg_class_by_size(size);
777 int len_offset = mirror::Array::LengthOffset().Int32Value();
778 int data_offset;
779 RegLocation rl_result;
780 bool constant_index = rl_index.is_const;
781 rl_array = LoadValue(rl_array, kCoreReg);
782 if (!constant_index) {
783 rl_index = LoadValue(rl_index, kCoreReg);
784 }
785
786 if (rl_dest.wide) {
787 data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Int32Value();
788 } else {
789 data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Int32Value();
790 }
791
792 // If index is constant, just fold it into the data offset
793 if (constant_index) {
794 data_offset += mir_graph_->ConstantValue(rl_index) << scale;
795 }
796
797 /* null object? */
798 GenNullCheck(rl_array.s_reg_low, rl_array.low_reg, opt_flags);
799
800 bool needs_range_check = (!(opt_flags & MIR_IGNORE_RANGE_CHECK));
801 int reg_len = INVALID_REG;
802 if (needs_range_check) {
803 reg_len = AllocTemp();
804 /* Get len */
805 LoadWordDisp(rl_array.low_reg, len_offset, reg_len);
806 }
807 if (rl_dest.wide || rl_dest.fp || constant_index) {
808 int reg_ptr;
809 if (constant_index) {
810 reg_ptr = rl_array.low_reg; // NOTE: must not alter reg_ptr in constant case.
811 } else {
812 // No special indexed operation, lea + load w/ displacement
813 reg_ptr = AllocTemp();
814 OpRegRegRegShift(kOpAdd, reg_ptr, rl_array.low_reg, rl_index.low_reg,
815 EncodeShift(kArmLsl, scale));
816 FreeTemp(rl_index.low_reg);
817 }
818 rl_result = EvalLoc(rl_dest, reg_class, true);
819
820 if (needs_range_check) {
821 if (constant_index) {
822 GenImmedCheck(kCondLs, reg_len, mir_graph_->ConstantValue(rl_index), kThrowConstantArrayBounds);
823 } else {
824 GenRegRegCheck(kCondLs, reg_len, rl_index.low_reg, kThrowArrayBounds);
825 }
826 FreeTemp(reg_len);
827 }
828 if (rl_dest.wide) {
829 LoadBaseDispWide(reg_ptr, data_offset, rl_result.low_reg, rl_result.high_reg, INVALID_SREG);
830 if (!constant_index) {
831 FreeTemp(reg_ptr);
832 }
833 StoreValueWide(rl_dest, rl_result);
834 } else {
835 LoadBaseDisp(reg_ptr, data_offset, rl_result.low_reg, size, INVALID_SREG);
836 if (!constant_index) {
837 FreeTemp(reg_ptr);
838 }
839 StoreValue(rl_dest, rl_result);
840 }
841 } else {
842 // Offset base, then use indexed load
843 int reg_ptr = AllocTemp();
844 OpRegRegImm(kOpAdd, reg_ptr, rl_array.low_reg, data_offset);
845 FreeTemp(rl_array.low_reg);
846 rl_result = EvalLoc(rl_dest, reg_class, true);
847
848 if (needs_range_check) {
849 // TODO: change kCondCS to a more meaningful name, is the sense of
850 // carry-set/clear flipped?
851 GenRegRegCheck(kCondCs, rl_index.low_reg, reg_len, kThrowArrayBounds);
852 FreeTemp(reg_len);
853 }
854 LoadBaseIndexed(reg_ptr, rl_index.low_reg, rl_result.low_reg, scale, size);
855 FreeTemp(reg_ptr);
856 StoreValue(rl_dest, rl_result);
857 }
858}
859
860/*
861 * Generate array store
862 *
863 */
864void ArmMir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array,
Brian Carlstrom2ce745c2013-07-17 17:44:30 -0700865 RegLocation rl_index, RegLocation rl_src, int scale) {
Brian Carlstrom7940e442013-07-12 13:46:57 -0700866 RegisterClass reg_class = oat_reg_class_by_size(size);
867 int len_offset = mirror::Array::LengthOffset().Int32Value();
868 int data_offset;
869 bool constant_index = rl_index.is_const;
870
871 if (rl_src.wide) {
872 data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Int32Value();
873 } else {
874 data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Int32Value();
875 }
876
877 // If index is constant, just fold it into the data offset.
878 if (constant_index) {
879 data_offset += mir_graph_->ConstantValue(rl_index) << scale;
880 }
881
882 rl_array = LoadValue(rl_array, kCoreReg);
883 if (!constant_index) {
884 rl_index = LoadValue(rl_index, kCoreReg);
885 }
886
887 int reg_ptr;
888 if (constant_index) {
889 reg_ptr = rl_array.low_reg;
890 } else if (IsTemp(rl_array.low_reg)) {
891 Clobber(rl_array.low_reg);
892 reg_ptr = rl_array.low_reg;
893 } else {
894 reg_ptr = AllocTemp();
895 }
896
897 /* null object? */
898 GenNullCheck(rl_array.s_reg_low, rl_array.low_reg, opt_flags);
899
900 bool needs_range_check = (!(opt_flags & MIR_IGNORE_RANGE_CHECK));
901 int reg_len = INVALID_REG;
902 if (needs_range_check) {
903 reg_len = AllocTemp();
Brian Carlstrom7934ac22013-07-26 10:54:15 -0700904 // NOTE: max live temps(4) here.
Brian Carlstrom7940e442013-07-12 13:46:57 -0700905 /* Get len */
906 LoadWordDisp(rl_array.low_reg, len_offset, reg_len);
907 }
908 /* at this point, reg_ptr points to array, 2 live temps */
909 if (rl_src.wide || rl_src.fp || constant_index) {
910 if (rl_src.wide) {
911 rl_src = LoadValueWide(rl_src, reg_class);
912 } else {
913 rl_src = LoadValue(rl_src, reg_class);
914 }
915 if (!constant_index) {
916 OpRegRegRegShift(kOpAdd, reg_ptr, rl_array.low_reg, rl_index.low_reg,
917 EncodeShift(kArmLsl, scale));
918 }
919 if (needs_range_check) {
920 if (constant_index) {
921 GenImmedCheck(kCondLs, reg_len, mir_graph_->ConstantValue(rl_index), kThrowConstantArrayBounds);
922 } else {
923 GenRegRegCheck(kCondLs, reg_len, rl_index.low_reg, kThrowArrayBounds);
924 }
925 FreeTemp(reg_len);
926 }
927
928 if (rl_src.wide) {
929 StoreBaseDispWide(reg_ptr, data_offset, rl_src.low_reg, rl_src.high_reg);
930 } else {
931 StoreBaseDisp(reg_ptr, data_offset, rl_src.low_reg, size);
932 }
933 } else {
934 /* reg_ptr -> array data */
935 OpRegRegImm(kOpAdd, reg_ptr, rl_array.low_reg, data_offset);
936 rl_src = LoadValue(rl_src, reg_class);
937 if (needs_range_check) {
938 GenRegRegCheck(kCondCs, rl_index.low_reg, reg_len, kThrowArrayBounds);
939 FreeTemp(reg_len);
940 }
941 StoreBaseIndexed(reg_ptr, rl_index.low_reg, rl_src.low_reg,
942 scale, size);
943 }
944 if (!constant_index) {
945 FreeTemp(reg_ptr);
946 }
947}
948
949/*
950 * Generate array store
951 *
952 */
953void ArmMir2Lir::GenArrayObjPut(int opt_flags, RegLocation rl_array,
Brian Carlstrom2ce745c2013-07-17 17:44:30 -0700954 RegLocation rl_index, RegLocation rl_src, int scale) {
Brian Carlstrom7940e442013-07-12 13:46:57 -0700955 int len_offset = mirror::Array::LengthOffset().Int32Value();
956 int data_offset = mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value();
957
958 FlushAllRegs(); // Use explicit registers
959 LockCallTemps();
960
961 int r_value = TargetReg(kArg0); // Register holding value
962 int r_array_class = TargetReg(kArg1); // Register holding array's Class
963 int r_array = TargetReg(kArg2); // Register holding array
964 int r_index = TargetReg(kArg3); // Register holding index into array
965
966 LoadValueDirectFixed(rl_array, r_array); // Grab array
967 LoadValueDirectFixed(rl_src, r_value); // Grab value
968 LoadValueDirectFixed(rl_index, r_index); // Grab index
969
970 GenNullCheck(rl_array.s_reg_low, r_array, opt_flags); // NPE?
971
972 // Store of null?
973 LIR* null_value_check = OpCmpImmBranch(kCondEq, r_value, 0, NULL);
974
975 // Get the array's class.
976 LoadWordDisp(r_array, mirror::Object::ClassOffset().Int32Value(), r_array_class);
Ian Rogers468532e2013-08-05 10:56:33 -0700977 CallRuntimeHelperRegReg(QUICK_ENTRYPOINT_OFFSET(pCanPutArrayElement), r_value,
Brian Carlstrom7940e442013-07-12 13:46:57 -0700978 r_array_class, true);
979 // Redo LoadValues in case they didn't survive the call.
980 LoadValueDirectFixed(rl_array, r_array); // Reload array
981 LoadValueDirectFixed(rl_index, r_index); // Reload index
982 LoadValueDirectFixed(rl_src, r_value); // Reload value
983 r_array_class = INVALID_REG;
984
985 // Branch here if value to be stored == null
986 LIR* target = NewLIR0(kPseudoTargetLabel);
987 null_value_check->target = target;
988
989 bool needs_range_check = (!(opt_flags & MIR_IGNORE_RANGE_CHECK));
990 int reg_len = INVALID_REG;
991 if (needs_range_check) {
992 reg_len = TargetReg(kArg1);
993 LoadWordDisp(r_array, len_offset, reg_len); // Get len
994 }
995 /* r_ptr -> array data */
996 int r_ptr = AllocTemp();
997 OpRegRegImm(kOpAdd, r_ptr, r_array, data_offset);
998 if (needs_range_check) {
999 GenRegRegCheck(kCondCs, r_index, reg_len, kThrowArrayBounds);
1000 }
1001 StoreBaseIndexed(r_ptr, r_index, r_value, scale, kWord);
1002 FreeTemp(r_ptr);
1003 FreeTemp(r_index);
1004 if (!mir_graph_->IsConstantNullRef(rl_src)) {
1005 MarkGCCard(r_value, r_array);
1006 }
1007}
1008
1009void ArmMir2Lir::GenShiftImmOpLong(Instruction::Code opcode,
Brian Carlstrom2ce745c2013-07-17 17:44:30 -07001010 RegLocation rl_dest, RegLocation rl_src, RegLocation rl_shift) {
Brian Carlstrom7940e442013-07-12 13:46:57 -07001011 rl_src = LoadValueWide(rl_src, kCoreReg);
1012 // Per spec, we only care about low 6 bits of shift amount.
1013 int shift_amount = mir_graph_->ConstantValue(rl_shift) & 0x3f;
1014 if (shift_amount == 0) {
1015 StoreValueWide(rl_dest, rl_src);
1016 return;
1017 }
1018 if (BadOverlap(rl_src, rl_dest)) {
1019 GenShiftOpLong(opcode, rl_dest, rl_src, rl_shift);
1020 return;
1021 }
1022 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
Brian Carlstromdf629502013-07-17 22:39:56 -07001023 switch (opcode) {
Brian Carlstrom7940e442013-07-12 13:46:57 -07001024 case Instruction::SHL_LONG:
1025 case Instruction::SHL_LONG_2ADDR:
1026 if (shift_amount == 1) {
1027 OpRegRegReg(kOpAdd, rl_result.low_reg, rl_src.low_reg, rl_src.low_reg);
1028 OpRegRegReg(kOpAdc, rl_result.high_reg, rl_src.high_reg, rl_src.high_reg);
1029 } else if (shift_amount == 32) {
1030 OpRegCopy(rl_result.high_reg, rl_src.low_reg);
1031 LoadConstant(rl_result.low_reg, 0);
1032 } else if (shift_amount > 31) {
1033 OpRegRegImm(kOpLsl, rl_result.high_reg, rl_src.low_reg, shift_amount - 32);
1034 LoadConstant(rl_result.low_reg, 0);
1035 } else {
1036 OpRegRegImm(kOpLsl, rl_result.high_reg, rl_src.high_reg, shift_amount);
1037 OpRegRegRegShift(kOpOr, rl_result.high_reg, rl_result.high_reg, rl_src.low_reg,
1038 EncodeShift(kArmLsr, 32 - shift_amount));
1039 OpRegRegImm(kOpLsl, rl_result.low_reg, rl_src.low_reg, shift_amount);
1040 }
1041 break;
1042 case Instruction::SHR_LONG:
1043 case Instruction::SHR_LONG_2ADDR:
1044 if (shift_amount == 32) {
1045 OpRegCopy(rl_result.low_reg, rl_src.high_reg);
1046 OpRegRegImm(kOpAsr, rl_result.high_reg, rl_src.high_reg, 31);
1047 } else if (shift_amount > 31) {
1048 OpRegRegImm(kOpAsr, rl_result.low_reg, rl_src.high_reg, shift_amount - 32);
1049 OpRegRegImm(kOpAsr, rl_result.high_reg, rl_src.high_reg, 31);
1050 } else {
1051 int t_reg = AllocTemp();
1052 OpRegRegImm(kOpLsr, t_reg, rl_src.low_reg, shift_amount);
1053 OpRegRegRegShift(kOpOr, rl_result.low_reg, t_reg, rl_src.high_reg,
1054 EncodeShift(kArmLsl, 32 - shift_amount));
1055 FreeTemp(t_reg);
1056 OpRegRegImm(kOpAsr, rl_result.high_reg, rl_src.high_reg, shift_amount);
1057 }
1058 break;
1059 case Instruction::USHR_LONG:
1060 case Instruction::USHR_LONG_2ADDR:
1061 if (shift_amount == 32) {
1062 OpRegCopy(rl_result.low_reg, rl_src.high_reg);
1063 LoadConstant(rl_result.high_reg, 0);
1064 } else if (shift_amount > 31) {
1065 OpRegRegImm(kOpLsr, rl_result.low_reg, rl_src.high_reg, shift_amount - 32);
1066 LoadConstant(rl_result.high_reg, 0);
1067 } else {
1068 int t_reg = AllocTemp();
1069 OpRegRegImm(kOpLsr, t_reg, rl_src.low_reg, shift_amount);
1070 OpRegRegRegShift(kOpOr, rl_result.low_reg, t_reg, rl_src.high_reg,
1071 EncodeShift(kArmLsl, 32 - shift_amount));
1072 FreeTemp(t_reg);
1073 OpRegRegImm(kOpLsr, rl_result.high_reg, rl_src.high_reg, shift_amount);
1074 }
1075 break;
1076 default:
1077 LOG(FATAL) << "Unexpected case";
1078 }
1079 StoreValueWide(rl_dest, rl_result);
1080}
1081
1082void ArmMir2Lir::GenArithImmOpLong(Instruction::Code opcode,
Brian Carlstrom2ce745c2013-07-17 17:44:30 -07001083 RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) {
Brian Carlstrom7940e442013-07-12 13:46:57 -07001084 if ((opcode == Instruction::SUB_LONG_2ADDR) || (opcode == Instruction::SUB_LONG)) {
1085 if (!rl_src2.is_const) {
1086 // Don't bother with special handling for subtract from immediate.
1087 GenArithOpLong(opcode, rl_dest, rl_src1, rl_src2);
1088 return;
1089 }
1090 } else {
1091 // Normalize
1092 if (!rl_src2.is_const) {
1093 DCHECK(rl_src1.is_const);
1094 RegLocation rl_temp = rl_src1;
1095 rl_src1 = rl_src2;
1096 rl_src2 = rl_temp;
1097 }
1098 }
1099 if (BadOverlap(rl_src1, rl_dest)) {
1100 GenArithOpLong(opcode, rl_dest, rl_src1, rl_src2);
1101 return;
1102 }
1103 DCHECK(rl_src2.is_const);
1104 int64_t val = mir_graph_->ConstantValueWide(rl_src2);
1105 uint32_t val_lo = Low32Bits(val);
1106 uint32_t val_hi = High32Bits(val);
1107 int32_t mod_imm_lo = ModifiedImmediate(val_lo);
1108 int32_t mod_imm_hi = ModifiedImmediate(val_hi);
1109
1110 // Only a subset of add/sub immediate instructions set carry - so bail if we don't fit
Brian Carlstromdf629502013-07-17 22:39:56 -07001111 switch (opcode) {
Brian Carlstrom7940e442013-07-12 13:46:57 -07001112 case Instruction::ADD_LONG:
1113 case Instruction::ADD_LONG_2ADDR:
1114 case Instruction::SUB_LONG:
1115 case Instruction::SUB_LONG_2ADDR:
1116 if ((mod_imm_lo < 0) || (mod_imm_hi < 0)) {
1117 GenArithOpLong(opcode, rl_dest, rl_src1, rl_src2);
1118 return;
1119 }
1120 break;
1121 default:
1122 break;
1123 }
1124 rl_src1 = LoadValueWide(rl_src1, kCoreReg);
1125 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
1126 // NOTE: once we've done the EvalLoc on dest, we can no longer bail.
1127 switch (opcode) {
1128 case Instruction::ADD_LONG:
1129 case Instruction::ADD_LONG_2ADDR:
1130 NewLIR3(kThumb2AddRRI8, rl_result.low_reg, rl_src1.low_reg, mod_imm_lo);
1131 NewLIR3(kThumb2AdcRRI8, rl_result.high_reg, rl_src1.high_reg, mod_imm_hi);
1132 break;
1133 case Instruction::OR_LONG:
1134 case Instruction::OR_LONG_2ADDR:
1135 if ((val_lo != 0) || (rl_result.low_reg != rl_src1.low_reg)) {
1136 OpRegRegImm(kOpOr, rl_result.low_reg, rl_src1.low_reg, val_lo);
1137 }
1138 if ((val_hi != 0) || (rl_result.high_reg != rl_src1.high_reg)) {
1139 OpRegRegImm(kOpOr, rl_result.high_reg, rl_src1.high_reg, val_hi);
1140 }
1141 break;
1142 case Instruction::XOR_LONG:
1143 case Instruction::XOR_LONG_2ADDR:
1144 OpRegRegImm(kOpXor, rl_result.low_reg, rl_src1.low_reg, val_lo);
1145 OpRegRegImm(kOpXor, rl_result.high_reg, rl_src1.high_reg, val_hi);
1146 break;
1147 case Instruction::AND_LONG:
1148 case Instruction::AND_LONG_2ADDR:
1149 if ((val_lo != 0xffffffff) || (rl_result.low_reg != rl_src1.low_reg)) {
1150 OpRegRegImm(kOpAnd, rl_result.low_reg, rl_src1.low_reg, val_lo);
1151 }
1152 if ((val_hi != 0xffffffff) || (rl_result.high_reg != rl_src1.high_reg)) {
1153 OpRegRegImm(kOpAnd, rl_result.high_reg, rl_src1.high_reg, val_hi);
1154 }
1155 break;
1156 case Instruction::SUB_LONG_2ADDR:
1157 case Instruction::SUB_LONG:
1158 NewLIR3(kThumb2SubRRI8, rl_result.low_reg, rl_src1.low_reg, mod_imm_lo);
1159 NewLIR3(kThumb2SbcRRI8, rl_result.high_reg, rl_src1.high_reg, mod_imm_hi);
1160 break;
1161 default:
1162 LOG(FATAL) << "Unexpected opcode " << opcode;
1163 }
1164 StoreValueWide(rl_dest, rl_result);
1165}
1166
1167} // namespace art