blob: cab2c1b53d401fb3dfc1e617a0c3707abd87fd46 [file] [log] [blame]
Brian Carlstrom7940e442013-07-12 13:46:57 -07001/*
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#include "codegen_mips.h"
18#include "dex/compiler_internals.h"
19#include "dex/quick/mir_to_lir-inl.h"
20#include "mips_lir.h"
21
22#include <string>
23
24namespace art {
25
26static int core_regs[] = {r_ZERO, r_AT, r_V0, r_V1, r_A0, r_A1, r_A2, r_A3,
27 r_T0, r_T1, r_T2, r_T3, r_T4, r_T5, r_T6, r_T7,
28 r_S0, r_S1, r_S2, r_S3, r_S4, r_S5, r_S6, r_S7, r_T8,
29 r_T9, r_K0, r_K1, r_GP, r_SP, r_FP, r_RA};
30static int ReservedRegs[] = {r_ZERO, r_AT, r_S0, r_S1, r_K0, r_K1, r_GP, r_SP,
31 r_RA};
32static int core_temps[] = {r_V0, r_V1, r_A0, r_A1, r_A2, r_A3, r_T0, r_T1, r_T2,
33 r_T3, r_T4, r_T5, r_T6, r_T7, r_T8};
34static int FpRegs[] = {r_F0, r_F1, r_F2, r_F3, r_F4, r_F5, r_F6, r_F7,
35 r_F8, r_F9, r_F10, r_F11, r_F12, r_F13, r_F14, r_F15};
36static int fp_temps[] = {r_F0, r_F1, r_F2, r_F3, r_F4, r_F5, r_F6, r_F7,
37 r_F8, r_F9, r_F10, r_F11, r_F12, r_F13, r_F14, r_F15};
38
39RegLocation MipsMir2Lir::LocCReturn()
40{
41 RegLocation res = MIPS_LOC_C_RETURN;
42 return res;
43}
44
45RegLocation MipsMir2Lir::LocCReturnWide()
46{
47 RegLocation res = MIPS_LOC_C_RETURN_WIDE;
48 return res;
49}
50
51RegLocation MipsMir2Lir::LocCReturnFloat()
52{
53 RegLocation res = MIPS_LOC_C_RETURN_FLOAT;
54 return res;
55}
56
57RegLocation MipsMir2Lir::LocCReturnDouble()
58{
59 RegLocation res = MIPS_LOC_C_RETURN_DOUBLE;
60 return res;
61}
62
63// Return a target-dependent special register.
64int MipsMir2Lir::TargetReg(SpecialTargetRegister reg) {
65 int res = INVALID_REG;
66 switch (reg) {
67 case kSelf: res = rMIPS_SELF; break;
68 case kSuspend: res = rMIPS_SUSPEND; break;
69 case kLr: res = rMIPS_LR; break;
70 case kPc: res = rMIPS_PC; break;
71 case kSp: res = rMIPS_SP; break;
72 case kArg0: res = rMIPS_ARG0; break;
73 case kArg1: res = rMIPS_ARG1; break;
74 case kArg2: res = rMIPS_ARG2; break;
75 case kArg3: res = rMIPS_ARG3; break;
76 case kFArg0: res = rMIPS_FARG0; break;
77 case kFArg1: res = rMIPS_FARG1; break;
78 case kFArg2: res = rMIPS_FARG2; break;
79 case kFArg3: res = rMIPS_FARG3; break;
80 case kRet0: res = rMIPS_RET0; break;
81 case kRet1: res = rMIPS_RET1; break;
82 case kInvokeTgt: res = rMIPS_INVOKE_TGT; break;
83 case kCount: res = rMIPS_COUNT; break;
84 }
85 return res;
86}
87
88// Create a double from a pair of singles.
89int MipsMir2Lir::S2d(int low_reg, int high_reg)
90{
91 return MIPS_S2D(low_reg, high_reg);
92}
93
94// Return mask to strip off fp reg flags and bias.
95uint32_t MipsMir2Lir::FpRegMask()
96{
97 return MIPS_FP_REG_MASK;
98}
99
100// True if both regs single, both core or both double.
101bool MipsMir2Lir::SameRegType(int reg1, int reg2)
102{
103 return (MIPS_REGTYPE(reg1) == MIPS_REGTYPE(reg2));
104}
105
106/*
107 * Decode the register id.
108 */
109uint64_t MipsMir2Lir::GetRegMaskCommon(int reg)
110{
111 uint64_t seed;
112 int shift;
113 int reg_id;
114
115
116 reg_id = reg & 0x1f;
117 /* Each double register is equal to a pair of single-precision FP registers */
118 seed = MIPS_DOUBLEREG(reg) ? 3 : 1;
119 /* FP register starts at bit position 16 */
120 shift = MIPS_FPREG(reg) ? kMipsFPReg0 : 0;
121 /* Expand the double register id into single offset */
122 shift += reg_id;
123 return (seed << shift);
124}
125
126uint64_t MipsMir2Lir::GetPCUseDefEncoding()
127{
128 return ENCODE_MIPS_REG_PC;
129}
130
131
132void MipsMir2Lir::SetupTargetResourceMasks(LIR* lir)
133{
134 DCHECK_EQ(cu_->instruction_set, kMips);
135
136 // Mips-specific resource map setup here.
137 uint64_t flags = MipsMir2Lir::EncodingMap[lir->opcode].flags;
138
139 if (flags & REG_DEF_SP) {
140 lir->def_mask |= ENCODE_MIPS_REG_SP;
141 }
142
143 if (flags & REG_USE_SP) {
144 lir->use_mask |= ENCODE_MIPS_REG_SP;
145 }
146
147 if (flags & REG_DEF_LR) {
148 lir->def_mask |= ENCODE_MIPS_REG_LR;
149 }
150}
151
152/* For dumping instructions */
153#define MIPS_REG_COUNT 32
154static const char *mips_reg_name[MIPS_REG_COUNT] = {
155 "zero", "at", "v0", "v1", "a0", "a1", "a2", "a3",
156 "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7",
157 "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
158 "t8", "t9", "k0", "k1", "gp", "sp", "fp", "ra"
159};
160
161/*
162 * Interpret a format string and build a string no longer than size
163 * See format key in Assemble.c.
164 */
165std::string MipsMir2Lir::BuildInsnString(const char *fmt, LIR *lir, unsigned char* base_addr)
166{
167 std::string buf;
168 int i;
169 const char *fmt_end = &fmt[strlen(fmt)];
170 char tbuf[256];
171 char nc;
172 while (fmt < fmt_end) {
173 int operand;
174 if (*fmt == '!') {
175 fmt++;
176 DCHECK_LT(fmt, fmt_end);
177 nc = *fmt++;
178 if (nc=='!') {
179 strcpy(tbuf, "!");
180 } else {
181 DCHECK_LT(fmt, fmt_end);
182 DCHECK_LT(static_cast<unsigned>(nc-'0'), 4u);
183 operand = lir->operands[nc-'0'];
184 switch (*fmt++) {
185 case 'b':
186 strcpy(tbuf,"0000");
187 for (i=3; i>= 0; i--) {
188 tbuf[i] += operand & 1;
189 operand >>= 1;
190 }
191 break;
192 case 's':
193 sprintf(tbuf,"$f%d",operand & MIPS_FP_REG_MASK);
194 break;
195 case 'S':
196 DCHECK_EQ(((operand & MIPS_FP_REG_MASK) & 1), 0);
197 sprintf(tbuf,"$f%d",operand & MIPS_FP_REG_MASK);
198 break;
199 case 'h':
200 sprintf(tbuf,"%04x", operand);
201 break;
202 case 'M':
203 case 'd':
204 sprintf(tbuf,"%d", operand);
205 break;
206 case 'D':
207 sprintf(tbuf,"%d", operand+1);
208 break;
209 case 'E':
210 sprintf(tbuf,"%d", operand*4);
211 break;
212 case 'F':
213 sprintf(tbuf,"%d", operand*2);
214 break;
215 case 't':
216 sprintf(tbuf,"0x%08x (L%p)", reinterpret_cast<uintptr_t>(base_addr) + lir->offset + 4 +
217 (operand << 2), lir->target);
218 break;
219 case 'T':
220 sprintf(tbuf,"0x%08x", operand << 2);
221 break;
222 case 'u': {
223 int offset_1 = lir->operands[0];
224 int offset_2 = NEXT_LIR(lir)->operands[0];
225 uintptr_t target =
226 (((reinterpret_cast<uintptr_t>(base_addr) + lir->offset + 4) & ~3) +
227 (offset_1 << 21 >> 9) + (offset_2 << 1)) & 0xfffffffc;
228 sprintf(tbuf, "%p", reinterpret_cast<void*>(target));
229 break;
230 }
231
232 /* Nothing to print for BLX_2 */
233 case 'v':
234 strcpy(tbuf, "see above");
235 break;
236 case 'r':
237 DCHECK(operand >= 0 && operand < MIPS_REG_COUNT);
238 strcpy(tbuf, mips_reg_name[operand]);
239 break;
240 case 'N':
241 // Placeholder for delay slot handling
242 strcpy(tbuf, "; nop");
243 break;
244 default:
245 strcpy(tbuf,"DecodeError");
246 break;
247 }
248 buf += tbuf;
249 }
250 } else {
251 buf += *fmt++;
252 }
253 }
254 return buf;
255}
256
257// FIXME: need to redo resource maps for MIPS - fix this at that time
258void MipsMir2Lir::DumpResourceMask(LIR *mips_lir, uint64_t mask, const char *prefix)
259{
260 char buf[256];
261 buf[0] = 0;
262
263 if (mask == ENCODE_ALL) {
264 strcpy(buf, "all");
265 } else {
266 char num[8];
267 int i;
268
269 for (i = 0; i < kMipsRegEnd; i++) {
270 if (mask & (1ULL << i)) {
271 sprintf(num, "%d ", i);
272 strcat(buf, num);
273 }
274 }
275
276 if (mask & ENCODE_CCODE) {
277 strcat(buf, "cc ");
278 }
279 if (mask & ENCODE_FP_STATUS) {
280 strcat(buf, "fpcc ");
281 }
282 /* Memory bits */
283 if (mips_lir && (mask & ENCODE_DALVIK_REG)) {
284 sprintf(buf + strlen(buf), "dr%d%s", mips_lir->alias_info & 0xffff,
285 (mips_lir->alias_info & 0x80000000) ? "(+1)" : "");
286 }
287 if (mask & ENCODE_LITERAL) {
288 strcat(buf, "lit ");
289 }
290
291 if (mask & ENCODE_HEAP_REF) {
292 strcat(buf, "heap ");
293 }
294 if (mask & ENCODE_MUST_NOT_ALIAS) {
295 strcat(buf, "noalias ");
296 }
297 }
298 if (buf[0]) {
299 LOG(INFO) << prefix << ": " << buf;
300 }
301}
302
303/*
304 * TUNING: is true leaf? Can't just use METHOD_IS_LEAF to determine as some
305 * instructions might call out to C/assembly helper functions. Until
306 * machinery is in place, always spill lr.
307 */
308
309void MipsMir2Lir::AdjustSpillMask()
310{
311 core_spill_mask_ |= (1 << r_RA);
312 num_core_spills_++;
313}
314
315/*
316 * Mark a callee-save fp register as promoted. Note that
317 * vpush/vpop uses contiguous register lists so we must
318 * include any holes in the mask. Associate holes with
319 * Dalvik register INVALID_VREG (0xFFFFU).
320 */
321void MipsMir2Lir::MarkPreservedSingle(int s_reg, int reg)
322{
323 LOG(FATAL) << "No support yet for promoted FP regs";
324}
325
326void MipsMir2Lir::FlushRegWide(int reg1, int reg2)
327{
328 RegisterInfo* info1 = GetRegInfo(reg1);
329 RegisterInfo* info2 = GetRegInfo(reg2);
330 DCHECK(info1 && info2 && info1->pair && info2->pair &&
331 (info1->partner == info2->reg) &&
332 (info2->partner == info1->reg));
333 if ((info1->live && info1->dirty) || (info2->live && info2->dirty)) {
334 if (!(info1->is_temp && info2->is_temp)) {
335 /* Should not happen. If it does, there's a problem in eval_loc */
336 LOG(FATAL) << "Long half-temp, half-promoted";
337 }
338
339 info1->dirty = false;
340 info2->dirty = false;
341 if (mir_graph_->SRegToVReg(info2->s_reg) < mir_graph_->SRegToVReg(info1->s_reg))
342 info1 = info2;
343 int v_reg = mir_graph_->SRegToVReg(info1->s_reg);
344 StoreBaseDispWide(rMIPS_SP, VRegOffset(v_reg), info1->reg, info1->partner);
345 }
346}
347
348void MipsMir2Lir::FlushReg(int reg)
349{
350 RegisterInfo* info = GetRegInfo(reg);
351 if (info->live && info->dirty) {
352 info->dirty = false;
353 int v_reg = mir_graph_->SRegToVReg(info->s_reg);
354 StoreBaseDisp(rMIPS_SP, VRegOffset(v_reg), reg, kWord);
355 }
356}
357
358/* Give access to the target-dependent FP register encoding to common code */
359bool MipsMir2Lir::IsFpReg(int reg) {
360 return MIPS_FPREG(reg);
361}
362
363/* Clobber all regs that might be used by an external C call */
364void MipsMir2Lir::ClobberCalleeSave()
365{
366 Clobber(r_ZERO);
367 Clobber(r_AT);
368 Clobber(r_V0);
369 Clobber(r_V1);
370 Clobber(r_A0);
371 Clobber(r_A1);
372 Clobber(r_A2);
373 Clobber(r_A3);
374 Clobber(r_T0);
375 Clobber(r_T1);
376 Clobber(r_T2);
377 Clobber(r_T3);
378 Clobber(r_T4);
379 Clobber(r_T5);
380 Clobber(r_T6);
381 Clobber(r_T7);
382 Clobber(r_T8);
383 Clobber(r_T9);
384 Clobber(r_K0);
385 Clobber(r_K1);
386 Clobber(r_GP);
387 Clobber(r_FP);
388 Clobber(r_RA);
389 Clobber(r_F0);
390 Clobber(r_F1);
391 Clobber(r_F2);
392 Clobber(r_F3);
393 Clobber(r_F4);
394 Clobber(r_F5);
395 Clobber(r_F6);
396 Clobber(r_F7);
397 Clobber(r_F8);
398 Clobber(r_F9);
399 Clobber(r_F10);
400 Clobber(r_F11);
401 Clobber(r_F12);
402 Clobber(r_F13);
403 Clobber(r_F14);
404 Clobber(r_F15);
405}
406
407RegLocation MipsMir2Lir::GetReturnWideAlt()
408{
409 UNIMPLEMENTED(FATAL) << "No GetReturnWideAlt for MIPS";
410 RegLocation res = LocCReturnWide();
411 return res;
412}
413
414RegLocation MipsMir2Lir::GetReturnAlt()
415{
416 UNIMPLEMENTED(FATAL) << "No GetReturnAlt for MIPS";
417 RegLocation res = LocCReturn();
418 return res;
419}
420
421MipsMir2Lir::RegisterInfo* MipsMir2Lir::GetRegInfo(int reg)
422{
423 return MIPS_FPREG(reg) ? &reg_pool_->FPRegs[reg & MIPS_FP_REG_MASK]
424 : &reg_pool_->core_regs[reg];
425}
426
427/* To be used when explicitly managing register use */
428void MipsMir2Lir::LockCallTemps()
429{
430 LockTemp(rMIPS_ARG0);
431 LockTemp(rMIPS_ARG1);
432 LockTemp(rMIPS_ARG2);
433 LockTemp(rMIPS_ARG3);
434}
435
436/* To be used when explicitly managing register use */
437void MipsMir2Lir::FreeCallTemps()
438{
439 FreeTemp(rMIPS_ARG0);
440 FreeTemp(rMIPS_ARG1);
441 FreeTemp(rMIPS_ARG2);
442 FreeTemp(rMIPS_ARG3);
443}
444
445void MipsMir2Lir::GenMemBarrier(MemBarrierKind barrier_kind)
446{
447#if ANDROID_SMP != 0
448 NewLIR1(kMipsSync, 0 /* Only stype currently supported */);
449#endif
450}
451
452/*
453 * Alloc a pair of core registers, or a double. Low reg in low byte,
454 * high reg in next byte.
455 */
456int MipsMir2Lir::AllocTypedTempPair(bool fp_hint,
457 int reg_class)
458{
459 int high_reg;
460 int low_reg;
461 int res = 0;
462
463 if (((reg_class == kAnyReg) && fp_hint) || (reg_class == kFPReg)) {
464 low_reg = AllocTempDouble();
465 high_reg = low_reg + 1;
466 res = (low_reg & 0xff) | ((high_reg & 0xff) << 8);
467 return res;
468 }
469
470 low_reg = AllocTemp();
471 high_reg = AllocTemp();
472 res = (low_reg & 0xff) | ((high_reg & 0xff) << 8);
473 return res;
474}
475
476int MipsMir2Lir::AllocTypedTemp(bool fp_hint, int reg_class)
477{
478 if (((reg_class == kAnyReg) && fp_hint) || (reg_class == kFPReg))
479{
480 return AllocTempFloat();
481}
482 return AllocTemp();
483}
484
485void MipsMir2Lir::CompilerInitializeRegAlloc()
486{
487 int num_regs = sizeof(core_regs)/sizeof(*core_regs);
488 int num_reserved = sizeof(ReservedRegs)/sizeof(*ReservedRegs);
489 int num_temps = sizeof(core_temps)/sizeof(*core_temps);
490 int num_fp_regs = sizeof(FpRegs)/sizeof(*FpRegs);
491 int num_fp_temps = sizeof(fp_temps)/sizeof(*fp_temps);
492 reg_pool_ = static_cast<RegisterPool*>(arena_->NewMem(sizeof(*reg_pool_), true,
493 ArenaAllocator::kAllocRegAlloc));
494 reg_pool_->num_core_regs = num_regs;
495 reg_pool_->core_regs = static_cast<RegisterInfo*>
496 (arena_->NewMem(num_regs * sizeof(*reg_pool_->core_regs), true,
497 ArenaAllocator::kAllocRegAlloc));
498 reg_pool_->num_fp_regs = num_fp_regs;
499 reg_pool_->FPRegs = static_cast<RegisterInfo*>
500 (arena_->NewMem(num_fp_regs * sizeof(*reg_pool_->FPRegs), true,
501 ArenaAllocator::kAllocRegAlloc));
502 CompilerInitPool(reg_pool_->core_regs, core_regs, reg_pool_->num_core_regs);
503 CompilerInitPool(reg_pool_->FPRegs, FpRegs, reg_pool_->num_fp_regs);
504 // Keep special registers from being allocated
505 for (int i = 0; i < num_reserved; i++) {
506 if (NO_SUSPEND && (ReservedRegs[i] == rMIPS_SUSPEND)) {
507 //To measure cost of suspend check
508 continue;
509 }
510 MarkInUse(ReservedRegs[i]);
511 }
512 // Mark temp regs - all others not in use can be used for promotion
513 for (int i = 0; i < num_temps; i++) {
514 MarkTemp(core_temps[i]);
515 }
516 for (int i = 0; i < num_fp_temps; i++) {
517 MarkTemp(fp_temps[i]);
518 }
519}
520
521void MipsMir2Lir::FreeRegLocTemps(RegLocation rl_keep, RegLocation rl_free)
522{
523 if ((rl_free.low_reg != rl_keep.low_reg) && (rl_free.low_reg != rl_keep.high_reg) &&
524 (rl_free.high_reg != rl_keep.low_reg) && (rl_free.high_reg != rl_keep.high_reg)) {
525 // No overlap, free both
526 FreeTemp(rl_free.low_reg);
527 FreeTemp(rl_free.high_reg);
528 }
529}
530/*
531 * In the Arm code a it is typical to use the link register
532 * to hold the target address. However, for Mips we must
533 * ensure that all branch instructions can be restarted if
534 * there is a trap in the shadow. Allocate a temp register.
535 */
536int MipsMir2Lir::LoadHelper(int offset)
537{
538 LoadWordDisp(rMIPS_SELF, offset, r_T9);
539 return r_T9;
540}
541
542void MipsMir2Lir::SpillCoreRegs()
543{
544 if (num_core_spills_ == 0) {
545 return;
546 }
547 uint32_t mask = core_spill_mask_;
548 int offset = num_core_spills_ * 4;
549 OpRegImm(kOpSub, rMIPS_SP, offset);
550 for (int reg = 0; mask; mask >>= 1, reg++) {
551 if (mask & 0x1) {
552 offset -= 4;
553 StoreWordDisp(rMIPS_SP, offset, reg);
554 }
555 }
556}
557
558void MipsMir2Lir::UnSpillCoreRegs()
559{
560 if (num_core_spills_ == 0) {
561 return;
562 }
563 uint32_t mask = core_spill_mask_;
564 int offset = frame_size_;
565 for (int reg = 0; mask; mask >>= 1, reg++) {
566 if (mask & 0x1) {
567 offset -= 4;
568 LoadWordDisp(rMIPS_SP, offset, reg);
569 }
570 }
571 OpRegImm(kOpAdd, rMIPS_SP, frame_size_);
572}
573
574bool MipsMir2Lir::IsUnconditionalBranch(LIR* lir)
575{
576 return (lir->opcode == kMipsB);
577}
578
579MipsMir2Lir::MipsMir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena)
580 : Mir2Lir(cu, mir_graph, arena) {
581 for (int i = 0; i < kMipsLast; i++) {
582 if (MipsMir2Lir::EncodingMap[i].opcode != i) {
583 LOG(FATAL) << "Encoding order for " << MipsMir2Lir::EncodingMap[i].name
584 << " is wrong: expecting " << i << ", seeing "
585 << static_cast<int>(MipsMir2Lir::EncodingMap[i].opcode);
586 }
587 }
588}
589
590Mir2Lir* MipsCodeGenerator(CompilationUnit* const cu, MIRGraph* const mir_graph,
591 ArenaAllocator* const arena) {
592 return new MipsMir2Lir(cu, mir_graph, arena);
593}
594
595uint64_t MipsMir2Lir::GetTargetInstFlags(int opcode)
596{
597 return MipsMir2Lir::EncodingMap[opcode].flags;
598}
599
600const char* MipsMir2Lir::GetTargetInstName(int opcode)
601{
602 return MipsMir2Lir::EncodingMap[opcode].name;
603}
604
605const char* MipsMir2Lir::GetTargetInstFmt(int opcode)
606{
607 return MipsMir2Lir::EncodingMap[opcode].fmt;
608}
609
610} // namespace art