blob: 857ec93817bc7aba6995ba1d3756456133ceaa35 [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
buzbeea5954be2013-02-07 10:41:40 -0800570void 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}
589
buzbee4ef3e452012-12-14 13:35:28 -0800590
591 /*
592 * Check to see if a result pair has a misaligned overlap with an operand pair. This
593 * is not usual for dx to generate, but it is legal (for now). In a future rev of
594 * dex, we'll want to make this case illegal.
595 */
596static bool BadOverlap(CompilationUnit* cu, RegLocation rl_src, RegLocation rl_dest)
597{
598 DCHECK(rl_src.wide);
599 DCHECK(rl_dest.wide);
600 return (abs(SRegToVReg(cu, rl_src.s_reg_low) - SRegToVReg(cu, rl_dest.s_reg_low)) == 1);
601}
602
603void ArmCodegen::GenMulLong(CompilationUnit* cu, RegLocation rl_dest, RegLocation rl_src1,
604 RegLocation rl_src2)
605{
606 /*
607 * To pull off inline multiply, we have a worst-case requirement of 8 temporary
608 * registers. Normally for Arm, we get 5. We can get to 6 by including
609 * lr in the temp set. The only problematic case is all operands and result are
610 * distinct, and none have been promoted. In that case, we can succeed by aggressively
611 * freeing operand temp registers after they are no longer needed. All other cases
612 * can proceed normally. We'll just punt on the case of the result having a misaligned
613 * overlap with either operand and send that case to a runtime handler.
614 */
615 RegLocation rl_result;
616 if (BadOverlap(cu, rl_src1, rl_dest) || (BadOverlap(cu, rl_src2, rl_dest))) {
617 int func_offset = ENTRYPOINT_OFFSET(pLmul);
618 FlushAllRegs(cu);
619 CallRuntimeHelperRegLocationRegLocation(cu, func_offset, rl_src1, rl_src2, false);
620 rl_result = GetReturnWide(cu, false);
621 StoreValueWide(cu, rl_dest, rl_result);
622 return;
623 }
624 // Temporarily add LR to the temp pool, and assign it to tmp1
625 MarkTemp(cu, rARM_LR);
626 FreeTemp(cu, rARM_LR);
627 int tmp1 = rARM_LR;
628 LockTemp(cu, rARM_LR);
629
630 rl_src1 = LoadValueWide(cu, rl_src1, kCoreReg);
631 rl_src2 = LoadValueWide(cu, rl_src2, kCoreReg);
632
633 bool special_case = true;
634 // If operands are the same, or any pair has been promoted we're not the special case.
635 if ((rl_src1.s_reg_low == rl_src2.s_reg_low) ||
636 (!IsTemp(cu, rl_src1.low_reg) && !IsTemp(cu, rl_src1.high_reg)) ||
637 (!IsTemp(cu, rl_src2.low_reg) && !IsTemp(cu, rl_src2.high_reg))) {
638 special_case = false;
639 }
640 // Tuning: if rl_dest has been promoted and is *not* either operand, could use directly.
641 int res_lo = AllocTemp(cu);
642 int res_hi;
643 if (rl_src1.low_reg == rl_src2.low_reg) {
644 res_hi = AllocTemp(cu);
645 NewLIR3(cu, kThumb2MulRRR, tmp1, rl_src1.low_reg, rl_src1.high_reg);
646 NewLIR4(cu, kThumb2Umull, res_lo, res_hi, rl_src1.low_reg, rl_src1.low_reg);
647 OpRegRegRegShift(cu, kOpAdd, res_hi, res_hi, tmp1, EncodeShift(kArmLsl, 1));
648 } else {
649 // In the special case, all temps are now allocated
650 NewLIR3(cu, kThumb2MulRRR, tmp1, rl_src2.low_reg, rl_src1.high_reg);
651 if (special_case) {
652 DCHECK_NE(rl_src1.low_reg, rl_src2.low_reg);
653 DCHECK_NE(rl_src1.high_reg, rl_src2.high_reg);
654 FreeTemp(cu, rl_src1.high_reg);
655 }
656 res_hi = AllocTemp(cu);
657
658 NewLIR4(cu, kThumb2Umull, res_lo, res_hi, rl_src2.low_reg, rl_src1.low_reg);
659 NewLIR4(cu, kThumb2Mla, tmp1, rl_src1.low_reg, rl_src2.high_reg, tmp1);
660 NewLIR4(cu, kThumb2AddRRR, res_hi, tmp1, res_hi, 0);
661 if (special_case) {
662 FreeTemp(cu, rl_src1.low_reg);
663 Clobber(cu, rl_src1.low_reg);
664 Clobber(cu, rl_src1.high_reg);
665 }
666 }
667 FreeTemp(cu, tmp1);
668 rl_result = GetReturnWide(cu, false); // Just using as a template.
669 rl_result.low_reg = res_lo;
670 rl_result.high_reg = res_hi;
671 StoreValueWide(cu, rl_dest, rl_result);
672 // Now, restore lr to its non-temp status.
673 Clobber(cu, rARM_LR);
674 UnmarkTemp(cu, rARM_LR);
675}
676
buzbeea5954be2013-02-07 10:41:40 -0800677void ArmCodegen::GenAddLong(CompilationUnit* cu, RegLocation rl_dest, RegLocation rl_src1,
buzbee02031b12012-11-23 09:41:35 -0800678 RegLocation rl_src2)
buzbeeefc63692012-11-14 16:31:52 -0800679{
buzbee52a77fc2012-11-20 19:50:46 -0800680 LOG(FATAL) << "Unexpected use of GenAddLong for Arm";
buzbeeefc63692012-11-14 16:31:52 -0800681}
682
buzbeea5954be2013-02-07 10:41:40 -0800683void ArmCodegen::GenSubLong(CompilationUnit* cu, RegLocation rl_dest, RegLocation rl_src1,
buzbee02031b12012-11-23 09:41:35 -0800684 RegLocation rl_src2)
buzbeeefc63692012-11-14 16:31:52 -0800685{
buzbee52a77fc2012-11-20 19:50:46 -0800686 LOG(FATAL) << "Unexpected use of GenSubLong for Arm";
buzbeeefc63692012-11-14 16:31:52 -0800687}
688
buzbeea5954be2013-02-07 10:41:40 -0800689void ArmCodegen::GenAndLong(CompilationUnit* cu, RegLocation rl_dest, RegLocation rl_src1,
buzbee02031b12012-11-23 09:41:35 -0800690 RegLocation rl_src2)
buzbeeefc63692012-11-14 16:31:52 -0800691{
buzbee52a77fc2012-11-20 19:50:46 -0800692 LOG(FATAL) << "Unexpected use of GenAndLong for Arm";
buzbeeefc63692012-11-14 16:31:52 -0800693}
694
buzbeea5954be2013-02-07 10:41:40 -0800695void ArmCodegen::GenOrLong(CompilationUnit* cu, RegLocation rl_dest, RegLocation rl_src1,
buzbee02031b12012-11-23 09:41:35 -0800696 RegLocation rl_src2)
buzbeeefc63692012-11-14 16:31:52 -0800697{
buzbee52a77fc2012-11-20 19:50:46 -0800698 LOG(FATAL) << "Unexpected use of GenOrLong for Arm";
buzbeeefc63692012-11-14 16:31:52 -0800699}
700
buzbeea5954be2013-02-07 10:41:40 -0800701void ArmCodegen::GenXorLong(CompilationUnit* cu, RegLocation rl_dest, RegLocation rl_src1,
buzbee02031b12012-11-23 09:41:35 -0800702 RegLocation rl_src2)
buzbeeefc63692012-11-14 16:31:52 -0800703{
704 LOG(FATAL) << "Unexpected use of genXoLong for Arm";
buzbeeefc63692012-11-14 16:31:52 -0800705}
706
buzbeee6285f92012-12-06 15:57:46 -0800707/*
708 * Generate array load
709 */
710void ArmCodegen::GenArrayGet(CompilationUnit* cu, int opt_flags, OpSize size, RegLocation rl_array,
711 RegLocation rl_index, RegLocation rl_dest, int scale)
712{
713 RegisterClass reg_class = oat_reg_class_by_size(size);
Ian Rogers2dd0e2c2013-01-24 12:42:14 -0800714 int len_offset = mirror::Array::LengthOffset().Int32Value();
buzbeee6285f92012-12-06 15:57:46 -0800715 int data_offset;
716 RegLocation rl_result;
buzbee4ef3e452012-12-14 13:35:28 -0800717 bool constant_index = rl_index.is_const;
buzbeee6285f92012-12-06 15:57:46 -0800718 rl_array = LoadValue(cu, rl_array, kCoreReg);
buzbee4ef3e452012-12-14 13:35:28 -0800719 if (!constant_index) {
720 rl_index = LoadValue(cu, rl_index, kCoreReg);
721 }
buzbeee6285f92012-12-06 15:57:46 -0800722
723 if (rl_dest.wide) {
Ian Rogers2dd0e2c2013-01-24 12:42:14 -0800724 data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Int32Value();
buzbeee6285f92012-12-06 15:57:46 -0800725 } else {
Ian Rogers2dd0e2c2013-01-24 12:42:14 -0800726 data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Int32Value();
buzbeee6285f92012-12-06 15:57:46 -0800727 }
728
buzbee4ef3e452012-12-14 13:35:28 -0800729 // If index is constant, just fold it into the data offset
730 if (constant_index) {
731 data_offset += ConstantValue(cu, rl_index) << scale;
732 }
733
buzbeee6285f92012-12-06 15:57:46 -0800734 /* null object? */
735 GenNullCheck(cu, rl_array.s_reg_low, rl_array.low_reg, opt_flags);
736
737 bool needs_range_check = (!(opt_flags & MIR_IGNORE_RANGE_CHECK));
738 int reg_len = INVALID_REG;
739 if (needs_range_check) {
740 reg_len = AllocTemp(cu);
741 /* Get len */
742 LoadWordDisp(cu, rl_array.low_reg, len_offset, reg_len);
743 }
buzbee4ef3e452012-12-14 13:35:28 -0800744 if (rl_dest.wide || rl_dest.fp || constant_index) {
745 int reg_ptr;
746 if (constant_index) {
747 reg_ptr = rl_array.low_reg; // NOTE: must not alter reg_ptr in constant case.
748 } else {
749 // No special indexed operation, lea + load w/ displacement
750 reg_ptr = AllocTemp(cu);
751 OpRegRegRegShift(cu, kOpAdd, reg_ptr, rl_array.low_reg, rl_index.low_reg,
752 EncodeShift(kArmLsl, scale));
753 FreeTemp(cu, rl_index.low_reg);
754 }
buzbeee6285f92012-12-06 15:57:46 -0800755 rl_result = EvalLoc(cu, rl_dest, reg_class, true);
756
757 if (needs_range_check) {
buzbee4ef3e452012-12-14 13:35:28 -0800758 if (constant_index) {
759 GenImmedCheck(cu, kCondLs, reg_len, ConstantValue(cu, rl_index), kThrowConstantArrayBounds);
760 } else {
761 GenRegRegCheck(cu, kCondLs, reg_len, rl_index.low_reg, kThrowArrayBounds);
762 }
buzbeee6285f92012-12-06 15:57:46 -0800763 FreeTemp(cu, reg_len);
764 }
765 if (rl_dest.wide) {
766 LoadBaseDispWide(cu, reg_ptr, data_offset, rl_result.low_reg, rl_result.high_reg, INVALID_SREG);
buzbee4ef3e452012-12-14 13:35:28 -0800767 if (!constant_index) {
768 FreeTemp(cu, reg_ptr);
769 }
buzbeee6285f92012-12-06 15:57:46 -0800770 StoreValueWide(cu, rl_dest, rl_result);
771 } else {
772 LoadBaseDisp(cu, reg_ptr, data_offset, rl_result.low_reg, size, 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 StoreValue(cu, rl_dest, rl_result);
777 }
778 } else {
779 // Offset base, then use indexed load
780 int reg_ptr = AllocTemp(cu);
781 OpRegRegImm(cu, kOpAdd, reg_ptr, rl_array.low_reg, data_offset);
782 FreeTemp(cu, rl_array.low_reg);
783 rl_result = EvalLoc(cu, rl_dest, reg_class, true);
784
785 if (needs_range_check) {
786 // TODO: change kCondCS to a more meaningful name, is the sense of
787 // carry-set/clear flipped?
788 GenRegRegCheck(cu, kCondCs, rl_index.low_reg, reg_len, kThrowArrayBounds);
789 FreeTemp(cu, reg_len);
790 }
791 LoadBaseIndexed(cu, reg_ptr, rl_index.low_reg, rl_result.low_reg, scale, size);
792 FreeTemp(cu, reg_ptr);
793 StoreValue(cu, rl_dest, rl_result);
794 }
795}
796
797/*
798 * Generate array store
799 *
800 */
801void ArmCodegen::GenArrayPut(CompilationUnit* cu, int opt_flags, OpSize size, RegLocation rl_array,
802 RegLocation rl_index, RegLocation rl_src, int scale)
803{
804 RegisterClass reg_class = oat_reg_class_by_size(size);
Ian Rogers2dd0e2c2013-01-24 12:42:14 -0800805 int len_offset = mirror::Array::LengthOffset().Int32Value();
buzbeee6285f92012-12-06 15:57:46 -0800806 int data_offset;
buzbee4ef3e452012-12-14 13:35:28 -0800807 bool constant_index = rl_index.is_const;
buzbeee6285f92012-12-06 15:57:46 -0800808
buzbee4ef3e452012-12-14 13:35:28 -0800809 if (rl_src.wide) {
Ian Rogers2dd0e2c2013-01-24 12:42:14 -0800810 data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Int32Value();
buzbeee6285f92012-12-06 15:57:46 -0800811 } else {
Ian Rogers2dd0e2c2013-01-24 12:42:14 -0800812 data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Int32Value();
buzbeee6285f92012-12-06 15:57:46 -0800813 }
814
buzbee4ef3e452012-12-14 13:35:28 -0800815 // If index is constant, just fold it into the data offset.
816 if (constant_index) {
817 data_offset += ConstantValue(cu, rl_index) << scale;
818 }
819
buzbeee6285f92012-12-06 15:57:46 -0800820 rl_array = LoadValue(cu, rl_array, kCoreReg);
buzbee4ef3e452012-12-14 13:35:28 -0800821 if (!constant_index) {
822 rl_index = LoadValue(cu, rl_index, kCoreReg);
823 }
824
825 int reg_ptr;
826 if (constant_index) {
827 reg_ptr = rl_array.low_reg;
828 } else if (IsTemp(cu, rl_array.low_reg)) {
buzbeee6285f92012-12-06 15:57:46 -0800829 Clobber(cu, rl_array.low_reg);
830 reg_ptr = rl_array.low_reg;
831 } else {
832 reg_ptr = AllocTemp(cu);
833 }
834
835 /* null object? */
836 GenNullCheck(cu, rl_array.s_reg_low, rl_array.low_reg, opt_flags);
837
838 bool needs_range_check = (!(opt_flags & MIR_IGNORE_RANGE_CHECK));
839 int reg_len = INVALID_REG;
840 if (needs_range_check) {
841 reg_len = AllocTemp(cu);
842 //NOTE: max live temps(4) here.
843 /* Get len */
844 LoadWordDisp(cu, rl_array.low_reg, len_offset, reg_len);
845 }
846 /* at this point, reg_ptr points to array, 2 live temps */
buzbee4ef3e452012-12-14 13:35:28 -0800847 if (rl_src.wide || rl_src.fp || constant_index) {
buzbeee6285f92012-12-06 15:57:46 -0800848 if (rl_src.wide) {
849 rl_src = LoadValueWide(cu, rl_src, reg_class);
850 } else {
851 rl_src = LoadValue(cu, rl_src, reg_class);
852 }
buzbee4ef3e452012-12-14 13:35:28 -0800853 if (!constant_index) {
854 OpRegRegRegShift(cu, kOpAdd, reg_ptr, rl_array.low_reg, rl_index.low_reg,
855 EncodeShift(kArmLsl, scale));
856 }
buzbeee6285f92012-12-06 15:57:46 -0800857 if (needs_range_check) {
buzbee4ef3e452012-12-14 13:35:28 -0800858 if (constant_index) {
859 GenImmedCheck(cu, kCondLs, reg_len, ConstantValue(cu, rl_index), kThrowConstantArrayBounds);
860 } else {
861 GenRegRegCheck(cu, kCondLs, reg_len, rl_index.low_reg, kThrowArrayBounds);
862 }
buzbeee6285f92012-12-06 15:57:46 -0800863 FreeTemp(cu, reg_len);
864 }
buzbee4ef3e452012-12-14 13:35:28 -0800865
buzbeee6285f92012-12-06 15:57:46 -0800866 if (rl_src.wide) {
867 StoreBaseDispWide(cu, reg_ptr, data_offset, rl_src.low_reg, rl_src.high_reg);
868 } else {
869 StoreBaseDisp(cu, reg_ptr, data_offset, rl_src.low_reg, size);
870 }
871 } else {
872 /* reg_ptr -> array data */
873 OpRegRegImm(cu, kOpAdd, reg_ptr, rl_array.low_reg, data_offset);
874 rl_src = LoadValue(cu, rl_src, reg_class);
875 if (needs_range_check) {
876 GenRegRegCheck(cu, kCondCs, rl_index.low_reg, reg_len, kThrowArrayBounds);
877 FreeTemp(cu, reg_len);
878 }
879 StoreBaseIndexed(cu, reg_ptr, rl_index.low_reg, rl_src.low_reg,
880 scale, size);
881 }
buzbee4ef3e452012-12-14 13:35:28 -0800882 if (!constant_index) {
883 FreeTemp(cu, reg_ptr);
884 }
buzbeee6285f92012-12-06 15:57:46 -0800885}
886
887/*
888 * Generate array store
889 *
890 */
891void ArmCodegen::GenArrayObjPut(CompilationUnit* cu, int opt_flags, RegLocation rl_array,
892 RegLocation rl_index, RegLocation rl_src, int scale)
893{
Ian Rogers2dd0e2c2013-01-24 12:42:14 -0800894 int len_offset = mirror::Array::LengthOffset().Int32Value();
895 int data_offset = mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value();
buzbeee6285f92012-12-06 15:57:46 -0800896
897 FlushAllRegs(cu); // Use explicit registers
898 LockCallTemps(cu);
899
900 int r_value = TargetReg(kArg0); // Register holding value
901 int r_array_class = TargetReg(kArg1); // Register holding array's Class
902 int r_array = TargetReg(kArg2); // Register holding array
903 int r_index = TargetReg(kArg3); // Register holding index into array
904
905 LoadValueDirectFixed(cu, rl_array, r_array); // Grab array
906 LoadValueDirectFixed(cu, rl_src, r_value); // Grab value
907 LoadValueDirectFixed(cu, rl_index, r_index); // Grab index
908
909 GenNullCheck(cu, rl_array.s_reg_low, r_array, opt_flags); // NPE?
910
911 // Store of null?
912 LIR* null_value_check = OpCmpImmBranch(cu, kCondEq, r_value, 0, NULL);
913
914 // Get the array's class.
Ian Rogers2dd0e2c2013-01-24 12:42:14 -0800915 LoadWordDisp(cu, r_array, mirror::Object::ClassOffset().Int32Value(), r_array_class);
buzbeee6285f92012-12-06 15:57:46 -0800916 CallRuntimeHelperRegReg(cu, ENTRYPOINT_OFFSET(pCanPutArrayElementFromCode), r_value,
917 r_array_class, true);
918 // Redo LoadValues in case they didn't survive the call.
919 LoadValueDirectFixed(cu, rl_array, r_array); // Reload array
920 LoadValueDirectFixed(cu, rl_index, r_index); // Reload index
921 LoadValueDirectFixed(cu, rl_src, r_value); // Reload value
922 r_array_class = INVALID_REG;
923
924 // Branch here if value to be stored == null
925 LIR* target = NewLIR0(cu, kPseudoTargetLabel);
926 null_value_check->target = target;
927
928 bool needs_range_check = (!(opt_flags & MIR_IGNORE_RANGE_CHECK));
929 int reg_len = INVALID_REG;
930 if (needs_range_check) {
931 reg_len = TargetReg(kArg1);
932 LoadWordDisp(cu, r_array, len_offset, reg_len); // Get len
933 }
934 /* r_ptr -> array data */
935 int r_ptr = AllocTemp(cu);
936 OpRegRegImm(cu, kOpAdd, r_ptr, r_array, data_offset);
937 if (needs_range_check) {
938 GenRegRegCheck(cu, kCondCs, r_index, reg_len, kThrowArrayBounds);
939 }
940 StoreBaseIndexed(cu, r_ptr, r_index, r_value, scale, kWord);
941 FreeTemp(cu, r_ptr);
942 FreeTemp(cu, r_index);
943 MarkGCCard(cu, r_value, r_array);
944}
945
buzbeea5954be2013-02-07 10:41:40 -0800946void ArmCodegen::GenShiftImmOpLong(CompilationUnit* cu, Instruction::Code opcode,
buzbee4ef3e452012-12-14 13:35:28 -0800947 RegLocation rl_dest, RegLocation rl_src, RegLocation rl_shift)
948{
949 rl_src = LoadValueWide(cu, rl_src, kCoreReg);
950 // Per spec, we only care about low 6 bits of shift amount.
951 int shift_amount = ConstantValue(cu, rl_shift) & 0x3f;
952 if (shift_amount == 0) {
953 StoreValueWide(cu, rl_dest, rl_src);
buzbeea5954be2013-02-07 10:41:40 -0800954 return;
buzbee4ef3e452012-12-14 13:35:28 -0800955 }
956 if (BadOverlap(cu, rl_src, rl_dest)) {
buzbeea5954be2013-02-07 10:41:40 -0800957 GenShiftOpLong(cu, opcode, rl_dest, rl_src, rl_shift);
958 return;
buzbee4ef3e452012-12-14 13:35:28 -0800959 }
960 RegLocation rl_result = EvalLoc(cu, rl_dest, kCoreReg, true);
961 switch(opcode) {
962 case Instruction::SHL_LONG:
963 case Instruction::SHL_LONG_2ADDR:
964 if (shift_amount == 1) {
965 OpRegRegReg(cu, kOpAdd, rl_result.low_reg, rl_src.low_reg, rl_src.low_reg);
966 OpRegRegReg(cu, kOpAdc, rl_result.high_reg, rl_src.high_reg, rl_src.high_reg);
967 } else if (shift_amount == 32) {
968 OpRegCopy(cu, rl_result.high_reg, rl_src.low_reg);
969 LoadConstant(cu, rl_result.low_reg, 0);
970 } else if (shift_amount > 31) {
971 OpRegRegImm(cu, kOpLsl, rl_result.high_reg, rl_src.low_reg, shift_amount - 32);
972 LoadConstant(cu, rl_result.low_reg, 0);
973 } else {
974 OpRegRegImm(cu, kOpLsl, rl_result.high_reg, rl_src.high_reg, shift_amount);
975 OpRegRegRegShift(cu, kOpOr, rl_result.high_reg, rl_result.high_reg, rl_src.low_reg,
976 EncodeShift(kArmLsr, 32 - shift_amount));
977 OpRegRegImm(cu, kOpLsl, rl_result.low_reg, rl_src.low_reg, shift_amount);
978 }
979 break;
980 case Instruction::SHR_LONG:
981 case Instruction::SHR_LONG_2ADDR:
982 if (shift_amount == 32) {
983 OpRegCopy(cu, rl_result.low_reg, rl_src.high_reg);
984 OpRegRegImm(cu, kOpAsr, rl_result.high_reg, rl_src.high_reg, 31);
985 } else if (shift_amount > 31) {
986 OpRegRegImm(cu, kOpAsr, rl_result.low_reg, rl_src.high_reg, shift_amount - 32);
987 OpRegRegImm(cu, kOpAsr, rl_result.high_reg, rl_src.high_reg, 31);
988 } else {
989 int t_reg = AllocTemp(cu);
990 OpRegRegImm(cu, kOpLsr, t_reg, rl_src.low_reg, shift_amount);
991 OpRegRegRegShift(cu, kOpOr, rl_result.low_reg, t_reg, rl_src.high_reg,
992 EncodeShift(kArmLsl, 32 - shift_amount));
993 FreeTemp(cu, t_reg);
994 OpRegRegImm(cu, kOpAsr, rl_result.high_reg, rl_src.high_reg, shift_amount);
995 }
996 break;
997 case Instruction::USHR_LONG:
998 case Instruction::USHR_LONG_2ADDR:
999 if (shift_amount == 32) {
1000 OpRegCopy(cu, rl_result.low_reg, rl_src.high_reg);
1001 LoadConstant(cu, rl_result.high_reg, 0);
1002 } else if (shift_amount > 31) {
1003 OpRegRegImm(cu, kOpLsr, rl_result.low_reg, rl_src.high_reg, shift_amount - 32);
1004 LoadConstant(cu, rl_result.high_reg, 0);
1005 } else {
1006 int t_reg = AllocTemp(cu);
1007 OpRegRegImm(cu, kOpLsr, t_reg, rl_src.low_reg, shift_amount);
1008 OpRegRegRegShift(cu, kOpOr, rl_result.low_reg, t_reg, rl_src.high_reg,
1009 EncodeShift(kArmLsl, 32 - shift_amount));
1010 FreeTemp(cu, t_reg);
1011 OpRegRegImm(cu, kOpLsr, rl_result.high_reg, rl_src.high_reg, shift_amount);
1012 }
1013 break;
1014 default:
1015 LOG(FATAL) << "Unexpected case";
buzbee4ef3e452012-12-14 13:35:28 -08001016 }
1017 StoreValueWide(cu, rl_dest, rl_result);
buzbee4ef3e452012-12-14 13:35:28 -08001018}
1019
buzbeea5954be2013-02-07 10:41:40 -08001020void ArmCodegen::GenArithImmOpLong(CompilationUnit* cu, Instruction::Code opcode,
buzbee4ef3e452012-12-14 13:35:28 -08001021 RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2)
1022{
1023 if ((opcode == Instruction::SUB_LONG_2ADDR) || (opcode == Instruction::SUB_LONG)) {
1024 if (!rl_src2.is_const) {
1025 // Don't bother with special handling for subtract from immediate.
buzbeea5954be2013-02-07 10:41:40 -08001026 GenArithOpLong(cu, opcode, rl_dest, rl_src1, rl_src2);
1027 return;
buzbee4ef3e452012-12-14 13:35:28 -08001028 }
1029 } else {
1030 // Normalize
1031 if (!rl_src2.is_const) {
1032 DCHECK(rl_src1.is_const);
1033 RegLocation rl_temp = rl_src1;
1034 rl_src1 = rl_src2;
1035 rl_src2 = rl_temp;
1036 }
1037 }
1038 if (BadOverlap(cu, rl_src1, rl_dest)) {
buzbeea5954be2013-02-07 10:41:40 -08001039 GenArithOpLong(cu, opcode, rl_dest, rl_src1, rl_src2);
1040 return;
buzbee4ef3e452012-12-14 13:35:28 -08001041 }
1042 DCHECK(rl_src2.is_const);
1043 int64_t val = ConstantValueWide(cu, rl_src2);
1044 uint32_t val_lo = Low32Bits(val);
1045 uint32_t val_hi = High32Bits(val);
1046 int32_t mod_imm_lo = ModifiedImmediate(val_lo);
1047 int32_t mod_imm_hi = ModifiedImmediate(val_hi);
1048
1049 // Only a subset of add/sub immediate instructions set carry - so bail if we don't fit
1050 switch(opcode) {
1051 case Instruction::ADD_LONG:
1052 case Instruction::ADD_LONG_2ADDR:
1053 case Instruction::SUB_LONG:
1054 case Instruction::SUB_LONG_2ADDR:
1055 if ((mod_imm_lo < 0) || (mod_imm_hi < 0)) {
buzbeea5954be2013-02-07 10:41:40 -08001056 GenArithOpLong(cu, opcode, rl_dest, rl_src1, rl_src2);
1057 return;
buzbee4ef3e452012-12-14 13:35:28 -08001058 }
1059 break;
1060 default:
1061 break;
1062 }
1063 rl_src1 = LoadValueWide(cu, rl_src1, kCoreReg);
1064 RegLocation rl_result = EvalLoc(cu, rl_dest, kCoreReg, true);
1065 // NOTE: once we've done the EvalLoc on dest, we can no longer bail.
1066 switch (opcode) {
1067 case Instruction::ADD_LONG:
1068 case Instruction::ADD_LONG_2ADDR:
1069 NewLIR3(cu, kThumb2AddRRI8, rl_result.low_reg, rl_src1.low_reg, mod_imm_lo);
1070 NewLIR3(cu, kThumb2AdcRRI8, rl_result.high_reg, rl_src1.high_reg, mod_imm_hi);
1071 break;
1072 case Instruction::OR_LONG:
1073 case Instruction::OR_LONG_2ADDR:
1074 if ((val_lo != 0) || (rl_result.low_reg != rl_src1.low_reg)) {
1075 OpRegRegImm(cu, kOpOr, rl_result.low_reg, rl_src1.low_reg, val_lo);
1076 }
1077 if ((val_hi != 0) || (rl_result.high_reg != rl_src1.high_reg)) {
1078 OpRegRegImm(cu, kOpOr, rl_result.high_reg, rl_src1.high_reg, val_hi);
1079 }
1080 break;
1081 case Instruction::XOR_LONG:
1082 case Instruction::XOR_LONG_2ADDR:
1083 OpRegRegImm(cu, kOpXor, rl_result.low_reg, rl_src1.low_reg, val_lo);
1084 OpRegRegImm(cu, kOpXor, rl_result.high_reg, rl_src1.high_reg, val_hi);
1085 break;
1086 case Instruction::AND_LONG:
1087 case Instruction::AND_LONG_2ADDR:
1088 if ((val_lo != 0xffffffff) || (rl_result.low_reg != rl_src1.low_reg)) {
1089 OpRegRegImm(cu, kOpAnd, rl_result.low_reg, rl_src1.low_reg, val_lo);
1090 }
1091 if ((val_hi != 0xffffffff) || (rl_result.high_reg != rl_src1.high_reg)) {
1092 OpRegRegImm(cu, kOpAnd, rl_result.high_reg, rl_src1.high_reg, val_hi);
1093 }
1094 break;
1095 case Instruction::SUB_LONG_2ADDR:
1096 case Instruction::SUB_LONG:
1097 NewLIR3(cu, kThumb2SubRRI8, rl_result.low_reg, rl_src1.low_reg, mod_imm_lo);
1098 NewLIR3(cu, kThumb2SbcRRI8, rl_result.high_reg, rl_src1.high_reg, mod_imm_hi);
1099 break;
1100 default:
1101 LOG(FATAL) << "Unexpected opcode " << opcode;
1102 }
1103 StoreValueWide(cu, rl_dest, rl_result);
buzbee4ef3e452012-12-14 13:35:28 -08001104}
1105
buzbeeefc63692012-11-14 16:31:52 -08001106} // namespace art