Implement CFI for JNI.
CFI is necessary for stack unwinding in gdb, lldb, and libunwind.
Change-Id: I37eb7973f99a6975034cf0e699e138c3a9aba10f
diff --git a/compiler/utils/arm64/assembler_arm64.cc b/compiler/utils/arm64/assembler_arm64.cc
index 58c7367..fbd0411 100644
--- a/compiler/utils/arm64/assembler_arm64.cc
+++ b/compiler/utils/arm64/assembler_arm64.cc
@@ -63,12 +63,14 @@
void Arm64Assembler::IncreaseFrameSize(size_t adjust) {
CHECK_ALIGNED(adjust, kStackAlignment);
AddConstant(SP, -adjust);
+ cfi().AdjustCFAOffset(adjust);
}
// See Arm64 PCS Section 5.2.2.1.
void Arm64Assembler::DecreaseFrameSize(size_t adjust) {
CHECK_ALIGNED(adjust, kStackAlignment);
AddConstant(SP, adjust);
+ cfi().AdjustCFAOffset(-adjust);
}
void Arm64Assembler::AddConstant(XRegister rd, int32_t value, Condition cond) {
@@ -638,6 +640,14 @@
___ Brk();
}
+static dwarf::Reg DWARFReg(XRegister reg) {
+ return dwarf::Reg::Arm64Core(static_cast<int>(reg));
+}
+
+static dwarf::Reg DWARFReg(DRegister reg) {
+ return dwarf::Reg::Arm64Fp(static_cast<int>(reg));
+}
+
constexpr size_t kFramePointerSize = 8;
constexpr unsigned int kJniRefSpillRegsSize = 11 + 8;
@@ -660,45 +670,20 @@
// TUNING: Use stp.
// Note: Must match Arm64JniCallingConvention::CoreSpillMask().
size_t reg_offset = frame_size;
- reg_offset -= 8;
- StoreToOffset(LR, SP, reg_offset);
- reg_offset -= 8;
- StoreToOffset(X29, SP, reg_offset);
- reg_offset -= 8;
- StoreToOffset(X28, SP, reg_offset);
- reg_offset -= 8;
- StoreToOffset(X27, SP, reg_offset);
- reg_offset -= 8;
- StoreToOffset(X26, SP, reg_offset);
- reg_offset -= 8;
- StoreToOffset(X25, SP, reg_offset);
- reg_offset -= 8;
- StoreToOffset(X24, SP, reg_offset);
- reg_offset -= 8;
- StoreToOffset(X23, SP, reg_offset);
- reg_offset -= 8;
- StoreToOffset(X22, SP, reg_offset);
- reg_offset -= 8;
- StoreToOffset(X21, SP, reg_offset);
- reg_offset -= 8;
- StoreToOffset(X20, SP, reg_offset);
-
- reg_offset -= 8;
- StoreDToOffset(D15, SP, reg_offset);
- reg_offset -= 8;
- StoreDToOffset(D14, SP, reg_offset);
- reg_offset -= 8;
- StoreDToOffset(D13, SP, reg_offset);
- reg_offset -= 8;
- StoreDToOffset(D12, SP, reg_offset);
- reg_offset -= 8;
- StoreDToOffset(D11, SP, reg_offset);
- reg_offset -= 8;
- StoreDToOffset(D10, SP, reg_offset);
- reg_offset -= 8;
- StoreDToOffset(D9, SP, reg_offset);
- reg_offset -= 8;
- StoreDToOffset(D8, SP, reg_offset);
+ static constexpr XRegister x_spills[] = {
+ LR, X29, X28, X27, X26, X25, X24, X23, X22, X21, X20 };
+ for (size_t i = 0; i < arraysize(x_spills); i++) {
+ XRegister reg = x_spills[i];
+ reg_offset -= 8;
+ StoreToOffset(reg, SP, reg_offset);
+ cfi_.RelOffset(DWARFReg(reg), reg_offset);
+ }
+ for (int d = 15; d >= 8; d--) {
+ DRegister reg = static_cast<DRegister>(d);
+ reg_offset -= 8;
+ StoreDToOffset(reg, SP, reg_offset);
+ cfi_.RelOffset(DWARFReg(reg), reg_offset);
+ }
// Move TR(Caller saved) to ETR(Callee saved). The original (ETR)X21 has been saved on stack.
// This way we make sure that TR is not trashed by native code.
@@ -734,6 +719,7 @@
void Arm64Assembler::RemoveFrame(size_t frame_size, const std::vector<ManagedRegister>& callee_save_regs) {
CHECK_ALIGNED(frame_size, kStackAlignment);
+ cfi_.RememberState();
// For now we only check that the size of the frame is greater than the spill size.
CHECK_EQ(callee_save_regs.size(), kJniRefSpillRegsSize);
@@ -748,51 +734,30 @@
// TUNING: Use ldp.
// Note: Must match Arm64JniCallingConvention::CoreSpillMask().
size_t reg_offset = frame_size;
- reg_offset -= 8;
- LoadFromOffset(LR, SP, reg_offset);
- reg_offset -= 8;
- LoadFromOffset(X29, SP, reg_offset);
- reg_offset -= 8;
- LoadFromOffset(X28, SP, reg_offset);
- reg_offset -= 8;
- LoadFromOffset(X27, SP, reg_offset);
- reg_offset -= 8;
- LoadFromOffset(X26, SP, reg_offset);
- reg_offset -= 8;
- LoadFromOffset(X25, SP, reg_offset);
- reg_offset -= 8;
- LoadFromOffset(X24, SP, reg_offset);
- reg_offset -= 8;
- LoadFromOffset(X23, SP, reg_offset);
- reg_offset -= 8;
- LoadFromOffset(X22, SP, reg_offset);
- reg_offset -= 8;
- LoadFromOffset(X21, SP, reg_offset);
- reg_offset -= 8;
- LoadFromOffset(X20, SP, reg_offset);
-
- reg_offset -= 8;
- LoadDFromOffset(D15, SP, reg_offset);
- reg_offset -= 8;
- LoadDFromOffset(D14, SP, reg_offset);
- reg_offset -= 8;
- LoadDFromOffset(D13, SP, reg_offset);
- reg_offset -= 8;
- LoadDFromOffset(D12, SP, reg_offset);
- reg_offset -= 8;
- LoadDFromOffset(D11, SP, reg_offset);
- reg_offset -= 8;
- LoadDFromOffset(D10, SP, reg_offset);
- reg_offset -= 8;
- LoadDFromOffset(D9, SP, reg_offset);
- reg_offset -= 8;
- LoadDFromOffset(D8, SP, reg_offset);
+ static constexpr XRegister x_spills[] = {
+ LR, X29, X28, X27, X26, X25, X24, X23, X22, X21, X20 };
+ for (size_t i = 0; i < arraysize(x_spills); i++) {
+ XRegister reg = x_spills[i];
+ reg_offset -= 8;
+ LoadFromOffset(reg, SP, reg_offset);
+ cfi_.Restore(DWARFReg(reg));
+ }
+ for (int d = 15; d >= 8; d--) {
+ DRegister reg = static_cast<DRegister>(d);
+ reg_offset -= 8;
+ LoadDFromOffset(reg, SP, reg_offset);
+ cfi_.Restore(DWARFReg(reg));
+ }
// Decrease frame size to start of callee saved regs.
DecreaseFrameSize(frame_size);
// Pop callee saved and return to LR.
___ Ret();
+
+ // The CFI should be restored for any code that follows the exit block.
+ cfi_.RestoreState();
+ cfi_.DefCFAOffset(frame_size);
}
} // namespace arm64