1 /*
2 * Copyright (C) 2012 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 Mips ISA */
18
19 #include "codegen_mips.h"
20
21 #include "base/logging.h"
22 #include "dex/mir_graph.h"
23 #include "dex/quick/mir_to_lir-inl.h"
24 #include "dex/reg_storage_eq.h"
25 #include "entrypoints/quick/quick_entrypoints.h"
26 #include "mips_lir.h"
27 #include "mirror/array-inl.h"
28
29 namespace art {
30
31 /*
32 * Compare two 64-bit values
33 * x = y return 0
34 * x < y return -1
35 * x > y return 1
36 *
37 * Mips32 implementation
38 * slt t0, x.hi, y.hi; # (x.hi < y.hi) ? 1:0
39 * sgt t1, x.hi, y.hi; # (y.hi > x.hi) ? 1:0
40 * subu res, t0, t1 # res = -1:1:0 for [ < > = ]
41 * bnez res, finish
42 * sltu t0, x.lo, y.lo
43 * sgtu r1, x.lo, y.lo
44 * subu res, t0, t1
45 * finish:
46 *
47 * Mips64 implementation
48 * slt temp, x, y; # (x < y) ? 1:0
49 * slt res, y, x; # (x > y) ? 1:0
50 * subu res, res, temp; # res = -1:1:0 for [ < > = ]
51 *
52 */
GenCmpLong(RegLocation rl_dest,RegLocation rl_src1,RegLocation rl_src2)53 void MipsMir2Lir::GenCmpLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) {
54 rl_src1 = LoadValueWide(rl_src1, kCoreReg);
55 rl_src2 = LoadValueWide(rl_src2, kCoreReg);
56 if (cu_->target64) {
57 RegStorage temp = AllocTempWide();
58 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
59 NewLIR3(kMipsSlt, temp.GetReg(), rl_src1.reg.GetReg(), rl_src2.reg.GetReg());
60 NewLIR3(kMipsSlt, rl_result.reg.GetReg(), rl_src2.reg.GetReg(), rl_src1.reg.GetReg());
61 NewLIR3(kMipsSubu, rl_result.reg.GetReg(), rl_result.reg.GetReg(), temp.GetReg());
62 FreeTemp(temp);
63 StoreValue(rl_dest, rl_result);
64 } else {
65 RegStorage t0 = AllocTemp();
66 RegStorage t1 = AllocTemp();
67 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
68 NewLIR3(kMipsSlt, t0.GetReg(), rl_src1.reg.GetHighReg(), rl_src2.reg.GetHighReg());
69 NewLIR3(kMipsSlt, t1.GetReg(), rl_src2.reg.GetHighReg(), rl_src1.reg.GetHighReg());
70 NewLIR3(kMipsSubu, rl_result.reg.GetReg(), t1.GetReg(), t0.GetReg());
71 LIR* branch = OpCmpImmBranch(kCondNe, rl_result.reg, 0, nullptr);
72 NewLIR3(kMipsSltu, t0.GetReg(), rl_src1.reg.GetLowReg(), rl_src2.reg.GetLowReg());
73 NewLIR3(kMipsSltu, t1.GetReg(), rl_src2.reg.GetLowReg(), rl_src1.reg.GetLowReg());
74 NewLIR3(kMipsSubu, rl_result.reg.GetReg(), t1.GetReg(), t0.GetReg());
75 FreeTemp(t0);
76 FreeTemp(t1);
77 LIR* target = NewLIR0(kPseudoTargetLabel);
78 branch->target = target;
79 StoreValue(rl_dest, rl_result);
80 }
81 }
82
OpCmpBranch(ConditionCode cond,RegStorage src1,RegStorage src2,LIR * target)83 LIR* MipsMir2Lir::OpCmpBranch(ConditionCode cond, RegStorage src1, RegStorage src2, LIR* target) {
84 LIR* branch;
85 MipsOpCode slt_op;
86 MipsOpCode br_op;
87 bool cmp_zero = false;
88 bool swapped = false;
89 switch (cond) {
90 case kCondEq:
91 br_op = kMipsBeq;
92 cmp_zero = true;
93 break;
94 case kCondNe:
95 br_op = kMipsBne;
96 cmp_zero = true;
97 break;
98 case kCondUlt:
99 slt_op = kMipsSltu;
100 br_op = kMipsBnez;
101 break;
102 case kCondUge:
103 slt_op = kMipsSltu;
104 br_op = kMipsBeqz;
105 break;
106 case kCondGe:
107 slt_op = kMipsSlt;
108 br_op = kMipsBeqz;
109 break;
110 case kCondGt:
111 slt_op = kMipsSlt;
112 br_op = kMipsBnez;
113 swapped = true;
114 break;
115 case kCondLe:
116 slt_op = kMipsSlt;
117 br_op = kMipsBeqz;
118 swapped = true;
119 break;
120 case kCondLt:
121 slt_op = kMipsSlt;
122 br_op = kMipsBnez;
123 break;
124 case kCondHi: // Gtu
125 slt_op = kMipsSltu;
126 br_op = kMipsBnez;
127 swapped = true;
128 break;
129 default:
130 LOG(FATAL) << "No support for ConditionCode: " << cond;
131 return nullptr;
132 }
133 if (cmp_zero) {
134 branch = NewLIR2(br_op, src1.GetReg(), src2.GetReg());
135 } else {
136 RegStorage t_reg = AllocTemp();
137 if (swapped) {
138 NewLIR3(slt_op, t_reg.GetReg(), src2.GetReg(), src1.GetReg());
139 } else {
140 NewLIR3(slt_op, t_reg.GetReg(), src1.GetReg(), src2.GetReg());
141 }
142 branch = NewLIR1(br_op, t_reg.GetReg());
143 FreeTemp(t_reg);
144 }
145 branch->target = target;
146 return branch;
147 }
148
OpCmpImmBranch(ConditionCode cond,RegStorage reg,int check_value,LIR * target)149 LIR* MipsMir2Lir::OpCmpImmBranch(ConditionCode cond, RegStorage reg, int check_value, LIR* target) {
150 LIR* branch;
151 if (check_value != 0) {
152 // TUNING: handle s16 & kCondLt/Mi case using slti.
153 RegStorage t_reg = AllocTemp();
154 LoadConstant(t_reg, check_value);
155 branch = OpCmpBranch(cond, reg, t_reg, target);
156 FreeTemp(t_reg);
157 return branch;
158 }
159 MipsOpCode opc;
160 switch (cond) {
161 case kCondEq: opc = kMipsBeqz; break;
162 case kCondGe: opc = kMipsBgez; break;
163 case kCondGt: opc = kMipsBgtz; break;
164 case kCondLe: opc = kMipsBlez; break;
165 // case KCondMi:
166 case kCondLt: opc = kMipsBltz; break;
167 case kCondNe: opc = kMipsBnez; break;
168 default:
169 // Tuning: use slti when applicable
170 RegStorage t_reg = AllocTemp();
171 LoadConstant(t_reg, check_value);
172 branch = OpCmpBranch(cond, reg, t_reg, target);
173 FreeTemp(t_reg);
174 return branch;
175 }
176 branch = NewLIR1(opc, reg.GetReg());
177 branch->target = target;
178 return branch;
179 }
180
OpRegCopyNoInsert(RegStorage r_dest,RegStorage r_src)181 LIR* MipsMir2Lir::OpRegCopyNoInsert(RegStorage r_dest, RegStorage r_src) {
182 LIR* res;
183 MipsOpCode opcode;
184
185 if (!cu_->target64) {
186 // If src or dest is a pair, we'll be using low reg.
187 if (r_dest.IsPair()) {
188 r_dest = r_dest.GetLow();
189 }
190 if (r_src.IsPair()) {
191 r_src = r_src.GetLow();
192 }
193 } else {
194 DCHECK(!r_dest.IsPair() && !r_src.IsPair());
195 }
196
197 if (r_dest.IsFloat() || r_src.IsFloat())
198 return OpFpRegCopy(r_dest, r_src);
199 if (cu_->target64) {
200 // TODO: Check that r_src and r_dest are both 32 or both 64 bits length on Mips64.
201 if (r_dest.Is64Bit() || r_src.Is64Bit()) {
202 opcode = kMipsMove;
203 } else {
204 opcode = kMipsSll;
205 }
206 } else {
207 opcode = kMipsMove;
208 }
209 res = RawLIR(current_dalvik_offset_, opcode, r_dest.GetReg(), r_src.GetReg());
210 if (!(cu_->disable_opt & (1 << kSafeOptimizations)) && r_dest == r_src) {
211 res->flags.is_nop = true;
212 }
213 return res;
214 }
215
OpRegCopy(RegStorage r_dest,RegStorage r_src)216 void MipsMir2Lir::OpRegCopy(RegStorage r_dest, RegStorage r_src) {
217 if (r_dest != r_src) {
218 LIR *res = OpRegCopyNoInsert(r_dest, r_src);
219 AppendLIR(res);
220 }
221 }
222
OpRegCopyWide(RegStorage r_dest,RegStorage r_src)223 void MipsMir2Lir::OpRegCopyWide(RegStorage r_dest, RegStorage r_src) {
224 if (cu_->target64) {
225 OpRegCopy(r_dest, r_src);
226 return;
227 }
228 if (r_dest != r_src) {
229 bool dest_fp = r_dest.IsFloat();
230 bool src_fp = r_src.IsFloat();
231 if (dest_fp) {
232 if (src_fp) {
233 // Here if both src and dest are fp registers. OpRegCopy will choose the right copy
234 // (solo or pair).
235 OpRegCopy(r_dest, r_src);
236 } else {
237 // note the operands are swapped for the mtc1 and mthc1 instr.
238 // Here if dest is fp reg and src is core reg.
239 if (fpuIs32Bit_) {
240 NewLIR2(kMipsMtc1, r_src.GetLowReg(), r_dest.GetLowReg());
241 NewLIR2(kMipsMtc1, r_src.GetHighReg(), r_dest.GetHighReg());
242 } else {
243 r_dest = Fp64ToSolo32(r_dest);
244 NewLIR2(kMipsMtc1, r_src.GetLowReg(), r_dest.GetReg());
245 NewLIR2(kMipsMthc1, r_src.GetHighReg(), r_dest.GetReg());
246 }
247 }
248 } else {
249 if (src_fp) {
250 // Here if dest is core reg and src is fp reg.
251 if (fpuIs32Bit_) {
252 NewLIR2(kMipsMfc1, r_dest.GetLowReg(), r_src.GetLowReg());
253 NewLIR2(kMipsMfc1, r_dest.GetHighReg(), r_src.GetHighReg());
254 } else {
255 r_src = Fp64ToSolo32(r_src);
256 NewLIR2(kMipsMfc1, r_dest.GetLowReg(), r_src.GetReg());
257 NewLIR2(kMipsMfhc1, r_dest.GetHighReg(), r_src.GetReg());
258 }
259 } else {
260 // Here if both src and dest are core registers.
261 // Handle overlap
262 if (r_src.GetHighReg() != r_dest.GetLowReg()) {
263 OpRegCopy(r_dest.GetLow(), r_src.GetLow());
264 OpRegCopy(r_dest.GetHigh(), r_src.GetHigh());
265 } else if (r_src.GetLowReg() != r_dest.GetHighReg()) {
266 OpRegCopy(r_dest.GetHigh(), r_src.GetHigh());
267 OpRegCopy(r_dest.GetLow(), r_src.GetLow());
268 } else {
269 RegStorage r_tmp = AllocTemp();
270 OpRegCopy(r_tmp, r_src.GetHigh());
271 OpRegCopy(r_dest.GetLow(), r_src.GetLow());
272 OpRegCopy(r_dest.GetHigh(), r_tmp);
273 FreeTemp(r_tmp);
274 }
275 }
276 }
277 }
278 }
279
GenSelectConst32(RegStorage left_op,RegStorage right_op,ConditionCode code,int32_t true_val,int32_t false_val,RegStorage rs_dest,RegisterClass dest_reg_class)280 void MipsMir2Lir::GenSelectConst32(RegStorage left_op, RegStorage right_op, ConditionCode code,
281 int32_t true_val, int32_t false_val, RegStorage rs_dest,
282 RegisterClass dest_reg_class) {
283 UNUSED(dest_reg_class);
284 // Implement as a branch-over.
285 // TODO: Conditional move?
286 LoadConstant(rs_dest, true_val);
287 LIR* ne_branchover = OpCmpBranch(code, left_op, right_op, nullptr);
288 LoadConstant(rs_dest, false_val);
289 LIR* target_label = NewLIR0(kPseudoTargetLabel);
290 ne_branchover->target = target_label;
291 }
292
GenSelect(BasicBlock * bb,MIR * mir)293 void MipsMir2Lir::GenSelect(BasicBlock* bb, MIR* mir) {
294 UNUSED(bb, mir);
295 UNIMPLEMENTED(FATAL) << "Need codegen for select";
296 }
297
GenFusedLongCmpBranch(BasicBlock * bb,MIR * mir)298 void MipsMir2Lir::GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir) {
299 UNUSED(bb, mir);
300 UNIMPLEMENTED(FATAL) << "Need codegen for fused long cmp branch";
301 }
302
GenDivRem(RegLocation rl_dest,RegStorage reg1,RegStorage reg2,bool is_div)303 RegLocation MipsMir2Lir::GenDivRem(RegLocation rl_dest, RegStorage reg1, RegStorage reg2,
304 bool is_div) {
305 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
306
307 if (isaIsR6_) {
308 NewLIR3(is_div ? kMipsR6Div : kMipsR6Mod, rl_result.reg.GetReg(), reg1.GetReg(), reg2.GetReg());
309 } else {
310 NewLIR2(kMipsR2Div, reg1.GetReg(), reg2.GetReg());
311 NewLIR1(is_div ? kMipsR2Mflo : kMipsR2Mfhi, rl_result.reg.GetReg());
312 }
313 return rl_result;
314 }
315
GenDivRemLit(RegLocation rl_dest,RegStorage reg1,int lit,bool is_div)316 RegLocation MipsMir2Lir::GenDivRemLit(RegLocation rl_dest, RegStorage reg1, int lit, bool is_div) {
317 RegStorage t_reg = AllocTemp();
318 // lit is guarantee to be a 16-bit constant
319 if (IsUint<16>(lit)) {
320 NewLIR3(kMipsOri, t_reg.GetReg(), rZERO, lit);
321 } else {
322 // Addiu will sign extend the entire width (32 or 64) of the register.
323 NewLIR3(kMipsAddiu, t_reg.GetReg(), rZERO, lit);
324 }
325 RegLocation rl_result = GenDivRem(rl_dest, reg1, t_reg, is_div);
326 FreeTemp(t_reg);
327 return rl_result;
328 }
329
GenDivRem(RegLocation rl_dest,RegLocation rl_src1,RegLocation rl_src2,bool is_div,int flags)330 RegLocation MipsMir2Lir::GenDivRem(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2,
331 bool is_div, int flags) {
332 UNUSED(rl_dest, rl_src1, rl_src2, is_div, flags);
333 LOG(FATAL) << "Unexpected use of GenDivRem for Mips";
334 UNREACHABLE();
335 }
336
GenDivRemLit(RegLocation rl_dest,RegLocation rl_src1,int lit,bool is_div)337 RegLocation MipsMir2Lir::GenDivRemLit(RegLocation rl_dest, RegLocation rl_src1, int lit,
338 bool is_div) {
339 UNUSED(rl_dest, rl_src1, lit, is_div);
340 LOG(FATAL) << "Unexpected use of GenDivRemLit for Mips";
341 UNREACHABLE();
342 }
343
GenInlinedCas(CallInfo * info,bool is_long,bool is_object)344 bool MipsMir2Lir::GenInlinedCas(CallInfo* info, bool is_long, bool is_object) {
345 UNUSED(info, is_long, is_object);
346 return false;
347 }
348
GenInlinedAbsFloat(CallInfo * info)349 bool MipsMir2Lir::GenInlinedAbsFloat(CallInfo* info) {
350 UNUSED(info);
351 // TODO: add Mips implementation.
352 return false;
353 }
354
GenInlinedAbsDouble(CallInfo * info)355 bool MipsMir2Lir::GenInlinedAbsDouble(CallInfo* info) {
356 UNUSED(info);
357 // TODO: add Mips implementation.
358 return false;
359 }
360
GenInlinedSqrt(CallInfo * info)361 bool MipsMir2Lir::GenInlinedSqrt(CallInfo* info) {
362 UNUSED(info);
363 return false;
364 }
365
GenInlinedPeek(CallInfo * info,OpSize size)366 bool MipsMir2Lir::GenInlinedPeek(CallInfo* info, OpSize size) {
367 if (size != kSignedByte) {
368 // MIPS supports only aligned access. Defer unaligned access to JNI implementation.
369 return false;
370 }
371 RegLocation rl_src_address = info->args[0]; // Long address.
372 if (!cu_->target64) {
373 rl_src_address = NarrowRegLoc(rl_src_address); // Ignore high half in info->args[1].
374 }
375 RegLocation rl_dest = InlineTarget(info);
376 RegLocation rl_address;
377 if (cu_->target64) {
378 rl_address = LoadValueWide(rl_src_address, kCoreReg);
379 } else {
380 rl_address = LoadValue(rl_src_address, kCoreReg);
381 }
382 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
383 DCHECK(size == kSignedByte);
384 LoadBaseDisp(rl_address.reg, 0, rl_result.reg, size, kNotVolatile);
385 StoreValue(rl_dest, rl_result);
386 return true;
387 }
388
GenInlinedPoke(CallInfo * info,OpSize size)389 bool MipsMir2Lir::GenInlinedPoke(CallInfo* info, OpSize size) {
390 if (size != kSignedByte) {
391 // MIPS supports only aligned access. Defer unaligned access to JNI implementation.
392 return false;
393 }
394 RegLocation rl_src_address = info->args[0]; // Long address.
395 if (!cu_->target64) {
396 rl_src_address = NarrowRegLoc(rl_src_address); // Ignore high half in info->args[1].
397 }
398 RegLocation rl_src_value = info->args[2]; // [size] value.
399 RegLocation rl_address;
400 if (cu_->target64) {
401 rl_address = LoadValueWide(rl_src_address, kCoreReg);
402 } else {
403 rl_address = LoadValue(rl_src_address, kCoreReg);
404 }
405 DCHECK(size == kSignedByte);
406 RegLocation rl_value = LoadValue(rl_src_value, kCoreReg);
407 StoreBaseDisp(rl_address.reg, 0, rl_value.reg, size, kNotVolatile);
408 return true;
409 }
410
OpPcRelLoad(RegStorage reg,LIR * target)411 void MipsMir2Lir::OpPcRelLoad(RegStorage reg, LIR* target) {
412 UNUSED(reg, target);
413 LOG(FATAL) << "Unexpected use of OpPcRelLoad for Mips";
414 UNREACHABLE();
415 }
416
OpVldm(RegStorage r_base,int count)417 LIR* MipsMir2Lir::OpVldm(RegStorage r_base, int count) {
418 UNUSED(r_base, count);
419 LOG(FATAL) << "Unexpected use of OpVldm for Mips";
420 UNREACHABLE();
421 }
422
OpVstm(RegStorage r_base,int count)423 LIR* MipsMir2Lir::OpVstm(RegStorage r_base, int count) {
424 UNUSED(r_base, count);
425 LOG(FATAL) << "Unexpected use of OpVstm for Mips";
426 UNREACHABLE();
427 }
428
GenMultiplyByTwoBitMultiplier(RegLocation rl_src,RegLocation rl_result,int lit,int first_bit,int second_bit)429 void MipsMir2Lir::GenMultiplyByTwoBitMultiplier(RegLocation rl_src, RegLocation rl_result, int lit,
430 int first_bit, int second_bit) {
431 UNUSED(lit);
432 RegStorage t_reg = AllocTemp();
433 OpRegRegImm(kOpLsl, t_reg, rl_src.reg, second_bit - first_bit);
434 OpRegRegReg(kOpAdd, rl_result.reg, rl_src.reg, t_reg);
435 FreeTemp(t_reg);
436 if (first_bit != 0) {
437 OpRegRegImm(kOpLsl, rl_result.reg, rl_result.reg, first_bit);
438 }
439 }
440
GenDivZeroCheckWide(RegStorage reg)441 void MipsMir2Lir::GenDivZeroCheckWide(RegStorage reg) {
442 if (cu_->target64) {
443 GenDivZeroCheck(reg);
444 } else {
445 DCHECK(reg.IsPair()); // TODO: support k64BitSolo.
446 RegStorage t_reg = AllocTemp();
447 OpRegRegReg(kOpOr, t_reg, reg.GetLow(), reg.GetHigh());
448 GenDivZeroCheck(t_reg);
449 FreeTemp(t_reg);
450 }
451 }
452
453 // Test suspend flag, return target of taken suspend branch.
OpTestSuspend(LIR * target)454 LIR* MipsMir2Lir::OpTestSuspend(LIR* target) {
455 OpRegImm(kOpSub, TargetPtrReg(kSuspend), 1);
456 return OpCmpImmBranch((target == nullptr) ? kCondEq : kCondNe, TargetPtrReg(kSuspend), 0, target);
457 }
458
459 // Decrement register and branch on condition.
OpDecAndBranch(ConditionCode c_code,RegStorage reg,LIR * target)460 LIR* MipsMir2Lir::OpDecAndBranch(ConditionCode c_code, RegStorage reg, LIR* target) {
461 OpRegImm(kOpSub, reg, 1);
462 return OpCmpImmBranch(c_code, reg, 0, target);
463 }
464
SmallLiteralDivRem(Instruction::Code dalvik_opcode,bool is_div,RegLocation rl_src,RegLocation rl_dest,int lit)465 bool MipsMir2Lir::SmallLiteralDivRem(Instruction::Code dalvik_opcode, bool is_div,
466 RegLocation rl_src, RegLocation rl_dest, int lit) {
467 UNUSED(dalvik_opcode, is_div, rl_src, rl_dest, lit);
468 LOG(FATAL) << "Unexpected use of smallLiteralDive in Mips";
469 UNREACHABLE();
470 }
471
EasyMultiply(RegLocation rl_src,RegLocation rl_dest,int lit)472 bool MipsMir2Lir::EasyMultiply(RegLocation rl_src, RegLocation rl_dest, int lit) {
473 UNUSED(rl_src, rl_dest, lit);
474 LOG(FATAL) << "Unexpected use of easyMultiply in Mips";
475 UNREACHABLE();
476 }
477
OpIT(ConditionCode cond,const char * guide)478 LIR* MipsMir2Lir::OpIT(ConditionCode cond, const char* guide) {
479 UNUSED(cond, guide);
480 LOG(FATAL) << "Unexpected use of OpIT in Mips";
481 UNREACHABLE();
482 }
483
OpEndIT(LIR * it)484 void MipsMir2Lir::OpEndIT(LIR* it) {
485 UNUSED(it);
486 LOG(FATAL) << "Unexpected use of OpEndIT in Mips";
487 }
488
GenAddLong(RegLocation rl_dest,RegLocation rl_src1,RegLocation rl_src2)489 void MipsMir2Lir::GenAddLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) {
490 rl_src1 = LoadValueWide(rl_src1, kCoreReg);
491 rl_src2 = LoadValueWide(rl_src2, kCoreReg);
492 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
493 /*
494 * [v1 v0] = [a1 a0] + [a3 a2];
495 * addu v0,a2,a0
496 * addu t1,a3,a1
497 * sltu v1,v0,a2
498 * addu v1,v1,t1
499 */
500
501 OpRegRegReg(kOpAdd, rl_result.reg.GetLow(), rl_src2.reg.GetLow(), rl_src1.reg.GetLow());
502 RegStorage t_reg = AllocTemp();
503 OpRegRegReg(kOpAdd, t_reg, rl_src2.reg.GetHigh(), rl_src1.reg.GetHigh());
504 NewLIR3(kMipsSltu, rl_result.reg.GetHighReg(), rl_result.reg.GetLowReg(),
505 rl_src2.reg.GetLowReg());
506 OpRegRegReg(kOpAdd, rl_result.reg.GetHigh(), rl_result.reg.GetHigh(), t_reg);
507 FreeTemp(t_reg);
508 StoreValueWide(rl_dest, rl_result);
509 }
510
GenSubLong(RegLocation rl_dest,RegLocation rl_src1,RegLocation rl_src2)511 void MipsMir2Lir::GenSubLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) {
512 rl_src1 = LoadValueWide(rl_src1, kCoreReg);
513 rl_src2 = LoadValueWide(rl_src2, kCoreReg);
514 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
515 /*
516 * [v1 v0] = [a1 a0] - [a3 a2];
517 * sltu t1,a0,a2
518 * subu v0,a0,a2
519 * subu v1,a1,a3
520 * subu v1,v1,t1
521 */
522
523 RegStorage t_reg = AllocTemp();
524 NewLIR3(kMipsSltu, t_reg.GetReg(), rl_src1.reg.GetLowReg(), rl_src2.reg.GetLowReg());
525 OpRegRegReg(kOpSub, rl_result.reg.GetLow(), rl_src1.reg.GetLow(), rl_src2.reg.GetLow());
526 OpRegRegReg(kOpSub, rl_result.reg.GetHigh(), rl_src1.reg.GetHigh(), rl_src2.reg.GetHigh());
527 OpRegRegReg(kOpSub, rl_result.reg.GetHigh(), rl_result.reg.GetHigh(), t_reg);
528 FreeTemp(t_reg);
529 StoreValueWide(rl_dest, rl_result);
530 }
531
GenArithOpLong(Instruction::Code opcode,RegLocation rl_dest,RegLocation rl_src1,RegLocation rl_src2,int flags)532 void MipsMir2Lir::GenArithOpLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1,
533 RegLocation rl_src2, int flags) {
534 if (cu_->target64) {
535 switch (opcode) {
536 case Instruction::NOT_LONG:
537 GenNotLong(rl_dest, rl_src2);
538 return;
539 case Instruction::ADD_LONG:
540 case Instruction::ADD_LONG_2ADDR:
541 GenLongOp(kOpAdd, rl_dest, rl_src1, rl_src2);
542 return;
543 case Instruction::SUB_LONG:
544 case Instruction::SUB_LONG_2ADDR:
545 GenLongOp(kOpSub, rl_dest, rl_src1, rl_src2);
546 return;
547 case Instruction::MUL_LONG:
548 case Instruction::MUL_LONG_2ADDR:
549 GenMulLong(rl_dest, rl_src1, rl_src2);
550 return;
551 case Instruction::DIV_LONG:
552 case Instruction::DIV_LONG_2ADDR:
553 GenDivRemLong(opcode, rl_dest, rl_src1, rl_src2, /*is_div*/ true, flags);
554 return;
555 case Instruction::REM_LONG:
556 case Instruction::REM_LONG_2ADDR:
557 GenDivRemLong(opcode, rl_dest, rl_src1, rl_src2, /*is_div*/ false, flags);
558 return;
559 case Instruction::AND_LONG:
560 case Instruction::AND_LONG_2ADDR:
561 GenLongOp(kOpAnd, rl_dest, rl_src1, rl_src2);
562 return;
563 case Instruction::OR_LONG:
564 case Instruction::OR_LONG_2ADDR:
565 GenLongOp(kOpOr, rl_dest, rl_src1, rl_src2);
566 return;
567 case Instruction::XOR_LONG:
568 case Instruction::XOR_LONG_2ADDR:
569 GenLongOp(kOpXor, rl_dest, rl_src1, rl_src2);
570 return;
571 case Instruction::NEG_LONG:
572 GenNegLong(rl_dest, rl_src2);
573 return;
574
575 default:
576 LOG(FATAL) << "Invalid long arith op";
577 return;
578 }
579 } else {
580 switch (opcode) {
581 case Instruction::ADD_LONG:
582 case Instruction::ADD_LONG_2ADDR:
583 GenAddLong(rl_dest, rl_src1, rl_src2);
584 return;
585 case Instruction::SUB_LONG:
586 case Instruction::SUB_LONG_2ADDR:
587 GenSubLong(rl_dest, rl_src1, rl_src2);
588 return;
589 case Instruction::NEG_LONG:
590 GenNegLong(rl_dest, rl_src2);
591 return;
592 default:
593 break;
594 }
595 // Fallback for all other ops.
596 Mir2Lir::GenArithOpLong(opcode, rl_dest, rl_src1, rl_src2, flags);
597 }
598 }
599
GenLongOp(OpKind op,RegLocation rl_dest,RegLocation rl_src1,RegLocation rl_src2)600 void MipsMir2Lir::GenLongOp(OpKind op, RegLocation rl_dest, RegLocation rl_src1,
601 RegLocation rl_src2) {
602 rl_src1 = LoadValueWide(rl_src1, kCoreReg);
603 rl_src2 = LoadValueWide(rl_src2, kCoreReg);
604 RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true);
605 OpRegRegReg(op, rl_result.reg, rl_src1.reg, rl_src2.reg);
606 StoreValueWide(rl_dest, rl_result);
607 }
608
GenNotLong(RegLocation rl_dest,RegLocation rl_src)609 void MipsMir2Lir::GenNotLong(RegLocation rl_dest, RegLocation rl_src) {
610 rl_src = LoadValueWide(rl_src, kCoreReg);
611 RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true);
612 OpRegReg(kOpMvn, rl_result.reg, rl_src.reg);
613 StoreValueWide(rl_dest, rl_result);
614 }
615
GenMulLong(RegLocation rl_dest,RegLocation rl_src1,RegLocation rl_src2)616 void MipsMir2Lir::GenMulLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) {
617 rl_src1 = LoadValueWide(rl_src1, kCoreReg);
618 rl_src2 = LoadValueWide(rl_src2, kCoreReg);
619 RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true);
620 NewLIR3(kMips64Dmul, rl_result.reg.GetReg(), rl_src1.reg.GetReg(), rl_src2.reg.GetReg());
621 StoreValueWide(rl_dest, rl_result);
622 }
623
GenDivRemLong(Instruction::Code opcode,RegLocation rl_dest,RegLocation rl_src1,RegLocation rl_src2,bool is_div,int flags)624 void MipsMir2Lir::GenDivRemLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1,
625 RegLocation rl_src2, bool is_div, int flags) {
626 UNUSED(opcode);
627 // TODO: Implement easy div/rem?
628 rl_src1 = LoadValueWide(rl_src1, kCoreReg);
629 rl_src2 = LoadValueWide(rl_src2, kCoreReg);
630 if ((flags & MIR_IGNORE_DIV_ZERO_CHECK) == 0) {
631 GenDivZeroCheckWide(rl_src2.reg);
632 }
633 RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true);
634 NewLIR3(is_div ? kMips64Ddiv : kMips64Dmod, rl_result.reg.GetReg(), rl_src1.reg.GetReg(),
635 rl_src2.reg.GetReg());
636 StoreValueWide(rl_dest, rl_result);
637 }
638
GenNegLong(RegLocation rl_dest,RegLocation rl_src)639 void MipsMir2Lir::GenNegLong(RegLocation rl_dest, RegLocation rl_src) {
640 rl_src = LoadValueWide(rl_src, kCoreReg);
641 RegLocation rl_result;
642
643 if (cu_->target64) {
644 rl_result = EvalLocWide(rl_dest, kCoreReg, true);
645 OpRegReg(kOpNeg, rl_result.reg, rl_src.reg);
646 StoreValueWide(rl_dest, rl_result);
647 } else {
648 rl_result = EvalLoc(rl_dest, kCoreReg, true);
649 // [v1 v0] = -[a1 a0]
650 // negu v0,a0
651 // negu v1,a1
652 // sltu t1,r_zero
653 // subu v1,v1,t1
654 OpRegReg(kOpNeg, rl_result.reg.GetLow(), rl_src.reg.GetLow());
655 OpRegReg(kOpNeg, rl_result.reg.GetHigh(), rl_src.reg.GetHigh());
656 RegStorage t_reg = AllocTemp();
657 NewLIR3(kMipsSltu, t_reg.GetReg(), rZERO, rl_result.reg.GetLowReg());
658 OpRegRegReg(kOpSub, rl_result.reg.GetHigh(), rl_result.reg.GetHigh(), t_reg);
659 FreeTemp(t_reg);
660 StoreValueWide(rl_dest, rl_result);
661 }
662 }
663
664 /*
665 * Generate array load
666 */
GenArrayGet(int opt_flags,OpSize size,RegLocation rl_array,RegLocation rl_index,RegLocation rl_dest,int scale)667 void MipsMir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array,
668 RegLocation rl_index, RegLocation rl_dest, int scale) {
669 RegisterClass reg_class = RegClassBySize(size);
670 int len_offset = mirror::Array::LengthOffset().Int32Value();
671 int data_offset;
672 RegLocation rl_result;
673 rl_array = LoadValue(rl_array, kRefReg);
674 rl_index = LoadValue(rl_index, kCoreReg);
675
676 // FIXME: need to add support for rl_index.is_const.
677
678 if (size == k64 || size == kDouble) {
679 data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Int32Value();
680 } else {
681 data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Int32Value();
682 }
683
684 // Null object?
685 GenNullCheck(rl_array.reg, opt_flags);
686
687 RegStorage reg_ptr = (cu_->target64) ? AllocTempRef() : AllocTemp();
688 bool needs_range_check = (!(opt_flags & MIR_IGNORE_RANGE_CHECK));
689 RegStorage reg_len;
690 if (needs_range_check) {
691 reg_len = AllocTemp();
692 // Get len.
693 Load32Disp(rl_array.reg, len_offset, reg_len);
694 MarkPossibleNullPointerException(opt_flags);
695 } else {
696 ForceImplicitNullCheck(rl_array.reg, opt_flags, false);
697 }
698 // reg_ptr -> array data.
699 OpRegRegImm(kOpAdd, reg_ptr, rl_array.reg, data_offset);
700 FreeTemp(rl_array.reg);
701 if ((size == k64) || (size == kDouble)) {
702 if (scale) {
703 RegStorage r_new_index = AllocTemp();
704 OpRegRegImm(kOpLsl, r_new_index, rl_index.reg, scale);
705 OpRegReg(kOpAdd, reg_ptr, r_new_index);
706 FreeTemp(r_new_index);
707 } else {
708 OpRegReg(kOpAdd, reg_ptr, rl_index.reg);
709 }
710 FreeTemp(rl_index.reg);
711 rl_result = EvalLoc(rl_dest, reg_class, true);
712
713 if (needs_range_check) {
714 GenArrayBoundsCheck(rl_index.reg, reg_len);
715 FreeTemp(reg_len);
716 }
717 LoadBaseDisp(reg_ptr, 0, rl_result.reg, size, kNotVolatile);
718
719 FreeTemp(reg_ptr);
720 StoreValueWide(rl_dest, rl_result);
721 } else {
722 rl_result = EvalLoc(rl_dest, reg_class, true);
723
724 if (needs_range_check) {
725 GenArrayBoundsCheck(rl_index.reg, reg_len);
726 FreeTemp(reg_len);
727 }
728
729 if (cu_->target64) {
730 if (rl_result.ref) {
731 LoadBaseIndexed(reg_ptr, As64BitReg(rl_index.reg), As32BitReg(rl_result.reg), scale,
732 kReference);
733 } else {
734 LoadBaseIndexed(reg_ptr, As64BitReg(rl_index.reg), rl_result.reg, scale, size);
735 }
736 } else {
737 LoadBaseIndexed(reg_ptr, rl_index.reg, rl_result.reg, scale, size);
738 }
739
740 FreeTemp(reg_ptr);
741 StoreValue(rl_dest, rl_result);
742 }
743 }
744
745 /*
746 * Generate array store
747 *
748 */
GenArrayPut(int opt_flags,OpSize size,RegLocation rl_array,RegLocation rl_index,RegLocation rl_src,int scale,bool card_mark)749 void MipsMir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array,
750 RegLocation rl_index, RegLocation rl_src, int scale, bool card_mark) {
751 RegisterClass reg_class = RegClassBySize(size);
752 int len_offset = mirror::Array::LengthOffset().Int32Value();
753 int data_offset;
754
755 if (size == k64 || size == kDouble) {
756 data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Int32Value();
757 } else {
758 data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Int32Value();
759 }
760
761 rl_array = LoadValue(rl_array, kRefReg);
762 rl_index = LoadValue(rl_index, kCoreReg);
763
764 // FIXME: need to add support for rl_index.is_const.
765
766 RegStorage reg_ptr;
767 bool allocated_reg_ptr_temp = false;
768 if (IsTemp(rl_array.reg) && !card_mark) {
769 Clobber(rl_array.reg);
770 reg_ptr = rl_array.reg;
771 } else {
772 reg_ptr = AllocTemp();
773 OpRegCopy(reg_ptr, rl_array.reg);
774 allocated_reg_ptr_temp = true;
775 }
776
777 // Null object?
778 GenNullCheck(rl_array.reg, opt_flags);
779
780 bool needs_range_check = (!(opt_flags & MIR_IGNORE_RANGE_CHECK));
781 RegStorage reg_len;
782 if (needs_range_check) {
783 reg_len = AllocTemp();
784 // NOTE: max live temps(4) here.
785 // Get len.
786 Load32Disp(rl_array.reg, len_offset, reg_len);
787 MarkPossibleNullPointerException(opt_flags);
788 } else {
789 ForceImplicitNullCheck(rl_array.reg, opt_flags, false);
790 }
791 // reg_ptr -> array data.
792 OpRegImm(kOpAdd, reg_ptr, data_offset);
793 // At this point, reg_ptr points to array, 2 live temps.
794 if ((size == k64) || (size == kDouble)) {
795 // TUNING: specific wide routine that can handle fp regs.
796 if (scale) {
797 RegStorage r_new_index = AllocTemp();
798 OpRegRegImm(kOpLsl, r_new_index, rl_index.reg, scale);
799 OpRegReg(kOpAdd, reg_ptr, r_new_index);
800 FreeTemp(r_new_index);
801 } else {
802 OpRegReg(kOpAdd, reg_ptr, rl_index.reg);
803 }
804 rl_src = LoadValueWide(rl_src, reg_class);
805
806 if (needs_range_check) {
807 GenArrayBoundsCheck(rl_index.reg, reg_len);
808 FreeTemp(reg_len);
809 }
810
811 StoreBaseDisp(reg_ptr, 0, rl_src.reg, size, kNotVolatile);
812 } else {
813 rl_src = LoadValue(rl_src, reg_class);
814 if (needs_range_check) {
815 GenArrayBoundsCheck(rl_index.reg, reg_len);
816 FreeTemp(reg_len);
817 }
818 StoreBaseIndexed(reg_ptr, rl_index.reg, rl_src.reg, scale, size);
819 }
820 if (allocated_reg_ptr_temp) {
821 FreeTemp(reg_ptr);
822 }
823 if (card_mark) {
824 MarkGCCard(opt_flags, rl_src.reg, rl_array.reg);
825 }
826 }
827
GenShiftOpLong(Instruction::Code opcode,RegLocation rl_dest,RegLocation rl_src1,RegLocation rl_shift)828 void MipsMir2Lir::GenShiftOpLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1,
829 RegLocation rl_shift) {
830 if (!cu_->target64) {
831 Mir2Lir::GenShiftOpLong(opcode, rl_dest, rl_src1, rl_shift);
832 return;
833 }
834 OpKind op = kOpBkpt;
835 switch (opcode) {
836 case Instruction::SHL_LONG:
837 case Instruction::SHL_LONG_2ADDR:
838 op = kOpLsl;
839 break;
840 case Instruction::SHR_LONG:
841 case Instruction::SHR_LONG_2ADDR:
842 op = kOpAsr;
843 break;
844 case Instruction::USHR_LONG:
845 case Instruction::USHR_LONG_2ADDR:
846 op = kOpLsr;
847 break;
848 default:
849 LOG(FATAL) << "Unexpected case: " << opcode;
850 }
851 rl_shift = LoadValue(rl_shift, kCoreReg);
852 rl_src1 = LoadValueWide(rl_src1, kCoreReg);
853 RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true);
854 OpRegRegReg(op, rl_result.reg, rl_src1.reg, As64BitReg(rl_shift.reg));
855 StoreValueWide(rl_dest, rl_result);
856 }
857
GenShiftImmOpLong(Instruction::Code opcode,RegLocation rl_dest,RegLocation rl_src1,RegLocation rl_shift,int flags)858 void MipsMir2Lir::GenShiftImmOpLong(Instruction::Code opcode, RegLocation rl_dest,
859 RegLocation rl_src1, RegLocation rl_shift, int flags) {
860 UNUSED(flags);
861 if (!cu_->target64) {
862 // Default implementation is just to ignore the constant case.
863 GenShiftOpLong(opcode, rl_dest, rl_src1, rl_shift);
864 return;
865 }
866 OpKind op = kOpBkpt;
867 // Per spec, we only care about low 6 bits of shift amount.
868 int shift_amount = mir_graph_->ConstantValue(rl_shift) & 0x3f;
869 rl_src1 = LoadValueWide(rl_src1, kCoreReg);
870 if (shift_amount == 0) {
871 StoreValueWide(rl_dest, rl_src1);
872 return;
873 }
874
875 RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true);
876 switch (opcode) {
877 case Instruction::SHL_LONG:
878 case Instruction::SHL_LONG_2ADDR:
879 op = kOpLsl;
880 break;
881 case Instruction::SHR_LONG:
882 case Instruction::SHR_LONG_2ADDR:
883 op = kOpAsr;
884 break;
885 case Instruction::USHR_LONG:
886 case Instruction::USHR_LONG_2ADDR:
887 op = kOpLsr;
888 break;
889 default:
890 LOG(FATAL) << "Unexpected case";
891 }
892 OpRegRegImm(op, rl_result.reg, rl_src1.reg, shift_amount);
893 StoreValueWide(rl_dest, rl_result);
894 }
895
GenArithImmOpLong(Instruction::Code opcode,RegLocation rl_dest,RegLocation rl_src1,RegLocation rl_src2,int flags)896 void MipsMir2Lir::GenArithImmOpLong(Instruction::Code opcode, RegLocation rl_dest,
897 RegLocation rl_src1, RegLocation rl_src2, int flags) {
898 // Default - bail to non-const handler.
899 GenArithOpLong(opcode, rl_dest, rl_src1, rl_src2, flags);
900 }
901
GenIntToLong(RegLocation rl_dest,RegLocation rl_src)902 void MipsMir2Lir::GenIntToLong(RegLocation rl_dest, RegLocation rl_src) {
903 if (!cu_->target64) {
904 Mir2Lir::GenIntToLong(rl_dest, rl_src);
905 return;
906 }
907 rl_src = LoadValue(rl_src, kCoreReg);
908 RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true);
909 NewLIR3(kMipsSll, rl_result.reg.GetReg(), As64BitReg(rl_src.reg).GetReg(), 0);
910 StoreValueWide(rl_dest, rl_result);
911 }
912
GenConversionCall(QuickEntrypointEnum trampoline,RegLocation rl_dest,RegLocation rl_src,RegisterClass reg_class)913 void MipsMir2Lir::GenConversionCall(QuickEntrypointEnum trampoline, RegLocation rl_dest,
914 RegLocation rl_src, RegisterClass reg_class) {
915 FlushAllRegs(); // Send everything to home location.
916 CallRuntimeHelperRegLocation(trampoline, rl_src, false);
917 if (rl_dest.wide) {
918 RegLocation rl_result;
919 rl_result = GetReturnWide(reg_class);
920 StoreValueWide(rl_dest, rl_result);
921 } else {
922 RegLocation rl_result;
923 rl_result = GetReturn(reg_class);
924 StoreValue(rl_dest, rl_result);
925 }
926 }
927
928 } // namespace art
929