Fix and clean up intrinsic Math.abs(long).
On ARM, make sure we don't clobber a register that we still
need for the next insn. On x86, don't free a source register
for temps if it's overlapping a result register as that
would allow the sign_reg temporary to alias with the result.
Change-Id: I785f2f607900ae8ceb2fa0b8f3eefafdf6fab5c7
diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc
index e3dc554..f3c5a34 100644
--- a/compiler/dex/quick/gen_invoke.cc
+++ b/compiler/dex/quick/gen_invoke.cc
@@ -1163,40 +1163,35 @@
// TODO - add Mips implementation
return false;
}
- if (cu_->instruction_set == kThumb2) {
- RegLocation rl_src = info->args[0];
- rl_src = LoadValueWide(rl_src, kCoreReg);
- RegLocation rl_dest = InlineTargetWide(info);
- RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
- int sign_reg = AllocTemp();
- // abs(x) = y<=x>>31, (x+y)^y.
- OpRegRegImm(kOpAsr, sign_reg, rl_src.reg.GetHighReg(), 31);
- OpRegRegReg(kOpAdd, rl_result.reg.GetReg(), rl_src.reg.GetReg(), sign_reg);
- OpRegRegReg(kOpAdc, rl_result.reg.GetHighReg(), rl_src.reg.GetHighReg(), sign_reg);
- OpRegReg(kOpXor, rl_result.reg.GetReg(), sign_reg);
- OpRegReg(kOpXor, rl_result.reg.GetHighReg(), sign_reg);
- StoreValueWide(rl_dest, rl_result);
- return true;
- } else {
- DCHECK_EQ(cu_->instruction_set, kX86);
- // Reuse source registers to avoid running out of temps
- RegLocation rl_src = info->args[0];
- rl_src = LoadValueWide(rl_src, kCoreReg);
- RegLocation rl_dest = InlineTargetWide(info);
- RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
- OpRegCopyWide(rl_result.reg.GetReg(), rl_result.reg.GetHighReg(), rl_src.reg.GetReg(), rl_src.reg.GetHighReg());
- FreeTemp(rl_src.reg.GetReg());
- FreeTemp(rl_src.reg.GetHighReg());
- int sign_reg = AllocTemp();
- // abs(x) = y<=x>>31, (x+y)^y.
- OpRegRegImm(kOpAsr, sign_reg, rl_result.reg.GetHighReg(), 31);
- OpRegReg(kOpAdd, rl_result.reg.GetReg(), sign_reg);
- OpRegReg(kOpAdc, rl_result.reg.GetHighReg(), sign_reg);
- OpRegReg(kOpXor, rl_result.reg.GetReg(), sign_reg);
- OpRegReg(kOpXor, rl_result.reg.GetHighReg(), sign_reg);
- StoreValueWide(rl_dest, rl_result);
- return true;
+ RegLocation rl_src = info->args[0];
+ rl_src = LoadValueWide(rl_src, kCoreReg);
+ RegLocation rl_dest = InlineTargetWide(info);
+ RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
+
+ // If on x86 or if we would clobber a register needed later, just copy the source first.
+ if (cu_->instruction_set == kX86 || rl_result.reg.GetReg() == rl_src.reg.GetHighReg()) {
+ OpRegCopyWide(rl_result.reg.GetReg(), rl_result.reg.GetHighReg(),
+ rl_src.reg.GetReg(), rl_src.reg.GetHighReg());
+ if (rl_result.reg.GetReg() != rl_src.reg.GetReg() &&
+ rl_result.reg.GetReg() != rl_src.reg.GetHighReg() &&
+ rl_result.reg.GetHighReg() != rl_src.reg.GetReg() &&
+ rl_result.reg.GetHighReg() != rl_src.reg.GetHighReg()) {
+ // Reuse source registers to avoid running out of temps.
+ FreeTemp(rl_src.reg.GetReg());
+ FreeTemp(rl_src.reg.GetHighReg());
+ }
+ rl_src = rl_result;
}
+
+ // abs(x) = y<=x>>31, (x+y)^y.
+ int sign_reg = AllocTemp();
+ OpRegRegImm(kOpAsr, sign_reg, rl_src.reg.GetHighReg(), 31);
+ OpRegRegReg(kOpAdd, rl_result.reg.GetReg(), rl_src.reg.GetReg(), sign_reg);
+ OpRegRegReg(kOpAdc, rl_result.reg.GetHighReg(), rl_src.reg.GetHighReg(), sign_reg);
+ OpRegReg(kOpXor, rl_result.reg.GetReg(), sign_reg);
+ OpRegReg(kOpXor, rl_result.reg.GetHighReg(), sign_reg);
+ StoreValueWide(rl_dest, rl_result);
+ return true;
}
bool Mir2Lir::GenInlinedAbsFloat(CallInfo* info) {