/* * 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. */ /* This file contains codegen for the Mips ISA */ #include "codegen_mips.h" #include "base/logging.h" #include "dex/mir_graph.h" #include "dex/quick/mir_to_lir-inl.h" #include "dex/reg_storage_eq.h" #include "entrypoints/quick/quick_entrypoints.h" #include "mips_lir.h" #include "mirror/array-inl.h" namespace art { /* * Compare two 64-bit values * x = y return 0 * x < y return -1 * x > y return 1 * * Mips32 implementation * slt t0, x.hi, y.hi; # (x.hi < y.hi) ? 1:0 * sgt t1, x.hi, y.hi; # (y.hi > x.hi) ? 1:0 * subu res, t0, t1 # res = -1:1:0 for [ < > = ] * bnez res, finish * sltu t0, x.lo, y.lo * sgtu r1, x.lo, y.lo * subu res, t0, t1 * finish: * * Mips64 implementation * slt temp, x, y; # (x < y) ? 1:0 * slt res, y, x; # (x > y) ? 1:0 * subu res, res, temp; # res = -1:1:0 for [ < > = ] * */ void MipsMir2Lir::GenCmpLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) { rl_src1 = LoadValueWide(rl_src1, kCoreReg); rl_src2 = LoadValueWide(rl_src2, kCoreReg); if (cu_->target64) { RegStorage temp = AllocTempWide(); RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); NewLIR3(kMipsSlt, temp.GetReg(), rl_src1.reg.GetReg(), rl_src2.reg.GetReg()); NewLIR3(kMipsSlt, rl_result.reg.GetReg(), rl_src2.reg.GetReg(), rl_src1.reg.GetReg()); NewLIR3(kMipsSubu, rl_result.reg.GetReg(), rl_result.reg.GetReg(), temp.GetReg()); FreeTemp(temp); StoreValue(rl_dest, rl_result); } else { RegStorage t0 = AllocTemp(); RegStorage t1 = AllocTemp(); RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); NewLIR3(kMipsSlt, t0.GetReg(), rl_src1.reg.GetHighReg(), rl_src2.reg.GetHighReg()); NewLIR3(kMipsSlt, t1.GetReg(), rl_src2.reg.GetHighReg(), rl_src1.reg.GetHighReg()); NewLIR3(kMipsSubu, rl_result.reg.GetReg(), t1.GetReg(), t0.GetReg()); LIR* branch = OpCmpImmBranch(kCondNe, rl_result.reg, 0, nullptr); NewLIR3(kMipsSltu, t0.GetReg(), rl_src1.reg.GetLowReg(), rl_src2.reg.GetLowReg()); NewLIR3(kMipsSltu, t1.GetReg(), rl_src2.reg.GetLowReg(), rl_src1.reg.GetLowReg()); NewLIR3(kMipsSubu, rl_result.reg.GetReg(), t1.GetReg(), t0.GetReg()); FreeTemp(t0); FreeTemp(t1); LIR* target = NewLIR0(kPseudoTargetLabel); branch->target = target; StoreValue(rl_dest, rl_result); } } LIR* MipsMir2Lir::OpCmpBranch(ConditionCode cond, RegStorage src1, RegStorage src2, LIR* target) { LIR* branch; MipsOpCode slt_op; MipsOpCode br_op; bool cmp_zero = false; bool swapped = false; switch (cond) { case kCondEq: br_op = kMipsBeq; cmp_zero = true; break; case kCondNe: br_op = kMipsBne; cmp_zero = true; break; case kCondUlt: slt_op = kMipsSltu; br_op = kMipsBnez; break; case kCondUge: slt_op = kMipsSltu; br_op = kMipsBeqz; break; case kCondGe: slt_op = kMipsSlt; br_op = kMipsBeqz; break; case kCondGt: slt_op = kMipsSlt; br_op = kMipsBnez; swapped = true; break; case kCondLe: slt_op = kMipsSlt; br_op = kMipsBeqz; swapped = true; break; case kCondLt: slt_op = kMipsSlt; br_op = kMipsBnez; break; case kCondHi: // Gtu slt_op = kMipsSltu; br_op = kMipsBnez; swapped = true; break; default: LOG(FATAL) << "No support for ConditionCode: " << cond; return nullptr; } if (cmp_zero) { branch = NewLIR2(br_op, src1.GetReg(), src2.GetReg()); } else { RegStorage t_reg = AllocTemp(); if (swapped) { NewLIR3(slt_op, t_reg.GetReg(), src2.GetReg(), src1.GetReg()); } else { NewLIR3(slt_op, t_reg.GetReg(), src1.GetReg(), src2.GetReg()); } branch = NewLIR1(br_op, t_reg.GetReg()); FreeTemp(t_reg); } branch->target = target; return branch; } LIR* MipsMir2Lir::OpCmpImmBranch(ConditionCode cond, RegStorage reg, int check_value, LIR* target) { LIR* branch; if (check_value != 0) { // TUNING: handle s16 & kCondLt/Mi case using slti. RegStorage t_reg = AllocTemp(); LoadConstant(t_reg, check_value); branch = OpCmpBranch(cond, reg, t_reg, target); FreeTemp(t_reg); return branch; } MipsOpCode opc; switch (cond) { case kCondEq: opc = kMipsBeqz; break; case kCondGe: opc = kMipsBgez; break; case kCondGt: opc = kMipsBgtz; break; case kCondLe: opc = kMipsBlez; break; // case KCondMi: case kCondLt: opc = kMipsBltz; break; case kCondNe: opc = kMipsBnez; break; default: // Tuning: use slti when applicable RegStorage t_reg = AllocTemp(); LoadConstant(t_reg, check_value); branch = OpCmpBranch(cond, reg, t_reg, target); FreeTemp(t_reg); return branch; } branch = NewLIR1(opc, reg.GetReg()); branch->target = target; return branch; } LIR* MipsMir2Lir::OpRegCopyNoInsert(RegStorage r_dest, RegStorage r_src) { LIR* res; MipsOpCode opcode; if (!cu_->target64) { // If src or dest is a pair, we'll be using low reg. if (r_dest.IsPair()) { r_dest = r_dest.GetLow(); } if (r_src.IsPair()) { r_src = r_src.GetLow(); } } else { DCHECK(!r_dest.IsPair() && !r_src.IsPair()); } if (r_dest.IsFloat() || r_src.IsFloat()) return OpFpRegCopy(r_dest, r_src); if (cu_->target64) { // TODO: Check that r_src and r_dest are both 32 or both 64 bits length on Mips64. if (r_dest.Is64Bit() || r_src.Is64Bit()) { opcode = kMipsMove; } else { opcode = kMipsSll; } } else { opcode = kMipsMove; } res = RawLIR(current_dalvik_offset_, opcode, r_dest.GetReg(), r_src.GetReg()); if (!(cu_->disable_opt & (1 << kSafeOptimizations)) && r_dest == r_src) { res->flags.is_nop = true; } return res; } void MipsMir2Lir::OpRegCopy(RegStorage r_dest, RegStorage r_src) { if (r_dest != r_src) { LIR *res = OpRegCopyNoInsert(r_dest, r_src); AppendLIR(res); } } void MipsMir2Lir::OpRegCopyWide(RegStorage r_dest, RegStorage r_src) { if (cu_->target64) { OpRegCopy(r_dest, r_src); return; } if (r_dest != r_src) { bool dest_fp = r_dest.IsFloat(); bool src_fp = r_src.IsFloat(); if (dest_fp) { if (src_fp) { // Here if both src and dest are fp registers. OpRegCopy will choose the right copy // (solo or pair). OpRegCopy(r_dest, r_src); } else { // note the operands are swapped for the mtc1 and mthc1 instr. // Here if dest is fp reg and src is core reg. if (fpuIs32Bit_) { NewLIR2(kMipsMtc1, r_src.GetLowReg(), r_dest.GetLowReg()); NewLIR2(kMipsMtc1, r_src.GetHighReg(), r_dest.GetHighReg()); } else { r_dest = Fp64ToSolo32(r_dest); NewLIR2(kMipsMtc1, r_src.GetLowReg(), r_dest.GetReg()); NewLIR2(kMipsMthc1, r_src.GetHighReg(), r_dest.GetReg()); } } } else { if (src_fp) { // Here if dest is core reg and src is fp reg. if (fpuIs32Bit_) { NewLIR2(kMipsMfc1, r_dest.GetLowReg(), r_src.GetLowReg()); NewLIR2(kMipsMfc1, r_dest.GetHighReg(), r_src.GetHighReg()); } else { r_src = Fp64ToSolo32(r_src); NewLIR2(kMipsMfc1, r_dest.GetLowReg(), r_src.GetReg()); NewLIR2(kMipsMfhc1, r_dest.GetHighReg(), r_src.GetReg()); } } else { // Here if both src and dest are core registers. // Handle overlap if (r_src.GetHighReg() != r_dest.GetLowReg()) { OpRegCopy(r_dest.GetLow(), r_src.GetLow()); OpRegCopy(r_dest.GetHigh(), r_src.GetHigh()); } else if (r_src.GetLowReg() != r_dest.GetHighReg()) { OpRegCopy(r_dest.GetHigh(), r_src.GetHigh()); OpRegCopy(r_dest.GetLow(), r_src.GetLow()); } else { RegStorage r_tmp = AllocTemp(); OpRegCopy(r_tmp, r_src.GetHigh()); OpRegCopy(r_dest.GetLow(), r_src.GetLow()); OpRegCopy(r_dest.GetHigh(), r_tmp); FreeTemp(r_tmp); } } } } } void MipsMir2Lir::GenSelectConst32(RegStorage left_op, RegStorage right_op, ConditionCode code, int32_t true_val, int32_t false_val, RegStorage rs_dest, RegisterClass dest_reg_class) { UNUSED(dest_reg_class); // Implement as a branch-over. // TODO: Conditional move? LoadConstant(rs_dest, true_val); LIR* ne_branchover = OpCmpBranch(code, left_op, right_op, nullptr); LoadConstant(rs_dest, false_val); LIR* target_label = NewLIR0(kPseudoTargetLabel); ne_branchover->target = target_label; } void MipsMir2Lir::GenSelect(BasicBlock* bb, MIR* mir) { UNUSED(bb, mir); UNIMPLEMENTED(FATAL) << "Need codegen for select"; } void MipsMir2Lir::GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir) { UNUSED(bb, mir); UNIMPLEMENTED(FATAL) << "Need codegen for fused long cmp branch"; } RegLocation MipsMir2Lir::GenDivRem(RegLocation rl_dest, RegStorage reg1, RegStorage reg2, bool is_div) { RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); if (isaIsR6_) { NewLIR3(is_div ? kMipsR6Div : kMipsR6Mod, rl_result.reg.GetReg(), reg1.GetReg(), reg2.GetReg()); } else { NewLIR2(kMipsR2Div, reg1.GetReg(), reg2.GetReg()); NewLIR1(is_div ? kMipsR2Mflo : kMipsR2Mfhi, rl_result.reg.GetReg()); } return rl_result; } RegLocation MipsMir2Lir::GenDivRemLit(RegLocation rl_dest, RegStorage reg1, int lit, bool is_div) { RegStorage t_reg = AllocTemp(); // lit is guarantee to be a 16-bit constant if (IsUint<16>(lit)) { NewLIR3(kMipsOri, t_reg.GetReg(), rZERO, lit); } else { // Addiu will sign extend the entire width (32 or 64) of the register. NewLIR3(kMipsAddiu, t_reg.GetReg(), rZERO, lit); } RegLocation rl_result = GenDivRem(rl_dest, reg1, t_reg, is_div); FreeTemp(t_reg); return rl_result; } RegLocation MipsMir2Lir::GenDivRem(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2, bool is_div, int flags) { UNUSED(rl_dest, rl_src1, rl_src2, is_div, flags); LOG(FATAL) << "Unexpected use of GenDivRem for Mips"; UNREACHABLE(); } RegLocation MipsMir2Lir::GenDivRemLit(RegLocation rl_dest, RegLocation rl_src1, int lit, bool is_div) { UNUSED(rl_dest, rl_src1, lit, is_div); LOG(FATAL) << "Unexpected use of GenDivRemLit for Mips"; UNREACHABLE(); } bool MipsMir2Lir::GenInlinedCas(CallInfo* info, bool is_long, bool is_object) { UNUSED(info, is_long, is_object); return false; } bool MipsMir2Lir::GenInlinedAbsFloat(CallInfo* info) { UNUSED(info); // TODO: add Mips implementation. return false; } bool MipsMir2Lir::GenInlinedAbsDouble(CallInfo* info) { UNUSED(info); // TODO: add Mips implementation. return false; } bool MipsMir2Lir::GenInlinedSqrt(CallInfo* info) { UNUSED(info); return false; } bool MipsMir2Lir::GenInlinedPeek(CallInfo* info, OpSize size) { if (size != kSignedByte) { // MIPS supports only aligned access. Defer unaligned access to JNI implementation. return false; } RegLocation rl_src_address = info->args[0]; // Long address. if (!cu_->target64) { rl_src_address = NarrowRegLoc(rl_src_address); // Ignore high half in info->args[1]. } RegLocation rl_dest = InlineTarget(info); RegLocation rl_address; if (cu_->target64) { rl_address = LoadValueWide(rl_src_address, kCoreReg); } else { rl_address = LoadValue(rl_src_address, kCoreReg); } RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); DCHECK(size == kSignedByte); LoadBaseDisp(rl_address.reg, 0, rl_result.reg, size, kNotVolatile); StoreValue(rl_dest, rl_result); return true; } bool MipsMir2Lir::GenInlinedPoke(CallInfo* info, OpSize size) { if (size != kSignedByte) { // MIPS supports only aligned access. Defer unaligned access to JNI implementation. return false; } RegLocation rl_src_address = info->args[0]; // Long address. if (!cu_->target64) { rl_src_address = NarrowRegLoc(rl_src_address); // Ignore high half in info->args[1]. } RegLocation rl_src_value = info->args[2]; // [size] value. RegLocation rl_address; if (cu_->target64) { rl_address = LoadValueWide(rl_src_address, kCoreReg); } else { rl_address = LoadValue(rl_src_address, kCoreReg); } DCHECK(size == kSignedByte); RegLocation rl_value = LoadValue(rl_src_value, kCoreReg); StoreBaseDisp(rl_address.reg, 0, rl_value.reg, size, kNotVolatile); return true; } void MipsMir2Lir::OpPcRelLoad(RegStorage reg, LIR* target) { UNUSED(reg, target); LOG(FATAL) << "Unexpected use of OpPcRelLoad for Mips"; UNREACHABLE(); } LIR* MipsMir2Lir::OpVldm(RegStorage r_base, int count) { UNUSED(r_base, count); LOG(FATAL) << "Unexpected use of OpVldm for Mips"; UNREACHABLE(); } LIR* MipsMir2Lir::OpVstm(RegStorage r_base, int count) { UNUSED(r_base, count); LOG(FATAL) << "Unexpected use of OpVstm for Mips"; UNREACHABLE(); } void MipsMir2Lir::GenMultiplyByTwoBitMultiplier(RegLocation rl_src, RegLocation rl_result, int lit, int first_bit, int second_bit) { UNUSED(lit); RegStorage t_reg = AllocTemp(); OpRegRegImm(kOpLsl, t_reg, rl_src.reg, second_bit - first_bit); OpRegRegReg(kOpAdd, rl_result.reg, rl_src.reg, t_reg); FreeTemp(t_reg); if (first_bit != 0) { OpRegRegImm(kOpLsl, rl_result.reg, rl_result.reg, first_bit); } } void MipsMir2Lir::GenDivZeroCheckWide(RegStorage reg) { if (cu_->target64) { GenDivZeroCheck(reg); } else { DCHECK(reg.IsPair()); // TODO: support k64BitSolo. RegStorage t_reg = AllocTemp(); OpRegRegReg(kOpOr, t_reg, reg.GetLow(), reg.GetHigh()); GenDivZeroCheck(t_reg); FreeTemp(t_reg); } } // Test suspend flag, return target of taken suspend branch. LIR* MipsMir2Lir::OpTestSuspend(LIR* target) { OpRegImm(kOpSub, TargetPtrReg(kSuspend), 1); return OpCmpImmBranch((target == nullptr) ? kCondEq : kCondNe, TargetPtrReg(kSuspend), 0, target); } // Decrement register and branch on condition. LIR* MipsMir2Lir::OpDecAndBranch(ConditionCode c_code, RegStorage reg, LIR* target) { OpRegImm(kOpSub, reg, 1); return OpCmpImmBranch(c_code, reg, 0, target); } bool MipsMir2Lir::SmallLiteralDivRem(Instruction::Code dalvik_opcode, bool is_div, RegLocation rl_src, RegLocation rl_dest, int lit) { UNUSED(dalvik_opcode, is_div, rl_src, rl_dest, lit); LOG(FATAL) << "Unexpected use of smallLiteralDive in Mips"; UNREACHABLE(); } bool MipsMir2Lir::EasyMultiply(RegLocation rl_src, RegLocation rl_dest, int lit) { UNUSED(rl_src, rl_dest, lit); LOG(FATAL) << "Unexpected use of easyMultiply in Mips"; UNREACHABLE(); } LIR* MipsMir2Lir::OpIT(ConditionCode cond, const char* guide) { UNUSED(cond, guide); LOG(FATAL) << "Unexpected use of OpIT in Mips"; UNREACHABLE(); } void MipsMir2Lir::OpEndIT(LIR* it) { UNUSED(it); LOG(FATAL) << "Unexpected use of OpEndIT in Mips"; } void MipsMir2Lir::GenAddLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) { rl_src1 = LoadValueWide(rl_src1, kCoreReg); rl_src2 = LoadValueWide(rl_src2, kCoreReg); RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); /* * [v1 v0] = [a1 a0] + [a3 a2]; * addu v0,a2,a0 * addu t1,a3,a1 * sltu v1,v0,a2 * addu v1,v1,t1 */ OpRegRegReg(kOpAdd, rl_result.reg.GetLow(), rl_src2.reg.GetLow(), rl_src1.reg.GetLow()); RegStorage t_reg = AllocTemp(); OpRegRegReg(kOpAdd, t_reg, rl_src2.reg.GetHigh(), rl_src1.reg.GetHigh()); NewLIR3(kMipsSltu, rl_result.reg.GetHighReg(), rl_result.reg.GetLowReg(), rl_src2.reg.GetLowReg()); OpRegRegReg(kOpAdd, rl_result.reg.GetHigh(), rl_result.reg.GetHigh(), t_reg); FreeTemp(t_reg); StoreValueWide(rl_dest, rl_result); } void MipsMir2Lir::GenSubLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) { rl_src1 = LoadValueWide(rl_src1, kCoreReg); rl_src2 = LoadValueWide(rl_src2, kCoreReg); RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); /* * [v1 v0] = [a1 a0] - [a3 a2]; * sltu t1,a0,a2 * subu v0,a0,a2 * subu v1,a1,a3 * subu v1,v1,t1 */ RegStorage t_reg = AllocTemp(); NewLIR3(kMipsSltu, t_reg.GetReg(), rl_src1.reg.GetLowReg(), rl_src2.reg.GetLowReg()); OpRegRegReg(kOpSub, rl_result.reg.GetLow(), rl_src1.reg.GetLow(), rl_src2.reg.GetLow()); OpRegRegReg(kOpSub, rl_result.reg.GetHigh(), rl_src1.reg.GetHigh(), rl_src2.reg.GetHigh()); OpRegRegReg(kOpSub, rl_result.reg.GetHigh(), rl_result.reg.GetHigh(), t_reg); FreeTemp(t_reg); StoreValueWide(rl_dest, rl_result); } void MipsMir2Lir::GenArithOpLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2, int flags) { if (cu_->target64) { switch (opcode) { case Instruction::NOT_LONG: GenNotLong(rl_dest, rl_src2); return; case Instruction::ADD_LONG: case Instruction::ADD_LONG_2ADDR: GenLongOp(kOpAdd, rl_dest, rl_src1, rl_src2); return; case Instruction::SUB_LONG: case Instruction::SUB_LONG_2ADDR: GenLongOp(kOpSub, rl_dest, rl_src1, rl_src2); return; case Instruction::MUL_LONG: case Instruction::MUL_LONG_2ADDR: GenMulLong(rl_dest, rl_src1, rl_src2); return; case Instruction::DIV_LONG: case Instruction::DIV_LONG_2ADDR: GenDivRemLong(opcode, rl_dest, rl_src1, rl_src2, /*is_div*/ true, flags); return; case Instruction::REM_LONG: case Instruction::REM_LONG_2ADDR: GenDivRemLong(opcode, rl_dest, rl_src1, rl_src2, /*is_div*/ false, flags); return; case Instruction::AND_LONG: case Instruction::AND_LONG_2ADDR: GenLongOp(kOpAnd, rl_dest, rl_src1, rl_src2); return; case Instruction::OR_LONG: case Instruction::OR_LONG_2ADDR: GenLongOp(kOpOr, rl_dest, rl_src1, rl_src2); return; case Instruction::XOR_LONG: case Instruction::XOR_LONG_2ADDR: GenLongOp(kOpXor, rl_dest, rl_src1, rl_src2); return; case Instruction::NEG_LONG: GenNegLong(rl_dest, rl_src2); return; default: LOG(FATAL) << "Invalid long arith op"; return; } } else { switch (opcode) { case Instruction::ADD_LONG: case Instruction::ADD_LONG_2ADDR: GenAddLong(rl_dest, rl_src1, rl_src2); return; case Instruction::SUB_LONG: case Instruction::SUB_LONG_2ADDR: GenSubLong(rl_dest, rl_src1, rl_src2); return; case Instruction::NEG_LONG: GenNegLong(rl_dest, rl_src2); return; default: break; } // Fallback for all other ops. Mir2Lir::GenArithOpLong(opcode, rl_dest, rl_src1, rl_src2, flags); } } void MipsMir2Lir::GenLongOp(OpKind op, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) { rl_src1 = LoadValueWide(rl_src1, kCoreReg); rl_src2 = LoadValueWide(rl_src2, kCoreReg); RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true); OpRegRegReg(op, rl_result.reg, rl_src1.reg, rl_src2.reg); StoreValueWide(rl_dest, rl_result); } void MipsMir2Lir::GenNotLong(RegLocation rl_dest, RegLocation rl_src) { rl_src = LoadValueWide(rl_src, kCoreReg); RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true); OpRegReg(kOpMvn, rl_result.reg, rl_src.reg); StoreValueWide(rl_dest, rl_result); } void MipsMir2Lir::GenMulLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) { rl_src1 = LoadValueWide(rl_src1, kCoreReg); rl_src2 = LoadValueWide(rl_src2, kCoreReg); RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true); NewLIR3(kMips64Dmul, rl_result.reg.GetReg(), rl_src1.reg.GetReg(), rl_src2.reg.GetReg()); StoreValueWide(rl_dest, rl_result); } void MipsMir2Lir::GenDivRemLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2, bool is_div, int flags) { UNUSED(opcode); // TODO: Implement easy div/rem? rl_src1 = LoadValueWide(rl_src1, kCoreReg); rl_src2 = LoadValueWide(rl_src2, kCoreReg); if ((flags & MIR_IGNORE_DIV_ZERO_CHECK) == 0) { GenDivZeroCheckWide(rl_src2.reg); } RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true); NewLIR3(is_div ? kMips64Ddiv : kMips64Dmod, rl_result.reg.GetReg(), rl_src1.reg.GetReg(), rl_src2.reg.GetReg()); StoreValueWide(rl_dest, rl_result); } void MipsMir2Lir::GenNegLong(RegLocation rl_dest, RegLocation rl_src) { rl_src = LoadValueWide(rl_src, kCoreReg); RegLocation rl_result; if (cu_->target64) { rl_result = EvalLocWide(rl_dest, kCoreReg, true); OpRegReg(kOpNeg, rl_result.reg, rl_src.reg); StoreValueWide(rl_dest, rl_result); } else { rl_result = EvalLoc(rl_dest, kCoreReg, true); // [v1 v0] = -[a1 a0] // negu v0,a0 // negu v1,a1 // sltu t1,r_zero // subu v1,v1,t1 OpRegReg(kOpNeg, rl_result.reg.GetLow(), rl_src.reg.GetLow()); OpRegReg(kOpNeg, rl_result.reg.GetHigh(), rl_src.reg.GetHigh()); RegStorage t_reg = AllocTemp(); NewLIR3(kMipsSltu, t_reg.GetReg(), rZERO, rl_result.reg.GetLowReg()); OpRegRegReg(kOpSub, rl_result.reg.GetHigh(), rl_result.reg.GetHigh(), t_reg); FreeTemp(t_reg); StoreValueWide(rl_dest, rl_result); } } /* * Generate array load */ void MipsMir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array, RegLocation rl_index, RegLocation rl_dest, int scale) { RegisterClass reg_class = RegClassBySize(size); int len_offset = mirror::Array::LengthOffset().Int32Value(); int data_offset; RegLocation rl_result; rl_array = LoadValue(rl_array, kRefReg); rl_index = LoadValue(rl_index, kCoreReg); // FIXME: need to add support for rl_index.is_const. if (size == k64 || size == kDouble) { data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Int32Value(); } else { data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Int32Value(); } // Null object? GenNullCheck(rl_array.reg, opt_flags); RegStorage reg_ptr = (cu_->target64) ? AllocTempRef() : AllocTemp(); bool needs_range_check = (!(opt_flags & MIR_IGNORE_RANGE_CHECK)); RegStorage reg_len; if (needs_range_check) { reg_len = AllocTemp(); // Get len. Load32Disp(rl_array.reg, len_offset, reg_len); MarkPossibleNullPointerException(opt_flags); } else { ForceImplicitNullCheck(rl_array.reg, opt_flags, false); } // reg_ptr -> array data. OpRegRegImm(kOpAdd, reg_ptr, rl_array.reg, data_offset); FreeTemp(rl_array.reg); if ((size == k64) || (size == kDouble)) { if (scale) { RegStorage r_new_index = AllocTemp(); OpRegRegImm(kOpLsl, r_new_index, rl_index.reg, scale); OpRegReg(kOpAdd, reg_ptr, r_new_index); FreeTemp(r_new_index); } else { OpRegReg(kOpAdd, reg_ptr, rl_index.reg); } FreeTemp(rl_index.reg); rl_result = EvalLoc(rl_dest, reg_class, true); if (needs_range_check) { GenArrayBoundsCheck(rl_index.reg, reg_len); FreeTemp(reg_len); } LoadBaseDisp(reg_ptr, 0, rl_result.reg, size, kNotVolatile); FreeTemp(reg_ptr); StoreValueWide(rl_dest, rl_result); } else { rl_result = EvalLoc(rl_dest, reg_class, true); if (needs_range_check) { GenArrayBoundsCheck(rl_index.reg, reg_len); FreeTemp(reg_len); } if (cu_->target64) { if (rl_result.ref) { LoadBaseIndexed(reg_ptr, As64BitReg(rl_index.reg), As32BitReg(rl_result.reg), scale, kReference); } else { LoadBaseIndexed(reg_ptr, As64BitReg(rl_index.reg), rl_result.reg, scale, size); } } else { LoadBaseIndexed(reg_ptr, rl_index.reg, rl_result.reg, scale, size); } FreeTemp(reg_ptr); StoreValue(rl_dest, rl_result); } } /* * Generate array store * */ void MipsMir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, RegLocation rl_index, RegLocation rl_src, int scale, bool card_mark) { RegisterClass reg_class = RegClassBySize(size); int len_offset = mirror::Array::LengthOffset().Int32Value(); int data_offset; if (size == k64 || size == kDouble) { data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Int32Value(); } else { data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Int32Value(); } rl_array = LoadValue(rl_array, kRefReg); rl_index = LoadValue(rl_index, kCoreReg); // FIXME: need to add support for rl_index.is_const. RegStorage reg_ptr; bool allocated_reg_ptr_temp = false; if (IsTemp(rl_array.reg) && !card_mark) { Clobber(rl_array.reg); reg_ptr = rl_array.reg; } else { reg_ptr = AllocTemp(); OpRegCopy(reg_ptr, rl_array.reg); allocated_reg_ptr_temp = true; } // Null object? GenNullCheck(rl_array.reg, opt_flags); bool needs_range_check = (!(opt_flags & MIR_IGNORE_RANGE_CHECK)); RegStorage reg_len; if (needs_range_check) { reg_len = AllocTemp(); // NOTE: max live temps(4) here. // Get len. Load32Disp(rl_array.reg, len_offset, reg_len); MarkPossibleNullPointerException(opt_flags); } else { ForceImplicitNullCheck(rl_array.reg, opt_flags, false); } // reg_ptr -> array data. OpRegImm(kOpAdd, reg_ptr, data_offset); // At this point, reg_ptr points to array, 2 live temps. if ((size == k64) || (size == kDouble)) { // TUNING: specific wide routine that can handle fp regs. if (scale) { RegStorage r_new_index = AllocTemp(); OpRegRegImm(kOpLsl, r_new_index, rl_index.reg, scale); OpRegReg(kOpAdd, reg_ptr, r_new_index); FreeTemp(r_new_index); } else { OpRegReg(kOpAdd, reg_ptr, rl_index.reg); } rl_src = LoadValueWide(rl_src, reg_class); if (needs_range_check) { GenArrayBoundsCheck(rl_index.reg, reg_len); FreeTemp(reg_len); } StoreBaseDisp(reg_ptr, 0, rl_src.reg, size, kNotVolatile); } else { rl_src = LoadValue(rl_src, reg_class); if (needs_range_check) { GenArrayBoundsCheck(rl_index.reg, reg_len); FreeTemp(reg_len); } StoreBaseIndexed(reg_ptr, rl_index.reg, rl_src.reg, scale, size); } if (allocated_reg_ptr_temp) { FreeTemp(reg_ptr); } if (card_mark) { MarkGCCard(opt_flags, rl_src.reg, rl_array.reg); } } void MipsMir2Lir::GenShiftOpLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_shift) { if (!cu_->target64) { Mir2Lir::GenShiftOpLong(opcode, rl_dest, rl_src1, rl_shift); return; } OpKind op = kOpBkpt; switch (opcode) { case Instruction::SHL_LONG: case Instruction::SHL_LONG_2ADDR: op = kOpLsl; break; case Instruction::SHR_LONG: case Instruction::SHR_LONG_2ADDR: op = kOpAsr; break; case Instruction::USHR_LONG: case Instruction::USHR_LONG_2ADDR: op = kOpLsr; break; default: LOG(FATAL) << "Unexpected case: " << opcode; } rl_shift = LoadValue(rl_shift, kCoreReg); rl_src1 = LoadValueWide(rl_src1, kCoreReg); RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true); OpRegRegReg(op, rl_result.reg, rl_src1.reg, As64BitReg(rl_shift.reg)); StoreValueWide(rl_dest, rl_result); } void MipsMir2Lir::GenShiftImmOpLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_shift, int flags) { UNUSED(flags); if (!cu_->target64) { // Default implementation is just to ignore the constant case. GenShiftOpLong(opcode, rl_dest, rl_src1, rl_shift); return; } OpKind op = kOpBkpt; // Per spec, we only care about low 6 bits of shift amount. int shift_amount = mir_graph_->ConstantValue(rl_shift) & 0x3f; rl_src1 = LoadValueWide(rl_src1, kCoreReg); if (shift_amount == 0) { StoreValueWide(rl_dest, rl_src1); return; } RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true); switch (opcode) { case Instruction::SHL_LONG: case Instruction::SHL_LONG_2ADDR: op = kOpLsl; break; case Instruction::SHR_LONG: case Instruction::SHR_LONG_2ADDR: op = kOpAsr; break; case Instruction::USHR_LONG: case Instruction::USHR_LONG_2ADDR: op = kOpLsr; break; default: LOG(FATAL) << "Unexpected case"; } OpRegRegImm(op, rl_result.reg, rl_src1.reg, shift_amount); StoreValueWide(rl_dest, rl_result); } void MipsMir2Lir::GenArithImmOpLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2, int flags) { // Default - bail to non-const handler. GenArithOpLong(opcode, rl_dest, rl_src1, rl_src2, flags); } void MipsMir2Lir::GenIntToLong(RegLocation rl_dest, RegLocation rl_src) { if (!cu_->target64) { Mir2Lir::GenIntToLong(rl_dest, rl_src); return; } rl_src = LoadValue(rl_src, kCoreReg); RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true); NewLIR3(kMipsSll, rl_result.reg.GetReg(), As64BitReg(rl_src.reg).GetReg(), 0); StoreValueWide(rl_dest, rl_result); } void MipsMir2Lir::GenConversionCall(QuickEntrypointEnum trampoline, RegLocation rl_dest, RegLocation rl_src, RegisterClass reg_class) { FlushAllRegs(); // Send everything to home location. CallRuntimeHelperRegLocation(trampoline, rl_src, false); if (rl_dest.wide) { RegLocation rl_result; rl_result = GetReturnWide(reg_class); StoreValueWide(rl_dest, rl_result); } else { RegLocation rl_result; rl_result = GetReturn(reg_class); StoreValue(rl_dest, rl_result); } } } // namespace art