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