Implement CFI for Quick.
CFI is necessary for stack unwinding in gdb, lldb, and libunwind.
Change-Id: Ic3b84c9dc91c4bae80e27cda02190f3274e95ae8
diff --git a/compiler/dex/quick/arm/call_arm.cc b/compiler/dex/quick/arm/call_arm.cc
index 518e3ea..3d18af6 100644
--- a/compiler/dex/quick/arm/call_arm.cc
+++ b/compiler/dex/quick/arm/call_arm.cc
@@ -355,7 +355,16 @@
FreeTemp(reg_card_no);
}
+static dwarf::Reg DwarfCoreReg(int num) {
+ return dwarf::Reg::ArmCore(num);
+}
+
+static dwarf::Reg DwarfFpReg(int num) {
+ return dwarf::Reg::ArmFp(num);
+}
+
void ArmMir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) {
+ DCHECK_EQ(cfi_.GetCurrentCFAOffset(), 0); // empty stack.
int spill_count = num_core_spills_ + num_fp_spills_;
/*
* On entry, r0, r1, r2 & r3 are live. Let the register allocation
@@ -403,28 +412,32 @@
}
}
/* Spill core callee saves */
- if (core_spill_mask_ == 0u) {
- // Nothing to spill.
- } else if ((core_spill_mask_ & ~(0xffu | (1u << rs_rARM_LR.GetRegNum()))) == 0u) {
- // Spilling only low regs and/or LR, use 16-bit PUSH.
- constexpr int lr_bit_shift = rs_rARM_LR.GetRegNum() - 8;
- NewLIR1(kThumbPush,
- (core_spill_mask_ & ~(1u << rs_rARM_LR.GetRegNum())) |
- ((core_spill_mask_ & (1u << rs_rARM_LR.GetRegNum())) >> lr_bit_shift));
- } else if (IsPowerOfTwo(core_spill_mask_)) {
- // kThumb2Push cannot be used to spill a single register.
- NewLIR1(kThumb2Push1, CTZ(core_spill_mask_));
- } else {
- NewLIR1(kThumb2Push, core_spill_mask_);
+ if (core_spill_mask_ != 0u) {
+ if ((core_spill_mask_ & ~(0xffu | (1u << rs_rARM_LR.GetRegNum()))) == 0u) {
+ // Spilling only low regs and/or LR, use 16-bit PUSH.
+ constexpr int lr_bit_shift = rs_rARM_LR.GetRegNum() - 8;
+ NewLIR1(kThumbPush,
+ (core_spill_mask_ & ~(1u << rs_rARM_LR.GetRegNum())) |
+ ((core_spill_mask_ & (1u << rs_rARM_LR.GetRegNum())) >> lr_bit_shift));
+ } else if (IsPowerOfTwo(core_spill_mask_)) {
+ // kThumb2Push cannot be used to spill a single register.
+ NewLIR1(kThumb2Push1, CTZ(core_spill_mask_));
+ } else {
+ NewLIR1(kThumb2Push, core_spill_mask_);
+ }
+ cfi_.AdjustCFAOffset(num_core_spills_ * kArmPointerSize);
+ cfi_.RelOffsetForMany(DwarfCoreReg(0), 0, core_spill_mask_, kArmPointerSize);
}
/* Need to spill any FP regs? */
- if (num_fp_spills_) {
+ if (num_fp_spills_ != 0u) {
/*
* NOTE: fp spills are a little different from core spills in that
* they are pushed as a contiguous block. When promoting from
* the fp set, we must allocate all singles from s16..highest-promoted
*/
NewLIR1(kThumb2VPushCS, num_fp_spills_);
+ cfi_.AdjustCFAOffset(num_fp_spills_ * kArmPointerSize);
+ cfi_.RelOffsetForMany(DwarfFpReg(0), 0, fp_spill_mask_, kArmPointerSize);
}
const int spill_size = spill_count * 4;
@@ -445,12 +458,14 @@
m2l_->LoadWordDisp(rs_rARM_SP, sp_displace_ - 4, rs_rARM_LR);
}
m2l_->OpRegImm(kOpAdd, rs_rARM_SP, sp_displace_);
+ m2l_->cfi().AdjustCFAOffset(-sp_displace_);
m2l_->ClobberCallerSave();
ThreadOffset<4> func_offset = QUICK_ENTRYPOINT_OFFSET(4, pThrowStackOverflow);
// Load the entrypoint directly into the pc instead of doing a load + branch. Assumes
// codegen and target are in thumb2 mode.
// NOTE: native pointer.
m2l_->LoadWordDisp(rs_rARM_SELF, func_offset.Int32Value(), rs_rARM_PC);
+ m2l_->cfi().AdjustCFAOffset(sp_displace_);
}
private:
@@ -465,6 +480,7 @@
// Need to restore LR since we used it as a temp.
AddSlowPath(new(arena_)StackOverflowSlowPath(this, branch, true, spill_size));
OpRegCopy(rs_rARM_SP, rs_rARM_LR); // Establish stack
+ cfi_.AdjustCFAOffset(frame_size_without_spills);
} else {
/*
* If the frame is small enough we are guaranteed to have enough space that remains to
@@ -475,6 +491,7 @@
MarkTemp(rs_rARM_LR);
FreeTemp(rs_rARM_LR);
OpRegRegImm(kOpSub, rs_rARM_SP, rs_rARM_SP, frame_size_without_spills);
+ cfi_.AdjustCFAOffset(frame_size_without_spills);
Clobber(rs_rARM_LR);
UnmarkTemp(rs_rARM_LR);
LIR* branch = OpCmpBranch(kCondUlt, rs_rARM_SP, rs_r12, nullptr);
@@ -484,9 +501,11 @@
// Implicit stack overflow check has already been done. Just make room on the
// stack for the frame now.
OpRegImm(kOpSub, rs_rARM_SP, frame_size_without_spills);
+ cfi_.AdjustCFAOffset(frame_size_without_spills);
}
} else {
OpRegImm(kOpSub, rs_rARM_SP, frame_size_without_spills);
+ cfi_.AdjustCFAOffset(frame_size_without_spills);
}
FlushIns(ArgLocs, rl_method);
@@ -507,7 +526,9 @@
}
void ArmMir2Lir::GenExitSequence() {
+ cfi_.RememberState();
int spill_count = num_core_spills_ + num_fp_spills_;
+
/*
* In the exit path, r0/r1 are live - make sure they aren't
* allocated by the register utilities as temps.
@@ -515,34 +536,47 @@
LockTemp(rs_r0);
LockTemp(rs_r1);
- OpRegImm(kOpAdd, rs_rARM_SP, frame_size_ - (spill_count * 4));
+ int adjust = frame_size_ - (spill_count * kArmPointerSize);
+ OpRegImm(kOpAdd, rs_rARM_SP, adjust);
+ cfi_.AdjustCFAOffset(-adjust);
/* Need to restore any FP callee saves? */
if (num_fp_spills_) {
NewLIR1(kThumb2VPopCS, num_fp_spills_);
+ cfi_.AdjustCFAOffset(-num_fp_spills_ * kArmPointerSize);
+ cfi_.RestoreMany(DwarfFpReg(0), fp_spill_mask_);
}
- if ((core_spill_mask_ & (1 << rs_rARM_LR.GetRegNum())) != 0) {
- /* Unspill rARM_LR to rARM_PC */
+ bool unspill_LR_to_PC = (core_spill_mask_ & (1 << rs_rARM_LR.GetRegNum())) != 0;
+ if (unspill_LR_to_PC) {
core_spill_mask_ &= ~(1 << rs_rARM_LR.GetRegNum());
core_spill_mask_ |= (1 << rs_rARM_PC.GetRegNum());
}
- if (core_spill_mask_ == 0u) {
- // Nothing to unspill.
- } else if ((core_spill_mask_ & ~(0xffu | (1u << rs_rARM_PC.GetRegNum()))) == 0u) {
- // Unspilling only low regs and/or PC, use 16-bit POP.
- constexpr int pc_bit_shift = rs_rARM_PC.GetRegNum() - 8;
- NewLIR1(kThumbPop,
- (core_spill_mask_ & ~(1u << rs_rARM_PC.GetRegNum())) |
- ((core_spill_mask_ & (1u << rs_rARM_PC.GetRegNum())) >> pc_bit_shift));
- } else if (IsPowerOfTwo(core_spill_mask_)) {
- // kThumb2Pop cannot be used to unspill a single register.
- NewLIR1(kThumb2Pop1, CTZ(core_spill_mask_));
- } else {
- NewLIR1(kThumb2Pop, core_spill_mask_);
+ if (core_spill_mask_ != 0u) {
+ if ((core_spill_mask_ & ~(0xffu | (1u << rs_rARM_PC.GetRegNum()))) == 0u) {
+ // Unspilling only low regs and/or PC, use 16-bit POP.
+ constexpr int pc_bit_shift = rs_rARM_PC.GetRegNum() - 8;
+ NewLIR1(kThumbPop,
+ (core_spill_mask_ & ~(1u << rs_rARM_PC.GetRegNum())) |
+ ((core_spill_mask_ & (1u << rs_rARM_PC.GetRegNum())) >> pc_bit_shift));
+ } else if (IsPowerOfTwo(core_spill_mask_)) {
+ // kThumb2Pop cannot be used to unspill a single register.
+ NewLIR1(kThumb2Pop1, CTZ(core_spill_mask_));
+ } else {
+ NewLIR1(kThumb2Pop, core_spill_mask_);
+ }
+ // If we pop to PC, there is no further epilogue code.
+ if (!unspill_LR_to_PC) {
+ cfi_.AdjustCFAOffset(-num_core_spills_ * kArmPointerSize);
+ cfi_.RestoreMany(DwarfCoreReg(0), core_spill_mask_);
+ DCHECK_EQ(cfi_.GetCurrentCFAOffset(), 0); // empty stack.
+ }
}
- if ((core_spill_mask_ & (1 << rs_rARM_PC.GetRegNum())) == 0) {
+ if (!unspill_LR_to_PC) {
/* We didn't pop to rARM_PC, so must do a bv rARM_LR */
NewLIR1(kThumbBx, rs_rARM_LR.GetReg());
}
+ // The CFI should be restored for any code that follows the exit block.
+ cfi_.RestoreState();
+ cfi_.DefCFAOffset(frame_size_);
}
void ArmMir2Lir::GenSpecialExitSequence() {
@@ -564,11 +598,16 @@
NewLIR1(kThumbPush, (1u << rs_r0.GetRegNum()) | // ArtMethod*
(core_spill_mask_ & ~(1u << rs_rARM_LR.GetRegNum())) | // Spills other than LR.
(1u << 8)); // LR encoded for 16-bit push.
+ cfi_.AdjustCFAOffset(frame_size_);
+ // Do not generate CFI for scratch register r0.
+ cfi_.RelOffsetForMany(DwarfCoreReg(0), 4, core_spill_mask_, kArmPointerSize);
}
void ArmMir2Lir::GenSpecialExitForSuspend() {
// Pop the frame. (ArtMethod* no longer needed but restore it anyway.)
NewLIR1(kThumb2Pop, (1u << rs_r0.GetRegNum()) | core_spill_mask_); // 32-bit because of LR.
+ cfi_.AdjustCFAOffset(-frame_size_);
+ cfi_.RestoreMany(DwarfCoreReg(0), core_spill_mask_);
}
static bool ArmUseRelativeCall(CompilationUnit* cu, const MethodReference& target_method) {