blob: 7c8214b92736e3a54dd704895d9476805d4baa67 [file] [log] [blame]
/*
* 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(int 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::SmallLiteralDivide(Instruction::Code dalvik_opcode,
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(ENTRYPOINT_OFFSET(pCanPutArrayElementFromCode), 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