diff options
Diffstat (limited to 'compiler/dex/quick/mips/target_mips.cc')
-rw-r--r-- | compiler/dex/quick/mips/target_mips.cc | 610 |
1 files changed, 610 insertions, 0 deletions
diff --git a/compiler/dex/quick/mips/target_mips.cc b/compiler/dex/quick/mips/target_mips.cc new file mode 100644 index 0000000000..cab2c1b53d --- /dev/null +++ b/compiler/dex/quick/mips/target_mips.cc @@ -0,0 +1,610 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "codegen_mips.h" +#include "dex/compiler_internals.h" +#include "dex/quick/mir_to_lir-inl.h" +#include "mips_lir.h" + +#include <string> + +namespace art { + +static int core_regs[] = {r_ZERO, r_AT, r_V0, r_V1, r_A0, r_A1, r_A2, r_A3, + r_T0, r_T1, r_T2, r_T3, r_T4, r_T5, r_T6, r_T7, + r_S0, r_S1, r_S2, r_S3, r_S4, r_S5, r_S6, r_S7, r_T8, + r_T9, r_K0, r_K1, r_GP, r_SP, r_FP, r_RA}; +static int ReservedRegs[] = {r_ZERO, r_AT, r_S0, r_S1, r_K0, r_K1, r_GP, r_SP, + r_RA}; +static int core_temps[] = {r_V0, r_V1, r_A0, r_A1, r_A2, r_A3, r_T0, r_T1, r_T2, + r_T3, r_T4, r_T5, r_T6, r_T7, r_T8}; +static int FpRegs[] = {r_F0, r_F1, r_F2, r_F3, r_F4, r_F5, r_F6, r_F7, + r_F8, r_F9, r_F10, r_F11, r_F12, r_F13, r_F14, r_F15}; +static int fp_temps[] = {r_F0, r_F1, r_F2, r_F3, r_F4, r_F5, r_F6, r_F7, + r_F8, r_F9, r_F10, r_F11, r_F12, r_F13, r_F14, r_F15}; + +RegLocation MipsMir2Lir::LocCReturn() +{ + RegLocation res = MIPS_LOC_C_RETURN; + return res; +} + +RegLocation MipsMir2Lir::LocCReturnWide() +{ + RegLocation res = MIPS_LOC_C_RETURN_WIDE; + return res; +} + +RegLocation MipsMir2Lir::LocCReturnFloat() +{ + RegLocation res = MIPS_LOC_C_RETURN_FLOAT; + return res; +} + +RegLocation MipsMir2Lir::LocCReturnDouble() +{ + RegLocation res = MIPS_LOC_C_RETURN_DOUBLE; + return res; +} + +// Return a target-dependent special register. +int MipsMir2Lir::TargetReg(SpecialTargetRegister reg) { + int res = INVALID_REG; + switch (reg) { + case kSelf: res = rMIPS_SELF; break; + case kSuspend: res = rMIPS_SUSPEND; break; + case kLr: res = rMIPS_LR; break; + case kPc: res = rMIPS_PC; break; + case kSp: res = rMIPS_SP; break; + case kArg0: res = rMIPS_ARG0; break; + case kArg1: res = rMIPS_ARG1; break; + case kArg2: res = rMIPS_ARG2; break; + case kArg3: res = rMIPS_ARG3; break; + case kFArg0: res = rMIPS_FARG0; break; + case kFArg1: res = rMIPS_FARG1; break; + case kFArg2: res = rMIPS_FARG2; break; + case kFArg3: res = rMIPS_FARG3; break; + case kRet0: res = rMIPS_RET0; break; + case kRet1: res = rMIPS_RET1; break; + case kInvokeTgt: res = rMIPS_INVOKE_TGT; break; + case kCount: res = rMIPS_COUNT; break; + } + return res; +} + +// Create a double from a pair of singles. +int MipsMir2Lir::S2d(int low_reg, int high_reg) +{ + return MIPS_S2D(low_reg, high_reg); +} + +// Return mask to strip off fp reg flags and bias. +uint32_t MipsMir2Lir::FpRegMask() +{ + return MIPS_FP_REG_MASK; +} + +// True if both regs single, both core or both double. +bool MipsMir2Lir::SameRegType(int reg1, int reg2) +{ + return (MIPS_REGTYPE(reg1) == MIPS_REGTYPE(reg2)); +} + +/* + * Decode the register id. + */ +uint64_t MipsMir2Lir::GetRegMaskCommon(int reg) +{ + uint64_t seed; + int shift; + int reg_id; + + + reg_id = reg & 0x1f; + /* Each double register is equal to a pair of single-precision FP registers */ + seed = MIPS_DOUBLEREG(reg) ? 3 : 1; + /* FP register starts at bit position 16 */ + shift = MIPS_FPREG(reg) ? kMipsFPReg0 : 0; + /* Expand the double register id into single offset */ + shift += reg_id; + return (seed << shift); +} + +uint64_t MipsMir2Lir::GetPCUseDefEncoding() +{ + return ENCODE_MIPS_REG_PC; +} + + +void MipsMir2Lir::SetupTargetResourceMasks(LIR* lir) +{ + DCHECK_EQ(cu_->instruction_set, kMips); + + // Mips-specific resource map setup here. + uint64_t flags = MipsMir2Lir::EncodingMap[lir->opcode].flags; + + if (flags & REG_DEF_SP) { + lir->def_mask |= ENCODE_MIPS_REG_SP; + } + + if (flags & REG_USE_SP) { + lir->use_mask |= ENCODE_MIPS_REG_SP; + } + + if (flags & REG_DEF_LR) { + lir->def_mask |= ENCODE_MIPS_REG_LR; + } +} + +/* For dumping instructions */ +#define MIPS_REG_COUNT 32 +static const char *mips_reg_name[MIPS_REG_COUNT] = { + "zero", "at", "v0", "v1", "a0", "a1", "a2", "a3", + "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", + "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", + "t8", "t9", "k0", "k1", "gp", "sp", "fp", "ra" +}; + +/* + * Interpret a format string and build a string no longer than size + * See format key in Assemble.c. + */ +std::string MipsMir2Lir::BuildInsnString(const char *fmt, LIR *lir, unsigned char* base_addr) +{ + std::string buf; + int i; + const char *fmt_end = &fmt[strlen(fmt)]; + char tbuf[256]; + char nc; + while (fmt < fmt_end) { + int operand; + if (*fmt == '!') { + fmt++; + DCHECK_LT(fmt, fmt_end); + nc = *fmt++; + if (nc=='!') { + strcpy(tbuf, "!"); + } else { + DCHECK_LT(fmt, fmt_end); + DCHECK_LT(static_cast<unsigned>(nc-'0'), 4u); + operand = lir->operands[nc-'0']; + switch (*fmt++) { + case 'b': + strcpy(tbuf,"0000"); + for (i=3; i>= 0; i--) { + tbuf[i] += operand & 1; + operand >>= 1; + } + break; + case 's': + sprintf(tbuf,"$f%d",operand & MIPS_FP_REG_MASK); + break; + case 'S': + DCHECK_EQ(((operand & MIPS_FP_REG_MASK) & 1), 0); + sprintf(tbuf,"$f%d",operand & MIPS_FP_REG_MASK); + break; + case 'h': + sprintf(tbuf,"%04x", operand); + break; + case 'M': + case 'd': + sprintf(tbuf,"%d", operand); + break; + case 'D': + sprintf(tbuf,"%d", operand+1); + break; + case 'E': + sprintf(tbuf,"%d", operand*4); + break; + case 'F': + sprintf(tbuf,"%d", operand*2); + break; + case 't': + sprintf(tbuf,"0x%08x (L%p)", reinterpret_cast<uintptr_t>(base_addr) + lir->offset + 4 + + (operand << 2), lir->target); + break; + case 'T': + sprintf(tbuf,"0x%08x", operand << 2); + break; + case 'u': { + int offset_1 = lir->operands[0]; + int offset_2 = NEXT_LIR(lir)->operands[0]; + uintptr_t target = + (((reinterpret_cast<uintptr_t>(base_addr) + lir->offset + 4) & ~3) + + (offset_1 << 21 >> 9) + (offset_2 << 1)) & 0xfffffffc; + sprintf(tbuf, "%p", reinterpret_cast<void*>(target)); + break; + } + + /* Nothing to print for BLX_2 */ + case 'v': + strcpy(tbuf, "see above"); + break; + case 'r': + DCHECK(operand >= 0 && operand < MIPS_REG_COUNT); + strcpy(tbuf, mips_reg_name[operand]); + break; + case 'N': + // Placeholder for delay slot handling + strcpy(tbuf, "; nop"); + break; + default: + strcpy(tbuf,"DecodeError"); + break; + } + buf += tbuf; + } + } else { + buf += *fmt++; + } + } + return buf; +} + +// FIXME: need to redo resource maps for MIPS - fix this at that time +void MipsMir2Lir::DumpResourceMask(LIR *mips_lir, uint64_t mask, const char *prefix) +{ + char buf[256]; + buf[0] = 0; + + if (mask == ENCODE_ALL) { + strcpy(buf, "all"); + } else { + char num[8]; + int i; + + for (i = 0; i < kMipsRegEnd; i++) { + if (mask & (1ULL << i)) { + sprintf(num, "%d ", i); + strcat(buf, num); + } + } + + if (mask & ENCODE_CCODE) { + strcat(buf, "cc "); + } + if (mask & ENCODE_FP_STATUS) { + strcat(buf, "fpcc "); + } + /* Memory bits */ + if (mips_lir && (mask & ENCODE_DALVIK_REG)) { + sprintf(buf + strlen(buf), "dr%d%s", mips_lir->alias_info & 0xffff, + (mips_lir->alias_info & 0x80000000) ? "(+1)" : ""); + } + if (mask & ENCODE_LITERAL) { + strcat(buf, "lit "); + } + + if (mask & ENCODE_HEAP_REF) { + strcat(buf, "heap "); + } + if (mask & ENCODE_MUST_NOT_ALIAS) { + strcat(buf, "noalias "); + } + } + if (buf[0]) { + LOG(INFO) << prefix << ": " << buf; + } +} + +/* + * TUNING: is true leaf? Can't just use METHOD_IS_LEAF to determine as some + * instructions might call out to C/assembly helper functions. Until + * machinery is in place, always spill lr. + */ + +void MipsMir2Lir::AdjustSpillMask() +{ + core_spill_mask_ |= (1 << r_RA); + num_core_spills_++; +} + +/* + * Mark a callee-save fp register as promoted. Note that + * vpush/vpop uses contiguous register lists so we must + * include any holes in the mask. Associate holes with + * Dalvik register INVALID_VREG (0xFFFFU). + */ +void MipsMir2Lir::MarkPreservedSingle(int s_reg, int reg) +{ + LOG(FATAL) << "No support yet for promoted FP regs"; +} + +void MipsMir2Lir::FlushRegWide(int reg1, int reg2) +{ + RegisterInfo* info1 = GetRegInfo(reg1); + RegisterInfo* info2 = GetRegInfo(reg2); + DCHECK(info1 && info2 && info1->pair && info2->pair && + (info1->partner == info2->reg) && + (info2->partner == info1->reg)); + if ((info1->live && info1->dirty) || (info2->live && info2->dirty)) { + if (!(info1->is_temp && info2->is_temp)) { + /* Should not happen. If it does, there's a problem in eval_loc */ + LOG(FATAL) << "Long half-temp, half-promoted"; + } + + info1->dirty = false; + info2->dirty = false; + if (mir_graph_->SRegToVReg(info2->s_reg) < mir_graph_->SRegToVReg(info1->s_reg)) + info1 = info2; + int v_reg = mir_graph_->SRegToVReg(info1->s_reg); + StoreBaseDispWide(rMIPS_SP, VRegOffset(v_reg), info1->reg, info1->partner); + } +} + +void MipsMir2Lir::FlushReg(int reg) +{ + RegisterInfo* info = GetRegInfo(reg); + if (info->live && info->dirty) { + info->dirty = false; + int v_reg = mir_graph_->SRegToVReg(info->s_reg); + StoreBaseDisp(rMIPS_SP, VRegOffset(v_reg), reg, kWord); + } +} + +/* Give access to the target-dependent FP register encoding to common code */ +bool MipsMir2Lir::IsFpReg(int reg) { + return MIPS_FPREG(reg); +} + +/* Clobber all regs that might be used by an external C call */ +void MipsMir2Lir::ClobberCalleeSave() +{ + Clobber(r_ZERO); + Clobber(r_AT); + Clobber(r_V0); + Clobber(r_V1); + Clobber(r_A0); + Clobber(r_A1); + Clobber(r_A2); + Clobber(r_A3); + Clobber(r_T0); + Clobber(r_T1); + Clobber(r_T2); + Clobber(r_T3); + Clobber(r_T4); + Clobber(r_T5); + Clobber(r_T6); + Clobber(r_T7); + Clobber(r_T8); + Clobber(r_T9); + Clobber(r_K0); + Clobber(r_K1); + Clobber(r_GP); + Clobber(r_FP); + Clobber(r_RA); + Clobber(r_F0); + Clobber(r_F1); + Clobber(r_F2); + Clobber(r_F3); + Clobber(r_F4); + Clobber(r_F5); + Clobber(r_F6); + Clobber(r_F7); + Clobber(r_F8); + Clobber(r_F9); + Clobber(r_F10); + Clobber(r_F11); + Clobber(r_F12); + Clobber(r_F13); + Clobber(r_F14); + Clobber(r_F15); +} + +RegLocation MipsMir2Lir::GetReturnWideAlt() +{ + UNIMPLEMENTED(FATAL) << "No GetReturnWideAlt for MIPS"; + RegLocation res = LocCReturnWide(); + return res; +} + +RegLocation MipsMir2Lir::GetReturnAlt() +{ + UNIMPLEMENTED(FATAL) << "No GetReturnAlt for MIPS"; + RegLocation res = LocCReturn(); + return res; +} + +MipsMir2Lir::RegisterInfo* MipsMir2Lir::GetRegInfo(int reg) +{ + return MIPS_FPREG(reg) ? ®_pool_->FPRegs[reg & MIPS_FP_REG_MASK] + : ®_pool_->core_regs[reg]; +} + +/* To be used when explicitly managing register use */ +void MipsMir2Lir::LockCallTemps() +{ + LockTemp(rMIPS_ARG0); + LockTemp(rMIPS_ARG1); + LockTemp(rMIPS_ARG2); + LockTemp(rMIPS_ARG3); +} + +/* To be used when explicitly managing register use */ +void MipsMir2Lir::FreeCallTemps() +{ + FreeTemp(rMIPS_ARG0); + FreeTemp(rMIPS_ARG1); + FreeTemp(rMIPS_ARG2); + FreeTemp(rMIPS_ARG3); +} + +void MipsMir2Lir::GenMemBarrier(MemBarrierKind barrier_kind) +{ +#if ANDROID_SMP != 0 + NewLIR1(kMipsSync, 0 /* Only stype currently supported */); +#endif +} + +/* + * Alloc a pair of core registers, or a double. Low reg in low byte, + * high reg in next byte. + */ +int MipsMir2Lir::AllocTypedTempPair(bool fp_hint, + int reg_class) +{ + int high_reg; + int low_reg; + int res = 0; + + if (((reg_class == kAnyReg) && fp_hint) || (reg_class == kFPReg)) { + low_reg = AllocTempDouble(); + high_reg = low_reg + 1; + res = (low_reg & 0xff) | ((high_reg & 0xff) << 8); + return res; + } + + low_reg = AllocTemp(); + high_reg = AllocTemp(); + res = (low_reg & 0xff) | ((high_reg & 0xff) << 8); + return res; +} + +int MipsMir2Lir::AllocTypedTemp(bool fp_hint, int reg_class) +{ + if (((reg_class == kAnyReg) && fp_hint) || (reg_class == kFPReg)) +{ + return AllocTempFloat(); +} + return AllocTemp(); +} + +void MipsMir2Lir::CompilerInitializeRegAlloc() +{ + int num_regs = sizeof(core_regs)/sizeof(*core_regs); + int num_reserved = sizeof(ReservedRegs)/sizeof(*ReservedRegs); + int num_temps = sizeof(core_temps)/sizeof(*core_temps); + int num_fp_regs = sizeof(FpRegs)/sizeof(*FpRegs); + int num_fp_temps = sizeof(fp_temps)/sizeof(*fp_temps); + reg_pool_ = static_cast<RegisterPool*>(arena_->NewMem(sizeof(*reg_pool_), true, + ArenaAllocator::kAllocRegAlloc)); + reg_pool_->num_core_regs = num_regs; + reg_pool_->core_regs = static_cast<RegisterInfo*> + (arena_->NewMem(num_regs * sizeof(*reg_pool_->core_regs), true, + ArenaAllocator::kAllocRegAlloc)); + reg_pool_->num_fp_regs = num_fp_regs; + reg_pool_->FPRegs = static_cast<RegisterInfo*> + (arena_->NewMem(num_fp_regs * sizeof(*reg_pool_->FPRegs), true, + ArenaAllocator::kAllocRegAlloc)); + CompilerInitPool(reg_pool_->core_regs, core_regs, reg_pool_->num_core_regs); + CompilerInitPool(reg_pool_->FPRegs, FpRegs, reg_pool_->num_fp_regs); + // Keep special registers from being allocated + for (int i = 0; i < num_reserved; i++) { + if (NO_SUSPEND && (ReservedRegs[i] == rMIPS_SUSPEND)) { + //To measure cost of suspend check + continue; + } + MarkInUse(ReservedRegs[i]); + } + // Mark temp regs - all others not in use can be used for promotion + for (int i = 0; i < num_temps; i++) { + MarkTemp(core_temps[i]); + } + for (int i = 0; i < num_fp_temps; i++) { + MarkTemp(fp_temps[i]); + } +} + +void MipsMir2Lir::FreeRegLocTemps(RegLocation rl_keep, RegLocation rl_free) +{ + if ((rl_free.low_reg != rl_keep.low_reg) && (rl_free.low_reg != rl_keep.high_reg) && + (rl_free.high_reg != rl_keep.low_reg) && (rl_free.high_reg != rl_keep.high_reg)) { + // No overlap, free both + FreeTemp(rl_free.low_reg); + FreeTemp(rl_free.high_reg); + } +} +/* + * In the Arm code a it is typical to use the link register + * to hold the target address. However, for Mips we must + * ensure that all branch instructions can be restarted if + * there is a trap in the shadow. Allocate a temp register. + */ +int MipsMir2Lir::LoadHelper(int offset) +{ + LoadWordDisp(rMIPS_SELF, offset, r_T9); + return r_T9; +} + +void MipsMir2Lir::SpillCoreRegs() +{ + if (num_core_spills_ == 0) { + return; + } + uint32_t mask = core_spill_mask_; + int offset = num_core_spills_ * 4; + OpRegImm(kOpSub, rMIPS_SP, offset); + for (int reg = 0; mask; mask >>= 1, reg++) { + if (mask & 0x1) { + offset -= 4; + StoreWordDisp(rMIPS_SP, offset, reg); + } + } +} + +void MipsMir2Lir::UnSpillCoreRegs() +{ + if (num_core_spills_ == 0) { + return; + } + uint32_t mask = core_spill_mask_; + int offset = frame_size_; + for (int reg = 0; mask; mask >>= 1, reg++) { + if (mask & 0x1) { + offset -= 4; + LoadWordDisp(rMIPS_SP, offset, reg); + } + } + OpRegImm(kOpAdd, rMIPS_SP, frame_size_); +} + +bool MipsMir2Lir::IsUnconditionalBranch(LIR* lir) +{ + return (lir->opcode == kMipsB); +} + +MipsMir2Lir::MipsMir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena) + : Mir2Lir(cu, mir_graph, arena) { + for (int i = 0; i < kMipsLast; i++) { + if (MipsMir2Lir::EncodingMap[i].opcode != i) { + LOG(FATAL) << "Encoding order for " << MipsMir2Lir::EncodingMap[i].name + << " is wrong: expecting " << i << ", seeing " + << static_cast<int>(MipsMir2Lir::EncodingMap[i].opcode); + } + } +} + +Mir2Lir* MipsCodeGenerator(CompilationUnit* const cu, MIRGraph* const mir_graph, + ArenaAllocator* const arena) { + return new MipsMir2Lir(cu, mir_graph, arena); +} + +uint64_t MipsMir2Lir::GetTargetInstFlags(int opcode) +{ + return MipsMir2Lir::EncodingMap[opcode].flags; +} + +const char* MipsMir2Lir::GetTargetInstName(int opcode) +{ + return MipsMir2Lir::EncodingMap[opcode].name; +} + +const char* MipsMir2Lir::GetTargetInstFmt(int opcode) +{ + return MipsMir2Lir::EncodingMap[opcode].fmt; +} + +} // namespace art |