Fix store instructions to large frames in ARM opt. compiler.
When accessing a stack frame at a large offset, use an
additional core register (R5 or R6) as a temporary register
whenever IP contains the value to store (and thus cannot be
used by art::Thumb2Assembler::StoreToOffset as a temporary
register to compute the memory address where the value is
to be stored). The previous value of R5 (or R6) is saved
on the stack before the emission of the store instruction
and restored afterwards.
Change-Id: Ic5fd5ab2c09d8327dd1f0f241d40d2c397ce64cd
diff --git a/compiler/utils/arm/assembler_thumb2.cc b/compiler/utils/arm/assembler_thumb2.cc
index fd2613a..9801108 100644
--- a/compiler/utils/arm/assembler_thumb2.cc
+++ b/compiler/utils/arm/assembler_thumb2.cc
@@ -2391,7 +2391,7 @@
int32_t offset,
Condition cond) {
if (!Address::CanHoldLoadOffsetThumb(type, offset)) {
- CHECK(base != IP);
+ CHECK_NE(base, IP);
LoadImmediate(IP, offset, cond);
add(IP, IP, ShifterOperand(base), cond);
base = IP;
@@ -2467,12 +2467,26 @@
Register base,
int32_t offset,
Condition cond) {
+ Register tmp_reg = kNoRegister;
if (!Address::CanHoldStoreOffsetThumb(type, offset)) {
- CHECK(reg != IP);
- CHECK(base != IP);
- LoadImmediate(IP, offset, cond);
- add(IP, IP, ShifterOperand(base), cond);
- base = IP;
+ CHECK_NE(base, IP);
+ if (reg != IP) {
+ tmp_reg = IP;
+ } else {
+ // Be careful not to use IP twice (for `reg` and to build the
+ // Address object used by the store instruction(s) below).
+ // Instead, save R5 on the stack (or R6 if R5 is not available),
+ // use it as secondary temporary register, and restore it after
+ // the store instruction has been emitted.
+ tmp_reg = base != R5 ? R5 : R6;
+ Push(tmp_reg);
+ if (base == SP) {
+ offset += kRegisterSize;
+ }
+ }
+ LoadImmediate(tmp_reg, offset, cond);
+ add(tmp_reg, tmp_reg, ShifterOperand(base), cond);
+ base = tmp_reg;
offset = 0;
}
CHECK(Address::CanHoldStoreOffsetThumb(type, offset));
@@ -2493,6 +2507,10 @@
LOG(FATAL) << "UNREACHABLE";
UNREACHABLE();
}
+ if (tmp_reg != kNoRegister && tmp_reg != IP) {
+ DCHECK(tmp_reg == R5 || tmp_reg == R6);
+ Pop(tmp_reg);
+ }
}