blob: 5a9786c0d65a776716a191c52667def8d3e353f6 [file] [log] [blame]
buzbeeefc63692012-11-14 16:31:52 -08001/*
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
buzbee1bc37c62012-11-20 13:35:41 -080019#include "arm_lir.h"
buzbee02031b12012-11-23 09:41:35 -080020#include "codegen_arm.h"
Brian Carlstrom641ce032013-01-31 15:21:37 -080021#include "compiler/codegen/codegen_util.h"
22#include "compiler/codegen/ralloc_util.h"
23#include "oat/runtime/oat_support_entrypoints.h"
24#include "oat_compilation_unit.h"
buzbeeefc63692012-11-14 16:31:52 -080025
26namespace art {
27
buzbee02031b12012-11-23 09:41:35 -080028LIR* ArmCodegen::OpCmpBranch(CompilationUnit* cu, ConditionCode cond, int src1,
buzbeeefc63692012-11-14 16:31:52 -080029 int src2, LIR* target)
30{
buzbeefa57c472012-11-21 12:06:18 -080031 OpRegReg(cu, kOpCmp, src1, src2);
32 return OpCondBranch(cu, cond, target);
buzbeeefc63692012-11-14 16:31:52 -080033}
34
35/*
36 * Generate a Thumb2 IT instruction, which can nullify up to
37 * four subsequent instructions based on a condition and its
38 * inverse. The condition applies to the first instruction, which
39 * is executed if the condition is met. The string "guide" consists
40 * of 0 to 3 chars, and applies to the 2nd through 4th instruction.
41 * A "T" means the instruction is executed if the condition is
42 * met, and an "E" means the instruction is executed if the condition
43 * is not met.
44 */
buzbee02031b12012-11-23 09:41:35 -080045LIR* ArmCodegen::OpIT(CompilationUnit* cu, ConditionCode ccode, const char* guide)
buzbeeefc63692012-11-14 16:31:52 -080046{
47 int mask;
buzbeeefc63692012-11-14 16:31:52 -080048 int mask3 = 0;
49 int mask2 = 0;
50 int mask1 = 0;
buzbee02031b12012-11-23 09:41:35 -080051 ArmConditionCode code = ArmConditionEncoding(ccode);
52 int cond_bit = code & 1;
53 int alt_bit = cond_bit ^ 1;
buzbeeefc63692012-11-14 16:31:52 -080054
55 //Note: case fallthroughs intentional
56 switch (strlen(guide)) {
57 case 3:
buzbeefa57c472012-11-21 12:06:18 -080058 mask1 = (guide[2] == 'T') ? cond_bit : alt_bit;
buzbeeefc63692012-11-14 16:31:52 -080059 case 2:
buzbeefa57c472012-11-21 12:06:18 -080060 mask2 = (guide[1] == 'T') ? cond_bit : alt_bit;
buzbeeefc63692012-11-14 16:31:52 -080061 case 1:
buzbeefa57c472012-11-21 12:06:18 -080062 mask3 = (guide[0] == 'T') ? cond_bit : alt_bit;
buzbeeefc63692012-11-14 16:31:52 -080063 break;
64 case 0:
65 break;
66 default:
buzbee52a77fc2012-11-20 19:50:46 -080067 LOG(FATAL) << "OAT: bad case in OpIT";
buzbeeefc63692012-11-14 16:31:52 -080068 }
69 mask = (mask3 << 3) | (mask2 << 2) | (mask1 << 1) |
70 (1 << (3 - strlen(guide)));
buzbeefa57c472012-11-21 12:06:18 -080071 return NewLIR2(cu, kThumb2It, code, mask);
buzbeeefc63692012-11-14 16:31:52 -080072}
73
74/*
75 * 64-bit 3way compare function.
76 * mov rX, #-1
77 * cmp op1hi, op2hi
78 * blt done
79 * bgt flip
80 * sub rX, op1lo, op2lo (treat as unsigned)
81 * beq done
82 * ite hi
83 * mov(hi) rX, #-1
84 * mov(!hi) rX, #1
85 * flip:
86 * neg rX
87 * done:
88 */
buzbee02031b12012-11-23 09:41:35 -080089void ArmCodegen::GenCmpLong(CompilationUnit* cu, RegLocation rl_dest, RegLocation rl_src1,
90 RegLocation rl_src2)
buzbeeefc63692012-11-14 16:31:52 -080091{
92 LIR* target1;
93 LIR* target2;
buzbeefa57c472012-11-21 12:06:18 -080094 rl_src1 = LoadValueWide(cu, rl_src1, kCoreReg);
95 rl_src2 = LoadValueWide(cu, rl_src2, kCoreReg);
96 int t_reg = AllocTemp(cu);
97 LoadConstant(cu, t_reg, -1);
98 OpRegReg(cu, kOpCmp, rl_src1.high_reg, rl_src2.high_reg);
99 LIR* branch1 = OpCondBranch(cu, kCondLt, NULL);
100 LIR* branch2 = OpCondBranch(cu, kCondGt, NULL);
101 OpRegRegReg(cu, kOpSub, t_reg, rl_src1.low_reg, rl_src2.low_reg);
102 LIR* branch3 = OpCondBranch(cu, kCondEq, NULL);
buzbeeefc63692012-11-14 16:31:52 -0800103
buzbee02031b12012-11-23 09:41:35 -0800104 OpIT(cu, kCondHi, "E");
buzbeefa57c472012-11-21 12:06:18 -0800105 NewLIR2(cu, kThumb2MovImmShift, t_reg, ModifiedImmediate(-1));
106 LoadConstant(cu, t_reg, 1);
107 GenBarrier(cu);
buzbeeefc63692012-11-14 16:31:52 -0800108
buzbeefa57c472012-11-21 12:06:18 -0800109 target2 = NewLIR0(cu, kPseudoTargetLabel);
110 OpRegReg(cu, kOpNeg, t_reg, t_reg);
buzbeeefc63692012-11-14 16:31:52 -0800111
buzbeefa57c472012-11-21 12:06:18 -0800112 target1 = NewLIR0(cu, kPseudoTargetLabel);
buzbeeefc63692012-11-14 16:31:52 -0800113
buzbeefa57c472012-11-21 12:06:18 -0800114 RegLocation rl_temp = LocCReturn(); // Just using as template, will change
115 rl_temp.low_reg = t_reg;
116 StoreValue(cu, rl_dest, rl_temp);
117 FreeTemp(cu, t_reg);
buzbeeefc63692012-11-14 16:31:52 -0800118
buzbeecbd6d442012-11-17 14:11:25 -0800119 branch1->target = target1;
120 branch2->target = target2;
buzbeeefc63692012-11-14 16:31:52 -0800121 branch3->target = branch1->target;
122}
123
buzbee4ef3e452012-12-14 13:35:28 -0800124void ArmCodegen::GenFusedLongCmpImmBranch(CompilationUnit* cu, BasicBlock* bb, RegLocation rl_src1,
125 int64_t val, ConditionCode ccode)
buzbeeefc63692012-11-14 16:31:52 -0800126{
buzbee4ef3e452012-12-14 13:35:28 -0800127 int32_t val_lo = Low32Bits(val);
128 int32_t val_hi = High32Bits(val);
129 DCHECK(ModifiedImmediate(val_lo) >= 0);
130 DCHECK(ModifiedImmediate(val_hi) >= 0);
buzbeefa57c472012-11-21 12:06:18 -0800131 LIR* label_list = cu->block_label_list;
132 LIR* taken = &label_list[bb->taken->id];
133 LIR* not_taken = &label_list[bb->fall_through->id];
buzbee4ef3e452012-12-14 13:35:28 -0800134 rl_src1 = LoadValueWide(cu, rl_src1, kCoreReg);
135 int32_t low_reg = rl_src1.low_reg;
136 int32_t high_reg = rl_src1.high_reg;
137
138 switch(ccode) {
139 case kCondEq:
140 OpCmpImmBranch(cu, kCondNe, high_reg, val_hi, not_taken);
141 break;
142 case kCondNe:
143 OpCmpImmBranch(cu, kCondNe, high_reg, val_hi, taken);
144 break;
145 case kCondLt:
146 OpCmpImmBranch(cu, kCondLt, high_reg, val_hi, taken);
147 OpCmpImmBranch(cu, kCondGt, high_reg, val_hi, not_taken);
148 ccode = kCondCc;
149 break;
150 case kCondLe:
151 OpCmpImmBranch(cu, kCondLt, high_reg, val_hi, taken);
152 OpCmpImmBranch(cu, kCondGt, high_reg, val_hi, not_taken);
153 ccode = kCondLs;
154 break;
155 case kCondGt:
156 OpCmpImmBranch(cu, kCondGt, high_reg, val_hi, taken);
157 OpCmpImmBranch(cu, kCondLt, high_reg, val_hi, not_taken);
158 ccode = kCondHi;
159 break;
160 case kCondGe:
161 OpCmpImmBranch(cu, kCondGt, high_reg, val_hi, taken);
162 OpCmpImmBranch(cu, kCondLt, high_reg, val_hi, not_taken);
163 ccode = kCondCs;
164 break;
165 default:
166 LOG(FATAL) << "Unexpected ccode: " << ccode;
167 }
168 OpCmpImmBranch(cu, ccode, low_reg, val_lo, taken);
169}
170
171
172void ArmCodegen::GenFusedLongCmpBranch(CompilationUnit* cu, BasicBlock* bb, MIR* mir)
173{
buzbeefa57c472012-11-21 12:06:18 -0800174 RegLocation rl_src1 = GetSrcWide(cu, mir, 0);
175 RegLocation rl_src2 = GetSrcWide(cu, mir, 2);
buzbee4ef3e452012-12-14 13:35:28 -0800176 // Normalize such that if either operand is constant, src2 will be constant.
177 ConditionCode ccode = static_cast<ConditionCode>(mir->dalvikInsn.arg[0]);
178 if (rl_src1.is_const) {
179 RegLocation rl_temp = rl_src1;
180 rl_src1 = rl_src2;
181 rl_src2 = rl_temp;
182 ccode = FlipComparisonOrder(ccode);
183 }
184 if (rl_src2.is_const) {
185 RegLocation rl_temp = UpdateLocWide(cu, rl_src2);
186 // Do special compare/branch against simple const operand if not already in registers.
187 int64_t val = ConstantValueWide(cu, rl_src2);
188 if ((rl_temp.location != kLocPhysReg) &&
189 ((ModifiedImmediate(Low32Bits(val)) >= 0) && (ModifiedImmediate(High32Bits(val)) >= 0))) {
190 GenFusedLongCmpImmBranch(cu, bb, rl_src1, val, ccode);
191 return;
192 }
193 }
194 LIR* label_list = cu->block_label_list;
195 LIR* taken = &label_list[bb->taken->id];
196 LIR* not_taken = &label_list[bb->fall_through->id];
buzbeefa57c472012-11-21 12:06:18 -0800197 rl_src1 = LoadValueWide(cu, rl_src1, kCoreReg);
198 rl_src2 = LoadValueWide(cu, rl_src2, kCoreReg);
buzbeefa57c472012-11-21 12:06:18 -0800199 OpRegReg(cu, kOpCmp, rl_src1.high_reg, rl_src2.high_reg);
buzbeeefc63692012-11-14 16:31:52 -0800200 switch(ccode) {
201 case kCondEq:
buzbeefa57c472012-11-21 12:06:18 -0800202 OpCondBranch(cu, kCondNe, not_taken);
buzbeeefc63692012-11-14 16:31:52 -0800203 break;
204 case kCondNe:
buzbeefa57c472012-11-21 12:06:18 -0800205 OpCondBranch(cu, kCondNe, taken);
buzbeeefc63692012-11-14 16:31:52 -0800206 break;
207 case kCondLt:
buzbeefa57c472012-11-21 12:06:18 -0800208 OpCondBranch(cu, kCondLt, taken);
209 OpCondBranch(cu, kCondGt, not_taken);
buzbeeefc63692012-11-14 16:31:52 -0800210 ccode = kCondCc;
211 break;
212 case kCondLe:
buzbeefa57c472012-11-21 12:06:18 -0800213 OpCondBranch(cu, kCondLt, taken);
214 OpCondBranch(cu, kCondGt, not_taken);
buzbeeefc63692012-11-14 16:31:52 -0800215 ccode = kCondLs;
216 break;
217 case kCondGt:
buzbeefa57c472012-11-21 12:06:18 -0800218 OpCondBranch(cu, kCondGt, taken);
219 OpCondBranch(cu, kCondLt, not_taken);
buzbeeefc63692012-11-14 16:31:52 -0800220 ccode = kCondHi;
221 break;
222 case kCondGe:
buzbeefa57c472012-11-21 12:06:18 -0800223 OpCondBranch(cu, kCondGt, taken);
224 OpCondBranch(cu, kCondLt, not_taken);
buzbeeefc63692012-11-14 16:31:52 -0800225 ccode = kCondCs;
226 break;
227 default:
buzbeecbd6d442012-11-17 14:11:25 -0800228 LOG(FATAL) << "Unexpected ccode: " << ccode;
buzbeeefc63692012-11-14 16:31:52 -0800229 }
buzbeefa57c472012-11-21 12:06:18 -0800230 OpRegReg(cu, kOpCmp, rl_src1.low_reg, rl_src2.low_reg);
231 OpCondBranch(cu, ccode, taken);
buzbeeefc63692012-11-14 16:31:52 -0800232}
233
234/*
235 * Generate a register comparison to an immediate and branch. Caller
236 * is responsible for setting branch target field.
237 */
buzbee02031b12012-11-23 09:41:35 -0800238LIR* ArmCodegen::OpCmpImmBranch(CompilationUnit* cu, ConditionCode cond, int reg, int check_value,
239 LIR* target)
buzbeeefc63692012-11-14 16:31:52 -0800240{
241 LIR* branch;
buzbeefa57c472012-11-21 12:06:18 -0800242 int mod_imm;
243 ArmConditionCode arm_cond = ArmConditionEncoding(cond);
244 if ((ARM_LOWREG(reg)) && (check_value == 0) &&
245 ((arm_cond == kArmCondEq) || (arm_cond == kArmCondNe))) {
246 branch = NewLIR2(cu, (arm_cond == kArmCondEq) ? kThumb2Cbz : kThumb2Cbnz,
buzbeeefc63692012-11-14 16:31:52 -0800247 reg, 0);
248 } else {
buzbeefa57c472012-11-21 12:06:18 -0800249 mod_imm = ModifiedImmediate(check_value);
250 if (ARM_LOWREG(reg) && ((check_value & 0xff) == check_value)) {
251 NewLIR2(cu, kThumbCmpRI8, reg, check_value);
252 } else if (mod_imm >= 0) {
buzbee4ef3e452012-12-14 13:35:28 -0800253 NewLIR2(cu, kThumb2CmpRI12, reg, mod_imm);
buzbeeefc63692012-11-14 16:31:52 -0800254 } else {
buzbeefa57c472012-11-21 12:06:18 -0800255 int t_reg = AllocTemp(cu);
256 LoadConstant(cu, t_reg, check_value);
257 OpRegReg(cu, kOpCmp, reg, t_reg);
buzbeeefc63692012-11-14 16:31:52 -0800258 }
buzbeefa57c472012-11-21 12:06:18 -0800259 branch = NewLIR2(cu, kThumbBCond, 0, arm_cond);
buzbeeefc63692012-11-14 16:31:52 -0800260 }
261 branch->target = target;
262 return branch;
263}
buzbee02031b12012-11-23 09:41:35 -0800264
265LIR* ArmCodegen::OpRegCopyNoInsert(CompilationUnit* cu, int r_dest, int r_src)
buzbeeefc63692012-11-14 16:31:52 -0800266{
267 LIR* res;
268 int opcode;
buzbeefa57c472012-11-21 12:06:18 -0800269 if (ARM_FPREG(r_dest) || ARM_FPREG(r_src))
buzbee02031b12012-11-23 09:41:35 -0800270 return OpFpRegCopy(cu, r_dest, r_src);
buzbeefa57c472012-11-21 12:06:18 -0800271 if (ARM_LOWREG(r_dest) && ARM_LOWREG(r_src))
buzbeeefc63692012-11-14 16:31:52 -0800272 opcode = kThumbMovRR;
buzbeefa57c472012-11-21 12:06:18 -0800273 else if (!ARM_LOWREG(r_dest) && !ARM_LOWREG(r_src))
buzbeeefc63692012-11-14 16:31:52 -0800274 opcode = kThumbMovRR_H2H;
buzbeefa57c472012-11-21 12:06:18 -0800275 else if (ARM_LOWREG(r_dest))
buzbeeefc63692012-11-14 16:31:52 -0800276 opcode = kThumbMovRR_H2L;
277 else
278 opcode = kThumbMovRR_L2H;
buzbeefa57c472012-11-21 12:06:18 -0800279 res = RawLIR(cu, cu->current_dalvik_offset, opcode, r_dest, r_src);
280 if (!(cu->disable_opt & (1 << kSafeOptimizations)) && r_dest == r_src) {
281 res->flags.is_nop = true;
buzbeeefc63692012-11-14 16:31:52 -0800282 }
283 return res;
284}
285
buzbee02031b12012-11-23 09:41:35 -0800286LIR* ArmCodegen::OpRegCopy(CompilationUnit* cu, int r_dest, int r_src)
buzbeeefc63692012-11-14 16:31:52 -0800287{
buzbeefa57c472012-11-21 12:06:18 -0800288 LIR* res = OpRegCopyNoInsert(cu, r_dest, r_src);
289 AppendLIR(cu, res);
buzbeeefc63692012-11-14 16:31:52 -0800290 return res;
291}
292
buzbee02031b12012-11-23 09:41:35 -0800293void ArmCodegen::OpRegCopyWide(CompilationUnit* cu, int dest_lo, int dest_hi, int src_lo,
294 int src_hi)
buzbeeefc63692012-11-14 16:31:52 -0800295{
buzbeefa57c472012-11-21 12:06:18 -0800296 bool dest_fp = ARM_FPREG(dest_lo) && ARM_FPREG(dest_hi);
297 bool src_fp = ARM_FPREG(src_lo) && ARM_FPREG(src_hi);
298 DCHECK_EQ(ARM_FPREG(src_lo), ARM_FPREG(src_hi));
299 DCHECK_EQ(ARM_FPREG(dest_lo), ARM_FPREG(dest_hi));
300 if (dest_fp) {
301 if (src_fp) {
302 OpRegCopy(cu, S2d(dest_lo, dest_hi), S2d(src_lo, src_hi));
buzbeeefc63692012-11-14 16:31:52 -0800303 } else {
buzbeefa57c472012-11-21 12:06:18 -0800304 NewLIR3(cu, kThumb2Fmdrr, S2d(dest_lo, dest_hi), src_lo, src_hi);
buzbeeefc63692012-11-14 16:31:52 -0800305 }
306 } else {
buzbeefa57c472012-11-21 12:06:18 -0800307 if (src_fp) {
308 NewLIR3(cu, kThumb2Fmrrd, dest_lo, dest_hi, S2d(src_lo, src_hi));
buzbeeefc63692012-11-14 16:31:52 -0800309 } else {
310 // Handle overlap
buzbeefa57c472012-11-21 12:06:18 -0800311 if (src_hi == dest_lo) {
312 OpRegCopy(cu, dest_hi, src_hi);
313 OpRegCopy(cu, dest_lo, src_lo);
buzbeeefc63692012-11-14 16:31:52 -0800314 } else {
buzbeefa57c472012-11-21 12:06:18 -0800315 OpRegCopy(cu, dest_lo, src_lo);
316 OpRegCopy(cu, dest_hi, src_hi);
buzbeeefc63692012-11-14 16:31:52 -0800317 }
318 }
319 }
320}
321
322// Table of magic divisors
buzbeeefc63692012-11-14 16:31:52 -0800323struct MagicTable {
324 uint32_t magic;
325 uint32_t shift;
326 DividePattern pattern;
327};
328
buzbeefa57c472012-11-21 12:06:18 -0800329static const MagicTable magic_table[] = {
buzbeeefc63692012-11-14 16:31:52 -0800330 {0, 0, DivideNone}, // 0
331 {0, 0, DivideNone}, // 1
332 {0, 0, DivideNone}, // 2
333 {0x55555556, 0, Divide3}, // 3
334 {0, 0, DivideNone}, // 4
335 {0x66666667, 1, Divide5}, // 5
336 {0x2AAAAAAB, 0, Divide3}, // 6
337 {0x92492493, 2, Divide7}, // 7
338 {0, 0, DivideNone}, // 8
339 {0x38E38E39, 1, Divide5}, // 9
340 {0x66666667, 2, Divide5}, // 10
341 {0x2E8BA2E9, 1, Divide5}, // 11
342 {0x2AAAAAAB, 1, Divide5}, // 12
343 {0x4EC4EC4F, 2, Divide5}, // 13
344 {0x92492493, 3, Divide7}, // 14
345 {0x88888889, 3, Divide7}, // 15
346};
347
348// Integer division by constant via reciprocal multiply (Hacker's Delight, 10-4)
buzbee02031b12012-11-23 09:41:35 -0800349bool ArmCodegen::SmallLiteralDivide(CompilationUnit* cu, Instruction::Code dalvik_opcode,
350 RegLocation rl_src, RegLocation rl_dest, int lit)
buzbeeefc63692012-11-14 16:31:52 -0800351{
buzbeefa57c472012-11-21 12:06:18 -0800352 if ((lit < 0) || (lit >= static_cast<int>(sizeof(magic_table)/sizeof(magic_table[0])))) {
buzbeeefc63692012-11-14 16:31:52 -0800353 return false;
354 }
buzbeefa57c472012-11-21 12:06:18 -0800355 DividePattern pattern = magic_table[lit].pattern;
buzbeeefc63692012-11-14 16:31:52 -0800356 if (pattern == DivideNone) {
357 return false;
358 }
359 // Tuning: add rem patterns
buzbeefa57c472012-11-21 12:06:18 -0800360 if (dalvik_opcode != Instruction::DIV_INT_LIT8) {
buzbeeefc63692012-11-14 16:31:52 -0800361 return false;
362 }
363
buzbeefa57c472012-11-21 12:06:18 -0800364 int r_magic = AllocTemp(cu);
365 LoadConstant(cu, r_magic, magic_table[lit].magic);
366 rl_src = LoadValue(cu, rl_src, kCoreReg);
367 RegLocation rl_result = EvalLoc(cu, rl_dest, kCoreReg, true);
368 int r_hi = AllocTemp(cu);
369 int r_lo = AllocTemp(cu);
370 NewLIR4(cu, kThumb2Smull, r_lo, r_hi, r_magic, rl_src.low_reg);
buzbeeefc63692012-11-14 16:31:52 -0800371 switch(pattern) {
372 case Divide3:
buzbeefa57c472012-11-21 12:06:18 -0800373 OpRegRegRegShift(cu, kOpSub, rl_result.low_reg, r_hi,
374 rl_src.low_reg, EncodeShift(kArmAsr, 31));
buzbeeefc63692012-11-14 16:31:52 -0800375 break;
376 case Divide5:
buzbeefa57c472012-11-21 12:06:18 -0800377 OpRegRegImm(cu, kOpAsr, r_lo, rl_src.low_reg, 31);
378 OpRegRegRegShift(cu, kOpRsub, rl_result.low_reg, r_lo, r_hi,
379 EncodeShift(kArmAsr, magic_table[lit].shift));
buzbeeefc63692012-11-14 16:31:52 -0800380 break;
381 case Divide7:
buzbeefa57c472012-11-21 12:06:18 -0800382 OpRegReg(cu, kOpAdd, r_hi, rl_src.low_reg);
383 OpRegRegImm(cu, kOpAsr, r_lo, rl_src.low_reg, 31);
384 OpRegRegRegShift(cu, kOpRsub, rl_result.low_reg, r_lo, r_hi,
385 EncodeShift(kArmAsr, magic_table[lit].shift));
buzbeeefc63692012-11-14 16:31:52 -0800386 break;
387 default:
buzbeecbd6d442012-11-17 14:11:25 -0800388 LOG(FATAL) << "Unexpected pattern: " << pattern;
buzbeeefc63692012-11-14 16:31:52 -0800389 }
buzbeefa57c472012-11-21 12:06:18 -0800390 StoreValue(cu, rl_dest, rl_result);
buzbeeefc63692012-11-14 16:31:52 -0800391 return true;
392}
393
buzbee02031b12012-11-23 09:41:35 -0800394LIR* ArmCodegen::GenRegMemCheck(CompilationUnit* cu, ConditionCode c_code,
buzbeeefc63692012-11-14 16:31:52 -0800395 int reg1, int base, int offset, ThrowKind kind)
396{
buzbee52a77fc2012-11-20 19:50:46 -0800397 LOG(FATAL) << "Unexpected use of GenRegMemCheck for Arm";
buzbeeefc63692012-11-14 16:31:52 -0800398 return NULL;
399}
400
buzbee02031b12012-11-23 09:41:35 -0800401RegLocation ArmCodegen::GenDivRemLit(CompilationUnit* cu, RegLocation rl_dest, int reg1, int lit,
402 bool is_div)
buzbeeefc63692012-11-14 16:31:52 -0800403{
buzbee52a77fc2012-11-20 19:50:46 -0800404 LOG(FATAL) << "Unexpected use of GenDivRemLit for Arm";
buzbeefa57c472012-11-21 12:06:18 -0800405 return rl_dest;
buzbeeefc63692012-11-14 16:31:52 -0800406}
407
buzbee02031b12012-11-23 09:41:35 -0800408RegLocation ArmCodegen::GenDivRem(CompilationUnit* cu, RegLocation rl_dest, int reg1, int reg2,
409 bool is_div)
buzbeeefc63692012-11-14 16:31:52 -0800410{
buzbee52a77fc2012-11-20 19:50:46 -0800411 LOG(FATAL) << "Unexpected use of GenDivRem for Arm";
buzbeefa57c472012-11-21 12:06:18 -0800412 return rl_dest;
buzbeeefc63692012-11-14 16:31:52 -0800413}
414
buzbee02031b12012-11-23 09:41:35 -0800415bool ArmCodegen::GenInlinedMinMaxInt(CompilationUnit *cu, CallInfo* info, bool is_min)
buzbeeefc63692012-11-14 16:31:52 -0800416{
buzbeefa57c472012-11-21 12:06:18 -0800417 DCHECK_EQ(cu->instruction_set, kThumb2);
418 RegLocation rl_src1 = info->args[0];
419 RegLocation rl_src2 = info->args[1];
420 rl_src1 = LoadValue(cu, rl_src1, kCoreReg);
421 rl_src2 = LoadValue(cu, rl_src2, kCoreReg);
422 RegLocation rl_dest = InlineTarget(cu, info);
423 RegLocation rl_result = EvalLoc(cu, rl_dest, kCoreReg, true);
424 OpRegReg(cu, kOpCmp, rl_src1.low_reg, rl_src2.low_reg);
buzbee02031b12012-11-23 09:41:35 -0800425 OpIT(cu, (is_min) ? kCondGt : kCondLt, "E");
buzbeefa57c472012-11-21 12:06:18 -0800426 OpRegReg(cu, kOpMov, rl_result.low_reg, rl_src2.low_reg);
427 OpRegReg(cu, kOpMov, rl_result.low_reg, rl_src1.low_reg);
428 GenBarrier(cu);
429 StoreValue(cu, rl_dest, rl_result);
buzbeeefc63692012-11-14 16:31:52 -0800430 return true;
431}
432
buzbee02031b12012-11-23 09:41:35 -0800433void ArmCodegen::OpLea(CompilationUnit* cu, int rBase, int reg1, int reg2, int scale, int offset)
buzbeeefc63692012-11-14 16:31:52 -0800434{
buzbee52a77fc2012-11-20 19:50:46 -0800435 LOG(FATAL) << "Unexpected use of OpLea for Arm";
buzbeeefc63692012-11-14 16:31:52 -0800436}
437
buzbee02031b12012-11-23 09:41:35 -0800438void ArmCodegen::OpTlsCmp(CompilationUnit* cu, int offset, int val)
buzbeeefc63692012-11-14 16:31:52 -0800439{
buzbee52a77fc2012-11-20 19:50:46 -0800440 LOG(FATAL) << "Unexpected use of OpTlsCmp for Arm";
buzbeeefc63692012-11-14 16:31:52 -0800441}
442
buzbee02031b12012-11-23 09:41:35 -0800443bool ArmCodegen::GenInlinedCas32(CompilationUnit* cu, CallInfo* info, bool need_write_barrier) {
buzbeefa57c472012-11-21 12:06:18 -0800444 DCHECK_EQ(cu->instruction_set, kThumb2);
445 // Unused - RegLocation rl_src_unsafe = info->args[0];
446 RegLocation rl_src_obj= info->args[1]; // Object - known non-null
447 RegLocation rl_src_offset= info->args[2]; // long low
448 rl_src_offset.wide = 0; // ignore high half in info->args[3]
449 RegLocation rl_src_expected= info->args[4]; // int or Object
450 RegLocation rl_src_new_value= info->args[5]; // int or Object
451 RegLocation rl_dest = InlineTarget(cu, info); // boolean place for result
buzbeeefc63692012-11-14 16:31:52 -0800452
453
buzbee1bc37c62012-11-20 13:35:41 -0800454 // Release store semantics, get the barrier out of the way. TODO: revisit
buzbeefa57c472012-11-21 12:06:18 -0800455 GenMemBarrier(cu, kStoreLoad);
buzbeeefc63692012-11-14 16:31:52 -0800456
buzbeefa57c472012-11-21 12:06:18 -0800457 RegLocation rl_object = LoadValue(cu, rl_src_obj, kCoreReg);
458 RegLocation rl_new_value = LoadValue(cu, rl_src_new_value, kCoreReg);
buzbeeefc63692012-11-14 16:31:52 -0800459
460 if (need_write_barrier) {
461 // Mark card for object assuming new value is stored.
buzbeefa57c472012-11-21 12:06:18 -0800462 MarkGCCard(cu, rl_new_value.low_reg, rl_object.low_reg);
buzbeeefc63692012-11-14 16:31:52 -0800463 }
464
buzbeefa57c472012-11-21 12:06:18 -0800465 RegLocation rl_offset = LoadValue(cu, rl_src_offset, kCoreReg);
buzbeeefc63692012-11-14 16:31:52 -0800466
buzbeefa57c472012-11-21 12:06:18 -0800467 int r_ptr = AllocTemp(cu);
468 OpRegRegReg(cu, kOpAdd, r_ptr, rl_object.low_reg, rl_offset.low_reg);
buzbeeefc63692012-11-14 16:31:52 -0800469
buzbeefa57c472012-11-21 12:06:18 -0800470 // Free now unneeded rl_object and rl_offset to give more temps.
471 ClobberSReg(cu, rl_object.s_reg_low);
472 FreeTemp(cu, rl_object.low_reg);
473 ClobberSReg(cu, rl_offset.s_reg_low);
474 FreeTemp(cu, rl_offset.low_reg);
buzbeeefc63692012-11-14 16:31:52 -0800475
buzbeefa57c472012-11-21 12:06:18 -0800476 int r_old_value = AllocTemp(cu);
477 NewLIR3(cu, kThumb2Ldrex, r_old_value, r_ptr, 0); // r_old_value := [r_ptr]
buzbeeefc63692012-11-14 16:31:52 -0800478
buzbeefa57c472012-11-21 12:06:18 -0800479 RegLocation rl_expected = LoadValue(cu, rl_src_expected, kCoreReg);
buzbeeefc63692012-11-14 16:31:52 -0800480
buzbeefa57c472012-11-21 12:06:18 -0800481 // if (r_old_value == rExpected) {
482 // [r_ptr] <- r_new_value && r_result := success ? 0 : 1
483 // r_result ^= 1
buzbeeefc63692012-11-14 16:31:52 -0800484 // } else {
buzbeefa57c472012-11-21 12:06:18 -0800485 // r_result := 0
buzbeeefc63692012-11-14 16:31:52 -0800486 // }
buzbeefa57c472012-11-21 12:06:18 -0800487 OpRegReg(cu, kOpCmp, r_old_value, rl_expected.low_reg);
488 FreeTemp(cu, r_old_value); // Now unneeded.
489 RegLocation rl_result = EvalLoc(cu, rl_dest, kCoreReg, true);
buzbee02031b12012-11-23 09:41:35 -0800490 OpIT(cu, kCondEq, "TE");
buzbeefa57c472012-11-21 12:06:18 -0800491 NewLIR4(cu, kThumb2Strex, rl_result.low_reg, rl_new_value.low_reg, r_ptr, 0);
492 FreeTemp(cu, r_ptr); // Now unneeded.
493 OpRegImm(cu, kOpXor, rl_result.low_reg, 1);
494 OpRegReg(cu, kOpXor, rl_result.low_reg, rl_result.low_reg);
buzbeeefc63692012-11-14 16:31:52 -0800495
buzbeefa57c472012-11-21 12:06:18 -0800496 StoreValue(cu, rl_dest, rl_result);
buzbeeefc63692012-11-14 16:31:52 -0800497
498 return true;
499}
500
buzbee02031b12012-11-23 09:41:35 -0800501LIR* ArmCodegen::OpPcRelLoad(CompilationUnit* cu, int reg, LIR* target)
buzbeeefc63692012-11-14 16:31:52 -0800502{
buzbeefa57c472012-11-21 12:06:18 -0800503 return RawLIR(cu, cu->current_dalvik_offset, kThumb2LdrPcRel12, reg, 0, 0, 0, 0, target);
buzbeeefc63692012-11-14 16:31:52 -0800504}
505
buzbee02031b12012-11-23 09:41:35 -0800506LIR* ArmCodegen::OpVldm(CompilationUnit* cu, int rBase, int count)
buzbeeefc63692012-11-14 16:31:52 -0800507{
buzbeefa57c472012-11-21 12:06:18 -0800508 return NewLIR3(cu, kThumb2Vldms, rBase, fr0, count);
buzbeeefc63692012-11-14 16:31:52 -0800509}
510
buzbee02031b12012-11-23 09:41:35 -0800511LIR* ArmCodegen::OpVstm(CompilationUnit* cu, int rBase, int count)
buzbeeefc63692012-11-14 16:31:52 -0800512{
buzbeefa57c472012-11-21 12:06:18 -0800513 return NewLIR3(cu, kThumb2Vstms, rBase, fr0, count);
buzbeeefc63692012-11-14 16:31:52 -0800514}
515
buzbee02031b12012-11-23 09:41:35 -0800516void ArmCodegen::GenMultiplyByTwoBitMultiplier(CompilationUnit* cu, RegLocation rl_src,
517 RegLocation rl_result, int lit,
518 int first_bit, int second_bit)
buzbeeefc63692012-11-14 16:31:52 -0800519{
buzbeefa57c472012-11-21 12:06:18 -0800520 OpRegRegRegShift(cu, kOpAdd, rl_result.low_reg, rl_src.low_reg, rl_src.low_reg,
521 EncodeShift(kArmLsl, second_bit - first_bit));
522 if (first_bit != 0) {
523 OpRegRegImm(cu, kOpLsl, rl_result.low_reg, rl_result.low_reg, first_bit);
buzbeeefc63692012-11-14 16:31:52 -0800524 }
525}
526
buzbee02031b12012-11-23 09:41:35 -0800527void ArmCodegen::GenDivZeroCheck(CompilationUnit* cu, int reg_lo, int reg_hi)
buzbeeefc63692012-11-14 16:31:52 -0800528{
buzbeefa57c472012-11-21 12:06:18 -0800529 int t_reg = AllocTemp(cu);
530 NewLIR4(cu, kThumb2OrrRRRs, t_reg, reg_lo, reg_hi, 0);
531 FreeTemp(cu, t_reg);
532 GenCheck(cu, kCondEq, kThrowDivZero);
buzbeeefc63692012-11-14 16:31:52 -0800533}
534
535// Test suspend flag, return target of taken suspend branch
buzbee02031b12012-11-23 09:41:35 -0800536LIR* ArmCodegen::OpTestSuspend(CompilationUnit* cu, LIR* target)
buzbeeefc63692012-11-14 16:31:52 -0800537{
buzbeefa57c472012-11-21 12:06:18 -0800538 NewLIR2(cu, kThumbSubRI8, rARM_SUSPEND, 1);
539 return OpCondBranch(cu, (target == NULL) ? kCondEq : kCondNe, target);
buzbeeefc63692012-11-14 16:31:52 -0800540}
541
542// Decrement register and branch on condition
buzbee02031b12012-11-23 09:41:35 -0800543LIR* ArmCodegen::OpDecAndBranch(CompilationUnit* cu, ConditionCode c_code, int reg, LIR* target)
buzbeeefc63692012-11-14 16:31:52 -0800544{
545 // Combine sub & test using sub setflags encoding here
buzbeefa57c472012-11-21 12:06:18 -0800546 NewLIR3(cu, kThumb2SubsRRI12, reg, reg, 1);
547 return OpCondBranch(cu, c_code, target);
buzbeeefc63692012-11-14 16:31:52 -0800548}
549
buzbee02031b12012-11-23 09:41:35 -0800550void ArmCodegen::GenMemBarrier(CompilationUnit* cu, MemBarrierKind barrier_kind)
buzbeeefc63692012-11-14 16:31:52 -0800551{
552#if ANDROID_SMP != 0
buzbeefa57c472012-11-21 12:06:18 -0800553 int dmb_flavor;
buzbee1bc37c62012-11-20 13:35:41 -0800554 // TODO: revisit Arm barrier kinds
buzbeefa57c472012-11-21 12:06:18 -0800555 switch (barrier_kind) {
556 case kLoadStore: dmb_flavor = kSY; break;
557 case kLoadLoad: dmb_flavor = kSY; break;
558 case kStoreStore: dmb_flavor = kST; break;
559 case kStoreLoad: dmb_flavor = kSY; break;
buzbee1bc37c62012-11-20 13:35:41 -0800560 default:
buzbeefa57c472012-11-21 12:06:18 -0800561 LOG(FATAL) << "Unexpected MemBarrierKind: " << barrier_kind;
562 dmb_flavor = kSY; // quiet gcc.
buzbee1bc37c62012-11-20 13:35:41 -0800563 break;
564 }
buzbeefa57c472012-11-21 12:06:18 -0800565 LIR* dmb = NewLIR1(cu, kThumb2Dmb, dmb_flavor);
566 dmb->def_mask = ENCODE_ALL;
buzbeeefc63692012-11-14 16:31:52 -0800567#endif
568}
569
buzbee02031b12012-11-23 09:41:35 -0800570bool ArmCodegen::GenNegLong(CompilationUnit* cu, RegLocation rl_dest, RegLocation rl_src)
buzbeeefc63692012-11-14 16:31:52 -0800571{
buzbeefa57c472012-11-21 12:06:18 -0800572 rl_src = LoadValueWide(cu, rl_src, kCoreReg);
573 RegLocation rl_result = EvalLoc(cu, rl_dest, kCoreReg, true);
574 int z_reg = AllocTemp(cu);
575 LoadConstantNoClobber(cu, z_reg, 0);
buzbeeefc63692012-11-14 16:31:52 -0800576 // Check for destructive overlap
buzbeefa57c472012-11-21 12:06:18 -0800577 if (rl_result.low_reg == rl_src.high_reg) {
578 int t_reg = AllocTemp(cu);
579 OpRegRegReg(cu, kOpSub, rl_result.low_reg, z_reg, rl_src.low_reg);
580 OpRegRegReg(cu, kOpSbc, rl_result.high_reg, z_reg, t_reg);
581 FreeTemp(cu, t_reg);
buzbeeefc63692012-11-14 16:31:52 -0800582 } else {
buzbeefa57c472012-11-21 12:06:18 -0800583 OpRegRegReg(cu, kOpSub, rl_result.low_reg, z_reg, rl_src.low_reg);
584 OpRegRegReg(cu, kOpSbc, rl_result.high_reg, z_reg, rl_src.high_reg);
buzbeeefc63692012-11-14 16:31:52 -0800585 }
buzbeefa57c472012-11-21 12:06:18 -0800586 FreeTemp(cu, z_reg);
587 StoreValueWide(cu, rl_dest, rl_result);
buzbeeefc63692012-11-14 16:31:52 -0800588 return false;
589}
590
buzbee4ef3e452012-12-14 13:35:28 -0800591
592 /*
593 * Check to see if a result pair has a misaligned overlap with an operand pair. This
594 * is not usual for dx to generate, but it is legal (for now). In a future rev of
595 * dex, we'll want to make this case illegal.
596 */
597static bool BadOverlap(CompilationUnit* cu, RegLocation rl_src, RegLocation rl_dest)
598{
599 DCHECK(rl_src.wide);
600 DCHECK(rl_dest.wide);
601 return (abs(SRegToVReg(cu, rl_src.s_reg_low) - SRegToVReg(cu, rl_dest.s_reg_low)) == 1);
602}
603
604void ArmCodegen::GenMulLong(CompilationUnit* cu, RegLocation rl_dest, RegLocation rl_src1,
605 RegLocation rl_src2)
606{
607 /*
608 * To pull off inline multiply, we have a worst-case requirement of 8 temporary
609 * registers. Normally for Arm, we get 5. We can get to 6 by including
610 * lr in the temp set. The only problematic case is all operands and result are
611 * distinct, and none have been promoted. In that case, we can succeed by aggressively
612 * freeing operand temp registers after they are no longer needed. All other cases
613 * can proceed normally. We'll just punt on the case of the result having a misaligned
614 * overlap with either operand and send that case to a runtime handler.
615 */
616 RegLocation rl_result;
617 if (BadOverlap(cu, rl_src1, rl_dest) || (BadOverlap(cu, rl_src2, rl_dest))) {
618 int func_offset = ENTRYPOINT_OFFSET(pLmul);
619 FlushAllRegs(cu);
620 CallRuntimeHelperRegLocationRegLocation(cu, func_offset, rl_src1, rl_src2, false);
621 rl_result = GetReturnWide(cu, false);
622 StoreValueWide(cu, rl_dest, rl_result);
623 return;
624 }
625 // Temporarily add LR to the temp pool, and assign it to tmp1
626 MarkTemp(cu, rARM_LR);
627 FreeTemp(cu, rARM_LR);
628 int tmp1 = rARM_LR;
629 LockTemp(cu, rARM_LR);
630
631 rl_src1 = LoadValueWide(cu, rl_src1, kCoreReg);
632 rl_src2 = LoadValueWide(cu, rl_src2, kCoreReg);
633
634 bool special_case = true;
635 // If operands are the same, or any pair has been promoted we're not the special case.
636 if ((rl_src1.s_reg_low == rl_src2.s_reg_low) ||
637 (!IsTemp(cu, rl_src1.low_reg) && !IsTemp(cu, rl_src1.high_reg)) ||
638 (!IsTemp(cu, rl_src2.low_reg) && !IsTemp(cu, rl_src2.high_reg))) {
639 special_case = false;
640 }
641 // Tuning: if rl_dest has been promoted and is *not* either operand, could use directly.
642 int res_lo = AllocTemp(cu);
643 int res_hi;
644 if (rl_src1.low_reg == rl_src2.low_reg) {
645 res_hi = AllocTemp(cu);
646 NewLIR3(cu, kThumb2MulRRR, tmp1, rl_src1.low_reg, rl_src1.high_reg);
647 NewLIR4(cu, kThumb2Umull, res_lo, res_hi, rl_src1.low_reg, rl_src1.low_reg);
648 OpRegRegRegShift(cu, kOpAdd, res_hi, res_hi, tmp1, EncodeShift(kArmLsl, 1));
649 } else {
650 // In the special case, all temps are now allocated
651 NewLIR3(cu, kThumb2MulRRR, tmp1, rl_src2.low_reg, rl_src1.high_reg);
652 if (special_case) {
653 DCHECK_NE(rl_src1.low_reg, rl_src2.low_reg);
654 DCHECK_NE(rl_src1.high_reg, rl_src2.high_reg);
655 FreeTemp(cu, rl_src1.high_reg);
656 }
657 res_hi = AllocTemp(cu);
658
659 NewLIR4(cu, kThumb2Umull, res_lo, res_hi, rl_src2.low_reg, rl_src1.low_reg);
660 NewLIR4(cu, kThumb2Mla, tmp1, rl_src1.low_reg, rl_src2.high_reg, tmp1);
661 NewLIR4(cu, kThumb2AddRRR, res_hi, tmp1, res_hi, 0);
662 if (special_case) {
663 FreeTemp(cu, rl_src1.low_reg);
664 Clobber(cu, rl_src1.low_reg);
665 Clobber(cu, rl_src1.high_reg);
666 }
667 }
668 FreeTemp(cu, tmp1);
669 rl_result = GetReturnWide(cu, false); // Just using as a template.
670 rl_result.low_reg = res_lo;
671 rl_result.high_reg = res_hi;
672 StoreValueWide(cu, rl_dest, rl_result);
673 // Now, restore lr to its non-temp status.
674 Clobber(cu, rARM_LR);
675 UnmarkTemp(cu, rARM_LR);
676}
677
buzbee02031b12012-11-23 09:41:35 -0800678bool ArmCodegen::GenAddLong(CompilationUnit* cu, RegLocation rl_dest, RegLocation rl_src1,
679 RegLocation rl_src2)
buzbeeefc63692012-11-14 16:31:52 -0800680{
buzbee52a77fc2012-11-20 19:50:46 -0800681 LOG(FATAL) << "Unexpected use of GenAddLong for Arm";
buzbeeefc63692012-11-14 16:31:52 -0800682 return false;
683}
684
buzbee02031b12012-11-23 09:41:35 -0800685bool ArmCodegen::GenSubLong(CompilationUnit* cu, RegLocation rl_dest, RegLocation rl_src1,
686 RegLocation rl_src2)
buzbeeefc63692012-11-14 16:31:52 -0800687{
buzbee52a77fc2012-11-20 19:50:46 -0800688 LOG(FATAL) << "Unexpected use of GenSubLong for Arm";
buzbeeefc63692012-11-14 16:31:52 -0800689 return false;
690}
691
buzbee02031b12012-11-23 09:41:35 -0800692bool ArmCodegen::GenAndLong(CompilationUnit* cu, RegLocation rl_dest, RegLocation rl_src1,
693 RegLocation rl_src2)
buzbeeefc63692012-11-14 16:31:52 -0800694{
buzbee52a77fc2012-11-20 19:50:46 -0800695 LOG(FATAL) << "Unexpected use of GenAndLong for Arm";
buzbeeefc63692012-11-14 16:31:52 -0800696 return false;
697}
698
buzbee02031b12012-11-23 09:41:35 -0800699bool ArmCodegen::GenOrLong(CompilationUnit* cu, RegLocation rl_dest, RegLocation rl_src1,
700 RegLocation rl_src2)
buzbeeefc63692012-11-14 16:31:52 -0800701{
buzbee52a77fc2012-11-20 19:50:46 -0800702 LOG(FATAL) << "Unexpected use of GenOrLong for Arm";
buzbeeefc63692012-11-14 16:31:52 -0800703 return false;
704}
705
buzbee02031b12012-11-23 09:41:35 -0800706bool ArmCodegen::GenXorLong(CompilationUnit* cu, RegLocation rl_dest, RegLocation rl_src1,
707 RegLocation rl_src2)
buzbeeefc63692012-11-14 16:31:52 -0800708{
709 LOG(FATAL) << "Unexpected use of genXoLong for Arm";
710 return false;
711}
712
buzbeee6285f92012-12-06 15:57:46 -0800713/*
714 * Generate array load
715 */
716void ArmCodegen::GenArrayGet(CompilationUnit* cu, int opt_flags, OpSize size, RegLocation rl_array,
717 RegLocation rl_index, RegLocation rl_dest, int scale)
718{
719 RegisterClass reg_class = oat_reg_class_by_size(size);
Ian Rogers2dd0e2c2013-01-24 12:42:14 -0800720 int len_offset = mirror::Array::LengthOffset().Int32Value();
buzbeee6285f92012-12-06 15:57:46 -0800721 int data_offset;
722 RegLocation rl_result;
buzbee4ef3e452012-12-14 13:35:28 -0800723 bool constant_index = rl_index.is_const;
buzbeee6285f92012-12-06 15:57:46 -0800724 rl_array = LoadValue(cu, rl_array, kCoreReg);
buzbee4ef3e452012-12-14 13:35:28 -0800725 if (!constant_index) {
726 rl_index = LoadValue(cu, rl_index, kCoreReg);
727 }
buzbeee6285f92012-12-06 15:57:46 -0800728
729 if (rl_dest.wide) {
Ian Rogers2dd0e2c2013-01-24 12:42:14 -0800730 data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Int32Value();
buzbeee6285f92012-12-06 15:57:46 -0800731 } else {
Ian Rogers2dd0e2c2013-01-24 12:42:14 -0800732 data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Int32Value();
buzbeee6285f92012-12-06 15:57:46 -0800733 }
734
buzbee4ef3e452012-12-14 13:35:28 -0800735 // If index is constant, just fold it into the data offset
736 if (constant_index) {
737 data_offset += ConstantValue(cu, rl_index) << scale;
738 }
739
buzbeee6285f92012-12-06 15:57:46 -0800740 /* null object? */
741 GenNullCheck(cu, rl_array.s_reg_low, rl_array.low_reg, opt_flags);
742
743 bool needs_range_check = (!(opt_flags & MIR_IGNORE_RANGE_CHECK));
744 int reg_len = INVALID_REG;
745 if (needs_range_check) {
746 reg_len = AllocTemp(cu);
747 /* Get len */
748 LoadWordDisp(cu, rl_array.low_reg, len_offset, reg_len);
749 }
buzbee4ef3e452012-12-14 13:35:28 -0800750 if (rl_dest.wide || rl_dest.fp || constant_index) {
751 int reg_ptr;
752 if (constant_index) {
753 reg_ptr = rl_array.low_reg; // NOTE: must not alter reg_ptr in constant case.
754 } else {
755 // No special indexed operation, lea + load w/ displacement
756 reg_ptr = AllocTemp(cu);
757 OpRegRegRegShift(cu, kOpAdd, reg_ptr, rl_array.low_reg, rl_index.low_reg,
758 EncodeShift(kArmLsl, scale));
759 FreeTemp(cu, rl_index.low_reg);
760 }
buzbeee6285f92012-12-06 15:57:46 -0800761 rl_result = EvalLoc(cu, rl_dest, reg_class, true);
762
763 if (needs_range_check) {
buzbee4ef3e452012-12-14 13:35:28 -0800764 if (constant_index) {
765 GenImmedCheck(cu, kCondLs, reg_len, ConstantValue(cu, rl_index), kThrowConstantArrayBounds);
766 } else {
767 GenRegRegCheck(cu, kCondLs, reg_len, rl_index.low_reg, kThrowArrayBounds);
768 }
buzbeee6285f92012-12-06 15:57:46 -0800769 FreeTemp(cu, reg_len);
770 }
771 if (rl_dest.wide) {
772 LoadBaseDispWide(cu, reg_ptr, data_offset, rl_result.low_reg, rl_result.high_reg, INVALID_SREG);
buzbee4ef3e452012-12-14 13:35:28 -0800773 if (!constant_index) {
774 FreeTemp(cu, reg_ptr);
775 }
buzbeee6285f92012-12-06 15:57:46 -0800776 StoreValueWide(cu, rl_dest, rl_result);
777 } else {
778 LoadBaseDisp(cu, reg_ptr, data_offset, rl_result.low_reg, size, INVALID_SREG);
buzbee4ef3e452012-12-14 13:35:28 -0800779 if (!constant_index) {
780 FreeTemp(cu, reg_ptr);
781 }
buzbeee6285f92012-12-06 15:57:46 -0800782 StoreValue(cu, rl_dest, rl_result);
783 }
784 } else {
785 // Offset base, then use indexed load
786 int reg_ptr = AllocTemp(cu);
787 OpRegRegImm(cu, kOpAdd, reg_ptr, rl_array.low_reg, data_offset);
788 FreeTemp(cu, rl_array.low_reg);
789 rl_result = EvalLoc(cu, rl_dest, reg_class, true);
790
791 if (needs_range_check) {
792 // TODO: change kCondCS to a more meaningful name, is the sense of
793 // carry-set/clear flipped?
794 GenRegRegCheck(cu, kCondCs, rl_index.low_reg, reg_len, kThrowArrayBounds);
795 FreeTemp(cu, reg_len);
796 }
797 LoadBaseIndexed(cu, reg_ptr, rl_index.low_reg, rl_result.low_reg, scale, size);
798 FreeTemp(cu, reg_ptr);
799 StoreValue(cu, rl_dest, rl_result);
800 }
801}
802
803/*
804 * Generate array store
805 *
806 */
807void ArmCodegen::GenArrayPut(CompilationUnit* cu, int opt_flags, OpSize size, RegLocation rl_array,
808 RegLocation rl_index, RegLocation rl_src, int scale)
809{
810 RegisterClass reg_class = oat_reg_class_by_size(size);
Ian Rogers2dd0e2c2013-01-24 12:42:14 -0800811 int len_offset = mirror::Array::LengthOffset().Int32Value();
buzbeee6285f92012-12-06 15:57:46 -0800812 int data_offset;
buzbee4ef3e452012-12-14 13:35:28 -0800813 bool constant_index = rl_index.is_const;
buzbeee6285f92012-12-06 15:57:46 -0800814
buzbee4ef3e452012-12-14 13:35:28 -0800815 if (rl_src.wide) {
Ian Rogers2dd0e2c2013-01-24 12:42:14 -0800816 data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Int32Value();
buzbeee6285f92012-12-06 15:57:46 -0800817 } else {
Ian Rogers2dd0e2c2013-01-24 12:42:14 -0800818 data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Int32Value();
buzbeee6285f92012-12-06 15:57:46 -0800819 }
820
buzbee4ef3e452012-12-14 13:35:28 -0800821 // If index is constant, just fold it into the data offset.
822 if (constant_index) {
823 data_offset += ConstantValue(cu, rl_index) << scale;
824 }
825
buzbeee6285f92012-12-06 15:57:46 -0800826 rl_array = LoadValue(cu, rl_array, kCoreReg);
buzbee4ef3e452012-12-14 13:35:28 -0800827 if (!constant_index) {
828 rl_index = LoadValue(cu, rl_index, kCoreReg);
829 }
830
831 int reg_ptr;
832 if (constant_index) {
833 reg_ptr = rl_array.low_reg;
834 } else if (IsTemp(cu, rl_array.low_reg)) {
buzbeee6285f92012-12-06 15:57:46 -0800835 Clobber(cu, rl_array.low_reg);
836 reg_ptr = rl_array.low_reg;
837 } else {
838 reg_ptr = AllocTemp(cu);
839 }
840
841 /* null object? */
842 GenNullCheck(cu, rl_array.s_reg_low, rl_array.low_reg, opt_flags);
843
844 bool needs_range_check = (!(opt_flags & MIR_IGNORE_RANGE_CHECK));
845 int reg_len = INVALID_REG;
846 if (needs_range_check) {
847 reg_len = AllocTemp(cu);
848 //NOTE: max live temps(4) here.
849 /* Get len */
850 LoadWordDisp(cu, rl_array.low_reg, len_offset, reg_len);
851 }
852 /* at this point, reg_ptr points to array, 2 live temps */
buzbee4ef3e452012-12-14 13:35:28 -0800853 if (rl_src.wide || rl_src.fp || constant_index) {
buzbeee6285f92012-12-06 15:57:46 -0800854 if (rl_src.wide) {
855 rl_src = LoadValueWide(cu, rl_src, reg_class);
856 } else {
857 rl_src = LoadValue(cu, rl_src, reg_class);
858 }
buzbee4ef3e452012-12-14 13:35:28 -0800859 if (!constant_index) {
860 OpRegRegRegShift(cu, kOpAdd, reg_ptr, rl_array.low_reg, rl_index.low_reg,
861 EncodeShift(kArmLsl, scale));
862 }
buzbeee6285f92012-12-06 15:57:46 -0800863 if (needs_range_check) {
buzbee4ef3e452012-12-14 13:35:28 -0800864 if (constant_index) {
865 GenImmedCheck(cu, kCondLs, reg_len, ConstantValue(cu, rl_index), kThrowConstantArrayBounds);
866 } else {
867 GenRegRegCheck(cu, kCondLs, reg_len, rl_index.low_reg, kThrowArrayBounds);
868 }
buzbeee6285f92012-12-06 15:57:46 -0800869 FreeTemp(cu, reg_len);
870 }
buzbee4ef3e452012-12-14 13:35:28 -0800871
buzbeee6285f92012-12-06 15:57:46 -0800872 if (rl_src.wide) {
873 StoreBaseDispWide(cu, reg_ptr, data_offset, rl_src.low_reg, rl_src.high_reg);
874 } else {
875 StoreBaseDisp(cu, reg_ptr, data_offset, rl_src.low_reg, size);
876 }
877 } else {
878 /* reg_ptr -> array data */
879 OpRegRegImm(cu, kOpAdd, reg_ptr, rl_array.low_reg, data_offset);
880 rl_src = LoadValue(cu, rl_src, reg_class);
881 if (needs_range_check) {
882 GenRegRegCheck(cu, kCondCs, rl_index.low_reg, reg_len, kThrowArrayBounds);
883 FreeTemp(cu, reg_len);
884 }
885 StoreBaseIndexed(cu, reg_ptr, rl_index.low_reg, rl_src.low_reg,
886 scale, size);
887 }
buzbee4ef3e452012-12-14 13:35:28 -0800888 if (!constant_index) {
889 FreeTemp(cu, reg_ptr);
890 }
buzbeee6285f92012-12-06 15:57:46 -0800891}
892
893/*
894 * Generate array store
895 *
896 */
897void ArmCodegen::GenArrayObjPut(CompilationUnit* cu, int opt_flags, RegLocation rl_array,
898 RegLocation rl_index, RegLocation rl_src, int scale)
899{
Ian Rogers2dd0e2c2013-01-24 12:42:14 -0800900 int len_offset = mirror::Array::LengthOffset().Int32Value();
901 int data_offset = mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value();
buzbeee6285f92012-12-06 15:57:46 -0800902
903 FlushAllRegs(cu); // Use explicit registers
904 LockCallTemps(cu);
905
906 int r_value = TargetReg(kArg0); // Register holding value
907 int r_array_class = TargetReg(kArg1); // Register holding array's Class
908 int r_array = TargetReg(kArg2); // Register holding array
909 int r_index = TargetReg(kArg3); // Register holding index into array
910
911 LoadValueDirectFixed(cu, rl_array, r_array); // Grab array
912 LoadValueDirectFixed(cu, rl_src, r_value); // Grab value
913 LoadValueDirectFixed(cu, rl_index, r_index); // Grab index
914
915 GenNullCheck(cu, rl_array.s_reg_low, r_array, opt_flags); // NPE?
916
917 // Store of null?
918 LIR* null_value_check = OpCmpImmBranch(cu, kCondEq, r_value, 0, NULL);
919
920 // Get the array's class.
Ian Rogers2dd0e2c2013-01-24 12:42:14 -0800921 LoadWordDisp(cu, r_array, mirror::Object::ClassOffset().Int32Value(), r_array_class);
buzbeee6285f92012-12-06 15:57:46 -0800922 CallRuntimeHelperRegReg(cu, ENTRYPOINT_OFFSET(pCanPutArrayElementFromCode), r_value,
923 r_array_class, true);
924 // Redo LoadValues in case they didn't survive the call.
925 LoadValueDirectFixed(cu, rl_array, r_array); // Reload array
926 LoadValueDirectFixed(cu, rl_index, r_index); // Reload index
927 LoadValueDirectFixed(cu, rl_src, r_value); // Reload value
928 r_array_class = INVALID_REG;
929
930 // Branch here if value to be stored == null
931 LIR* target = NewLIR0(cu, kPseudoTargetLabel);
932 null_value_check->target = target;
933
934 bool needs_range_check = (!(opt_flags & MIR_IGNORE_RANGE_CHECK));
935 int reg_len = INVALID_REG;
936 if (needs_range_check) {
937 reg_len = TargetReg(kArg1);
938 LoadWordDisp(cu, r_array, len_offset, reg_len); // Get len
939 }
940 /* r_ptr -> array data */
941 int r_ptr = AllocTemp(cu);
942 OpRegRegImm(cu, kOpAdd, r_ptr, r_array, data_offset);
943 if (needs_range_check) {
944 GenRegRegCheck(cu, kCondCs, r_index, reg_len, kThrowArrayBounds);
945 }
946 StoreBaseIndexed(cu, r_ptr, r_index, r_value, scale, kWord);
947 FreeTemp(cu, r_ptr);
948 FreeTemp(cu, r_index);
949 MarkGCCard(cu, r_value, r_array);
950}
951
buzbee4ef3e452012-12-14 13:35:28 -0800952bool ArmCodegen::GenShiftImmOpLong(CompilationUnit* cu, Instruction::Code opcode,
953 RegLocation rl_dest, RegLocation rl_src, RegLocation rl_shift)
954{
955 rl_src = LoadValueWide(cu, rl_src, kCoreReg);
956 // Per spec, we only care about low 6 bits of shift amount.
957 int shift_amount = ConstantValue(cu, rl_shift) & 0x3f;
958 if (shift_amount == 0) {
959 StoreValueWide(cu, rl_dest, rl_src);
960 return false; // TODO: remove useless bool return result.
961 }
962 if (BadOverlap(cu, rl_src, rl_dest)) {
963 return GenShiftOpLong(cu, opcode, rl_dest, rl_src, rl_shift);
964 }
965 RegLocation rl_result = EvalLoc(cu, rl_dest, kCoreReg, true);
966 switch(opcode) {
967 case Instruction::SHL_LONG:
968 case Instruction::SHL_LONG_2ADDR:
969 if (shift_amount == 1) {
970 OpRegRegReg(cu, kOpAdd, rl_result.low_reg, rl_src.low_reg, rl_src.low_reg);
971 OpRegRegReg(cu, kOpAdc, rl_result.high_reg, rl_src.high_reg, rl_src.high_reg);
972 } else if (shift_amount == 32) {
973 OpRegCopy(cu, rl_result.high_reg, rl_src.low_reg);
974 LoadConstant(cu, rl_result.low_reg, 0);
975 } else if (shift_amount > 31) {
976 OpRegRegImm(cu, kOpLsl, rl_result.high_reg, rl_src.low_reg, shift_amount - 32);
977 LoadConstant(cu, rl_result.low_reg, 0);
978 } else {
979 OpRegRegImm(cu, kOpLsl, rl_result.high_reg, rl_src.high_reg, shift_amount);
980 OpRegRegRegShift(cu, kOpOr, rl_result.high_reg, rl_result.high_reg, rl_src.low_reg,
981 EncodeShift(kArmLsr, 32 - shift_amount));
982 OpRegRegImm(cu, kOpLsl, rl_result.low_reg, rl_src.low_reg, shift_amount);
983 }
984 break;
985 case Instruction::SHR_LONG:
986 case Instruction::SHR_LONG_2ADDR:
987 if (shift_amount == 32) {
988 OpRegCopy(cu, rl_result.low_reg, rl_src.high_reg);
989 OpRegRegImm(cu, kOpAsr, rl_result.high_reg, rl_src.high_reg, 31);
990 } else if (shift_amount > 31) {
991 OpRegRegImm(cu, kOpAsr, rl_result.low_reg, rl_src.high_reg, shift_amount - 32);
992 OpRegRegImm(cu, kOpAsr, rl_result.high_reg, rl_src.high_reg, 31);
993 } else {
994 int t_reg = AllocTemp(cu);
995 OpRegRegImm(cu, kOpLsr, t_reg, rl_src.low_reg, shift_amount);
996 OpRegRegRegShift(cu, kOpOr, rl_result.low_reg, t_reg, rl_src.high_reg,
997 EncodeShift(kArmLsl, 32 - shift_amount));
998 FreeTemp(cu, t_reg);
999 OpRegRegImm(cu, kOpAsr, rl_result.high_reg, rl_src.high_reg, shift_amount);
1000 }
1001 break;
1002 case Instruction::USHR_LONG:
1003 case Instruction::USHR_LONG_2ADDR:
1004 if (shift_amount == 32) {
1005 OpRegCopy(cu, rl_result.low_reg, rl_src.high_reg);
1006 LoadConstant(cu, rl_result.high_reg, 0);
1007 } else if (shift_amount > 31) {
1008 OpRegRegImm(cu, kOpLsr, rl_result.low_reg, rl_src.high_reg, shift_amount - 32);
1009 LoadConstant(cu, rl_result.high_reg, 0);
1010 } else {
1011 int t_reg = AllocTemp(cu);
1012 OpRegRegImm(cu, kOpLsr, t_reg, rl_src.low_reg, shift_amount);
1013 OpRegRegRegShift(cu, kOpOr, rl_result.low_reg, t_reg, rl_src.high_reg,
1014 EncodeShift(kArmLsl, 32 - shift_amount));
1015 FreeTemp(cu, t_reg);
1016 OpRegRegImm(cu, kOpLsr, rl_result.high_reg, rl_src.high_reg, shift_amount);
1017 }
1018 break;
1019 default:
1020 LOG(FATAL) << "Unexpected case";
1021 return true;
1022 }
1023 StoreValueWide(cu, rl_dest, rl_result);
1024 return false;
1025}
1026
1027bool ArmCodegen::GenArithImmOpLong(CompilationUnit* cu, Instruction::Code opcode,
1028 RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2)
1029{
1030 if ((opcode == Instruction::SUB_LONG_2ADDR) || (opcode == Instruction::SUB_LONG)) {
1031 if (!rl_src2.is_const) {
1032 // Don't bother with special handling for subtract from immediate.
1033 return GenArithOpLong(cu, opcode, rl_dest, rl_src1, rl_src2);
1034 }
1035 } else {
1036 // Normalize
1037 if (!rl_src2.is_const) {
1038 DCHECK(rl_src1.is_const);
1039 RegLocation rl_temp = rl_src1;
1040 rl_src1 = rl_src2;
1041 rl_src2 = rl_temp;
1042 }
1043 }
1044 if (BadOverlap(cu, rl_src1, rl_dest)) {
1045 return GenArithOpLong(cu, opcode, rl_dest, rl_src1, rl_src2);
1046 }
1047 DCHECK(rl_src2.is_const);
1048 int64_t val = ConstantValueWide(cu, rl_src2);
1049 uint32_t val_lo = Low32Bits(val);
1050 uint32_t val_hi = High32Bits(val);
1051 int32_t mod_imm_lo = ModifiedImmediate(val_lo);
1052 int32_t mod_imm_hi = ModifiedImmediate(val_hi);
1053
1054 // Only a subset of add/sub immediate instructions set carry - so bail if we don't fit
1055 switch(opcode) {
1056 case Instruction::ADD_LONG:
1057 case Instruction::ADD_LONG_2ADDR:
1058 case Instruction::SUB_LONG:
1059 case Instruction::SUB_LONG_2ADDR:
1060 if ((mod_imm_lo < 0) || (mod_imm_hi < 0)) {
1061 return GenArithOpLong(cu, opcode, rl_dest, rl_src1, rl_src2);
1062 }
1063 break;
1064 default:
1065 break;
1066 }
1067 rl_src1 = LoadValueWide(cu, rl_src1, kCoreReg);
1068 RegLocation rl_result = EvalLoc(cu, rl_dest, kCoreReg, true);
1069 // NOTE: once we've done the EvalLoc on dest, we can no longer bail.
1070 switch (opcode) {
1071 case Instruction::ADD_LONG:
1072 case Instruction::ADD_LONG_2ADDR:
1073 NewLIR3(cu, kThumb2AddRRI8, rl_result.low_reg, rl_src1.low_reg, mod_imm_lo);
1074 NewLIR3(cu, kThumb2AdcRRI8, rl_result.high_reg, rl_src1.high_reg, mod_imm_hi);
1075 break;
1076 case Instruction::OR_LONG:
1077 case Instruction::OR_LONG_2ADDR:
1078 if ((val_lo != 0) || (rl_result.low_reg != rl_src1.low_reg)) {
1079 OpRegRegImm(cu, kOpOr, rl_result.low_reg, rl_src1.low_reg, val_lo);
1080 }
1081 if ((val_hi != 0) || (rl_result.high_reg != rl_src1.high_reg)) {
1082 OpRegRegImm(cu, kOpOr, rl_result.high_reg, rl_src1.high_reg, val_hi);
1083 }
1084 break;
1085 case Instruction::XOR_LONG:
1086 case Instruction::XOR_LONG_2ADDR:
1087 OpRegRegImm(cu, kOpXor, rl_result.low_reg, rl_src1.low_reg, val_lo);
1088 OpRegRegImm(cu, kOpXor, rl_result.high_reg, rl_src1.high_reg, val_hi);
1089 break;
1090 case Instruction::AND_LONG:
1091 case Instruction::AND_LONG_2ADDR:
1092 if ((val_lo != 0xffffffff) || (rl_result.low_reg != rl_src1.low_reg)) {
1093 OpRegRegImm(cu, kOpAnd, rl_result.low_reg, rl_src1.low_reg, val_lo);
1094 }
1095 if ((val_hi != 0xffffffff) || (rl_result.high_reg != rl_src1.high_reg)) {
1096 OpRegRegImm(cu, kOpAnd, rl_result.high_reg, rl_src1.high_reg, val_hi);
1097 }
1098 break;
1099 case Instruction::SUB_LONG_2ADDR:
1100 case Instruction::SUB_LONG:
1101 NewLIR3(cu, kThumb2SubRRI8, rl_result.low_reg, rl_src1.low_reg, mod_imm_lo);
1102 NewLIR3(cu, kThumb2SbcRRI8, rl_result.high_reg, rl_src1.high_reg, mod_imm_hi);
1103 break;
1104 default:
1105 LOG(FATAL) << "Unexpected opcode " << opcode;
1106 }
1107 StoreValueWide(cu, rl_dest, rl_result);
1108 return false; // TODO: remove bool return value from all of these Gen routines.
1109}
1110
buzbeeefc63692012-11-14 16:31:52 -08001111} // namespace art