/* * 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 "dex/quick/mir_to_lir-inl.h" #include "entrypoints/quick/quick_entrypoints.h" #include "mips_lir.h" #include "mirror/array.h" namespace art { /* * Compare two 64-bit values * x = y return 0 * x < y return -1 * x > y return 1 * * 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: * */ void MipsMir2Lir::GenCmpLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) { rl_src1 = LoadValueWide(rl_src1, kCoreReg); rl_src2 = LoadValueWide(rl_src2, kCoreReg); int t0 = AllocTemp(); int t1 = AllocTemp(); RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); NewLIR3(kMipsSlt, t0, rl_src1.high_reg, rl_src2.high_reg); NewLIR3(kMipsSlt, t1, rl_src2.high_reg, rl_src1.high_reg); NewLIR3(kMipsSubu, rl_result.low_reg, t1, t0); LIR* branch = OpCmpImmBranch(kCondNe, rl_result.low_reg, 0, NULL); NewLIR3(kMipsSltu, t0, rl_src1.low_reg, rl_src2.low_reg); NewLIR3(kMipsSltu, t1, rl_src2.low_reg, rl_src1.low_reg); NewLIR3(kMipsSubu, rl_result.low_reg, t1, t0); FreeTemp(t0); FreeTemp(t1); LIR* target = NewLIR0(kPseudoTargetLabel); branch->target = target; StoreValue(rl_dest, rl_result); } LIR* MipsMir2Lir::OpCmpBranch(ConditionCode cond, int src1, int 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 kCondCc: slt_op = kMipsSltu; br_op = kMipsBnez; break; case kCondCs: 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 NULL; } if (cmp_zero) { branch = NewLIR2(br_op, src1, src2); } else { int t_reg = AllocTemp(); if (swapped) { NewLIR3(slt_op, t_reg, src2, src1); } else { NewLIR3(slt_op, t_reg, src1, src2); } branch = NewLIR1(br_op, t_reg); FreeTemp(t_reg); } branch->target = target; return branch; } LIR* MipsMir2Lir::OpCmpImmBranch(ConditionCode cond, int reg, int check_value, LIR* target) { LIR* branch; if (check_value != 0) { // TUNING: handle s16 & kCondLt/Mi case using slti int 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 int t_reg = AllocTemp(); LoadConstant(t_reg, check_value); branch = OpCmpBranch(cond, reg, t_reg, target); FreeTemp(t_reg); return branch; } branch = NewLIR1(opc, reg); branch->target = target; return branch; } LIR* MipsMir2Lir::OpRegCopyNoInsert(int r_dest, int r_src) { if (MIPS_FPREG(r_dest) || MIPS_FPREG(r_src)) return OpFpRegCopy(r_dest, r_src); LIR* res = RawLIR(current_dalvik_offset_, kMipsMove, r_dest, r_src); if (!(cu_->disable_opt & (1 << kSafeOptimizations)) && r_dest == r_src) { res->flags.is_nop = true; } return res; } LIR* MipsMir2Lir::OpRegCopy(int r_dest, int r_src) { LIR *res = OpRegCopyNoInsert(r_dest, r_src); AppendLIR(res); return res; } void MipsMir2Lir::OpRegCopyWide(int dest_lo, int dest_hi, int src_lo, int src_hi) { bool dest_fp = MIPS_FPREG(dest_lo) && MIPS_FPREG(dest_hi); bool src_fp = MIPS_FPREG(src_lo) && MIPS_FPREG(src_hi); assert(MIPS_FPREG(src_lo) == MIPS_FPREG(src_hi)); assert(MIPS_FPREG(dest_lo) == MIPS_FPREG(dest_hi)); if (dest_fp) { if (src_fp) { OpRegCopy(S2d(dest_lo, dest_hi), S2d(src_lo, src_hi)); } else { /* note the operands are swapped for the mtc1 instr */ NewLIR2(kMipsMtc1, src_lo, dest_lo); NewLIR2(kMipsMtc1, src_hi, dest_hi); } } else { if (src_fp) { NewLIR2(kMipsMfc1, dest_lo, src_lo); NewLIR2(kMipsMfc1, dest_hi, src_hi); } else { // Handle overlap if (src_hi == dest_lo) { OpRegCopy(dest_hi, src_hi); OpRegCopy(dest_lo, src_lo); } else { OpRegCopy(dest_lo, src_lo); OpRegCopy(dest_hi, src_hi); } } } } void MipsMir2Lir::GenSelect(BasicBlock* bb, MIR* mir) { UNIMPLEMENTED(FATAL) << "Need codegen for select"; } void MipsMir2Lir::GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir) { UNIMPLEMENTED(FATAL) << "Need codegen for fused long cmp branch"; } LIR* MipsMir2Lir::GenRegMemCheck(ConditionCode c_code, int reg1, int base, int offset, ThrowKind kind) { LOG(FATAL) << "Unexpected use of GenRegMemCheck for Arm"; return NULL; } RegLocation MipsMir2Lir::GenDivRem(RegLocation rl_dest, int reg1, int reg2, bool is_div) { NewLIR4(kMipsDiv, r_HI, r_LO, reg1, reg2); RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); if (is_div) { NewLIR2(kMipsMflo, rl_result.low_reg, r_LO); } else { NewLIR2(kMipsMfhi, rl_result.low_reg, r_HI); } return rl_result; } RegLocation MipsMir2Lir::GenDivRemLit(RegLocation rl_dest, int reg1, int lit, bool is_div) { int t_reg = AllocTemp(); NewLIR3(kMipsAddiu, t_reg, r_ZERO, lit); NewLIR4(kMipsDiv, r_HI, r_LO, reg1, t_reg); RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); if (is_div) { NewLIR2(kMipsMflo, rl_result.low_reg, r_LO); } else { NewLIR2(kMipsMfhi, rl_result.low_reg, r_HI); } FreeTemp(t_reg); return rl_result; } void MipsMir2Lir::OpLea(int rBase, int reg1, int reg2, int scale, int offset) { LOG(FATAL) << "Unexpected use of OpLea for Arm"; } void MipsMir2Lir::OpTlsCmp(ThreadOffset offset, int val) { LOG(FATAL) << "Unexpected use of OpTlsCmp for Arm"; } bool MipsMir2Lir::GenInlinedCas32(CallInfo* info, bool need_write_barrier) { DCHECK_NE(cu_->instruction_set, kThumb2); return false; } bool MipsMir2Lir::GenInlinedSqrt(CallInfo* info) { DCHECK_NE(cu_->instruction_set, kThumb2); return false; } LIR* MipsMir2Lir::OpPcRelLoad(int reg, LIR* target) { LOG(FATAL) << "Unexpected use of OpPcRelLoad for Mips"; return NULL; } LIR* MipsMir2Lir::OpVldm(int rBase, int count) { LOG(FATAL) << "Unexpected use of OpVldm for Mips"; return NULL; } LIR* MipsMir2Lir::OpVstm(int rBase, int count) { LOG(FATAL) << "Unexpected use of OpVstm for Mips"; return NULL; } void MipsMir2Lir::GenMultiplyByTwoBitMultiplier(RegLocation rl_src, RegLocation rl_result, int lit, int first_bit, int second_bit) { int t_reg = AllocTemp(); OpRegRegImm(kOpLsl, t_reg, rl_src.low_reg, second_bit - first_bit); OpRegRegReg(kOpAdd, rl_result.low_reg, rl_src.low_reg, t_reg); FreeTemp(t_reg); if (first_bit != 0) { OpRegRegImm(kOpLsl, rl_result.low_reg, rl_result.low_reg, first_bit); } } void MipsMir2Lir::GenDivZeroCheck(int reg_lo, int reg_hi) { int t_reg = AllocTemp(); OpRegRegReg(kOpOr, t_reg, reg_lo, reg_hi); GenImmedCheck(kCondEq, t_reg, 0, kThrowDivZero); FreeTemp(t_reg); } // Test suspend flag, return target of taken suspend branch LIR* MipsMir2Lir::OpTestSuspend(LIR* target) { OpRegImm(kOpSub, rMIPS_SUSPEND, 1); return OpCmpImmBranch((target == NULL) ? kCondEq : kCondNe, rMIPS_SUSPEND, 0, target); } // Decrement register and branch on condition LIR* MipsMir2Lir::OpDecAndBranch(ConditionCode c_code, int 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) { LOG(FATAL) << "Unexpected use of smallLiteralDive in Mips"; return false; } LIR* MipsMir2Lir::OpIT(ConditionCode cond, const char* guide) { LOG(FATAL) << "Unexpected use of OpIT in Mips"; return NULL; } void MipsMir2Lir::GenMulLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) { LOG(FATAL) << "Unexpected use of GenMulLong for 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.low_reg, rl_src2.low_reg, rl_src1.low_reg); int t_reg = AllocTemp(); OpRegRegReg(kOpAdd, t_reg, rl_src2.high_reg, rl_src1.high_reg); NewLIR3(kMipsSltu, rl_result.high_reg, rl_result.low_reg, rl_src2.low_reg); OpRegRegReg(kOpAdd, rl_result.high_reg, rl_result.high_reg, 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 */ int t_reg = AllocTemp(); NewLIR3(kMipsSltu, t_reg, rl_src1.low_reg, rl_src2.low_reg); OpRegRegReg(kOpSub, rl_result.low_reg, rl_src1.low_reg, rl_src2.low_reg); OpRegRegReg(kOpSub, rl_result.high_reg, rl_src1.high_reg, rl_src2.high_reg); OpRegRegReg(kOpSub, rl_result.high_reg, rl_result.high_reg, t_reg); FreeTemp(t_reg); StoreValueWide(rl_dest, rl_result); } void MipsMir2Lir::GenNegLong(RegLocation rl_dest, RegLocation rl_src) { rl_src = LoadValueWide(rl_src, kCoreReg); RegLocation 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.low_reg, rl_src.low_reg); OpRegReg(kOpNeg, rl_result.high_reg, rl_src.high_reg); int t_reg = AllocTemp(); NewLIR3(kMipsSltu, t_reg, r_ZERO, rl_result.low_reg); OpRegRegReg(kOpSub, rl_result.high_reg, rl_result.high_reg, t_reg); FreeTemp(t_reg); StoreValueWide(rl_dest, rl_result); } void MipsMir2Lir::GenAndLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) { LOG(FATAL) << "Unexpected use of GenAndLong for Mips"; } void MipsMir2Lir::GenOrLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) { LOG(FATAL) << "Unexpected use of GenOrLong for Mips"; } void MipsMir2Lir::GenXorLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) { LOG(FATAL) << "Unexpected use of GenXorLong for Mips"; } /* * 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 = oat_reg_class_by_size(size); int len_offset = mirror::Array::LengthOffset().Int32Value(); int data_offset; RegLocation rl_result; rl_array = LoadValue(rl_array, kCoreReg); rl_index = LoadValue(rl_index, kCoreReg); if (size == kLong || 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.s_reg_low, rl_array.low_reg, opt_flags); int reg_ptr = AllocTemp(); bool needs_range_check = (!(opt_flags & MIR_IGNORE_RANGE_CHECK)); int reg_len = INVALID_REG; if (needs_range_check) { reg_len = AllocTemp(); /* Get len */ LoadWordDisp(rl_array.low_reg, len_offset, reg_len); } /* reg_ptr -> array data */ OpRegRegImm(kOpAdd, reg_ptr, rl_array.low_reg, data_offset); FreeTemp(rl_array.low_reg); if ((size == kLong) || (size == kDouble)) { if (scale) { int r_new_index = AllocTemp(); OpRegRegImm(kOpLsl, r_new_index, rl_index.low_reg, scale); OpRegReg(kOpAdd, reg_ptr, r_new_index); FreeTemp(r_new_index); } else { OpRegReg(kOpAdd, reg_ptr, rl_index.low_reg); } FreeTemp(rl_index.low_reg); rl_result = EvalLoc(rl_dest, reg_class, true); if (needs_range_check) { // TODO: change kCondCS to a more meaningful name, is the sense of // carry-set/clear flipped? GenRegRegCheck(kCondCs, rl_index.low_reg, reg_len, kThrowArrayBounds); FreeTemp(reg_len); } LoadBaseDispWide(reg_ptr, 0, rl_result.low_reg, rl_result.high_reg, INVALID_SREG); FreeTemp(reg_ptr); StoreValueWide(rl_dest, rl_result); } else { rl_result = EvalLoc(rl_dest, reg_class, true); if (needs_range_check) { // TODO: change kCondCS to a more meaningful name, is the sense of // carry-set/clear flipped? GenRegRegCheck(kCondCs, rl_index.low_reg, reg_len, kThrowArrayBounds); FreeTemp(reg_len); } LoadBaseIndexed(reg_ptr, rl_index.low_reg, rl_result.low_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) { RegisterClass reg_class = oat_reg_class_by_size(size); int len_offset = mirror::Array::LengthOffset().Int32Value(); int data_offset; if (size == kLong || 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, kCoreReg); rl_index = LoadValue(rl_index, kCoreReg); int reg_ptr = INVALID_REG; if (IsTemp(rl_array.low_reg)) { Clobber(rl_array.low_reg); reg_ptr = rl_array.low_reg; } else { reg_ptr = AllocTemp(); OpRegCopy(reg_ptr, rl_array.low_reg); } /* null object? */ GenNullCheck(rl_array.s_reg_low, rl_array.low_reg, opt_flags); bool needs_range_check = (!(opt_flags & MIR_IGNORE_RANGE_CHECK)); int reg_len = INVALID_REG; if (needs_range_check) { reg_len = AllocTemp(); // NOTE: max live temps(4) here. /* Get len */ LoadWordDisp(rl_array.low_reg, len_offset, reg_len); } /* reg_ptr -> array data */ OpRegImm(kOpAdd, reg_ptr, data_offset); /* at this point, reg_ptr points to array, 2 live temps */ if ((size == kLong) || (size == kDouble)) { // TUNING: specific wide routine that can handle fp regs if (scale) { int r_new_index = AllocTemp(); OpRegRegImm(kOpLsl, r_new_index, rl_index.low_reg, scale); OpRegReg(kOpAdd, reg_ptr, r_new_index); FreeTemp(r_new_index); } else { OpRegReg(kOpAdd, reg_ptr, rl_index.low_reg); } rl_src = LoadValueWide(rl_src, reg_class); if (needs_range_check) { GenRegRegCheck(kCondCs, rl_index.low_reg, reg_len, kThrowArrayBounds); FreeTemp(reg_len); } StoreBaseDispWide(reg_ptr, 0, rl_src.low_reg, rl_src.high_reg); FreeTemp(reg_ptr); } else { rl_src = LoadValue(rl_src, reg_class); if (needs_range_check) { GenRegRegCheck(kCondCs, rl_index.low_reg, reg_len, kThrowArrayBounds); FreeTemp(reg_len); } StoreBaseIndexed(reg_ptr, rl_index.low_reg, rl_src.low_reg, scale, size); } } /* * Generate array store * */ void MipsMir2Lir::GenArrayObjPut(int opt_flags, RegLocation rl_array, RegLocation rl_index, RegLocation rl_src, int scale) { int len_offset = mirror::Array::LengthOffset().Int32Value(); int data_offset = mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value(); FlushAllRegs(); // Use explicit registers LockCallTemps(); int r_value = TargetReg(kArg0); // Register holding value int r_array_class = TargetReg(kArg1); // Register holding array's Class int r_array = TargetReg(kArg2); // Register holding array int r_index = TargetReg(kArg3); // Register holding index into array LoadValueDirectFixed(rl_array, r_array); // Grab array LoadValueDirectFixed(rl_src, r_value); // Grab value LoadValueDirectFixed(rl_index, r_index); // Grab index GenNullCheck(rl_array.s_reg_low, r_array, opt_flags); // NPE? // Store of null? LIR* null_value_check = OpCmpImmBranch(kCondEq, r_value, 0, NULL); // Get the array's class. LoadWordDisp(r_array, mirror::Object::ClassOffset().Int32Value(), r_array_class); CallRuntimeHelperRegReg(QUICK_ENTRYPOINT_OFFSET(pCanPutArrayElement), r_value, r_array_class, true); // Redo LoadValues in case they didn't survive the call. LoadValueDirectFixed(rl_array, r_array); // Reload array LoadValueDirectFixed(rl_index, r_index); // Reload index LoadValueDirectFixed(rl_src, r_value); // Reload value r_array_class = INVALID_REG; // Branch here if value to be stored == null LIR* target = NewLIR0(kPseudoTargetLabel); null_value_check->target = target; bool needs_range_check = (!(opt_flags & MIR_IGNORE_RANGE_CHECK)); int reg_len = INVALID_REG; if (needs_range_check) { reg_len = TargetReg(kArg1); LoadWordDisp(r_array, len_offset, reg_len); // Get len } /* r_ptr -> array data */ int r_ptr = AllocTemp(); OpRegRegImm(kOpAdd, r_ptr, r_array, data_offset); if (needs_range_check) { GenRegRegCheck(kCondCs, r_index, reg_len, kThrowArrayBounds); } StoreBaseIndexed(r_ptr, r_index, r_value, scale, kWord); FreeTemp(r_ptr); FreeTemp(r_index); if (!mir_graph_->IsConstantNullRef(rl_src)) { MarkGCCard(r_value, r_array); } } void MipsMir2Lir::GenShiftImmOpLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_shift) { // Default implementation is just to ignore the constant case. GenShiftOpLong(opcode, rl_dest, rl_src1, rl_shift); } void MipsMir2Lir::GenArithImmOpLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) { // Default - bail to non-const handler. GenArithOpLong(opcode, rl_dest, rl_src1, rl_src2); } } // namespace art