Implement CFI for Optimizing.
CFI is necessary for stack unwinding in gdb, lldb, and libunwind.
Change-Id: I1a3480e3a4a99f48bf7e6e63c4e83a80cfee40a2
diff --git a/compiler/dwarf/debug_frame_opcode_writer.h b/compiler/dwarf/debug_frame_opcode_writer.h
index 85186bb..d0d1821 100644
--- a/compiler/dwarf/debug_frame_opcode_writer.h
+++ b/compiler/dwarf/debug_frame_opcode_writer.h
@@ -20,6 +20,7 @@
#include "dwarf.h"
#include "register.h"
#include "writer.h"
+#include "utils.h"
namespace art {
namespace dwarf {
@@ -41,45 +42,51 @@
static constexpr int kCodeAlignmentFactor = 1;
// Explicitely advance the program counter to given location.
- void AdvancePC(int absolute_pc) {
+ void ALWAYS_INLINE AdvancePC(int absolute_pc) {
DCHECK_GE(absolute_pc, current_pc_);
- int delta = FactorCodeOffset(absolute_pc - current_pc_);
- if (delta != 0) {
- if (delta <= 0x3F) {
- this->PushUint8(DW_CFA_advance_loc | delta);
- } else if (delta <= UINT8_MAX) {
- this->PushUint8(DW_CFA_advance_loc1);
- this->PushUint8(delta);
- } else if (delta <= UINT16_MAX) {
- this->PushUint8(DW_CFA_advance_loc2);
- this->PushUint16(delta);
- } else {
- this->PushUint8(DW_CFA_advance_loc4);
- this->PushUint32(delta);
+ if (UNLIKELY(enabled_)) {
+ int delta = FactorCodeOffset(absolute_pc - current_pc_);
+ if (delta != 0) {
+ if (delta <= 0x3F) {
+ this->PushUint8(DW_CFA_advance_loc | delta);
+ } else if (delta <= UINT8_MAX) {
+ this->PushUint8(DW_CFA_advance_loc1);
+ this->PushUint8(delta);
+ } else if (delta <= UINT16_MAX) {
+ this->PushUint8(DW_CFA_advance_loc2);
+ this->PushUint16(delta);
+ } else {
+ this->PushUint8(DW_CFA_advance_loc4);
+ this->PushUint32(delta);
+ }
}
+ current_pc_ = absolute_pc;
}
- current_pc_ = absolute_pc;
}
// Override this method to automatically advance the PC before each opcode.
virtual void ImplicitlyAdvancePC() { }
// Common alias in assemblers - spill relative to current stack pointer.
- void RelOffset(Reg reg, int offset) {
+ void ALWAYS_INLINE RelOffset(Reg reg, int offset) {
Offset(reg, offset - current_cfa_offset_);
}
// Common alias in assemblers - increase stack frame size.
- void AdjustCFAOffset(int delta) {
+ void ALWAYS_INLINE AdjustCFAOffset(int delta) {
DefCFAOffset(current_cfa_offset_ + delta);
}
// Custom alias - spill many registers based on bitmask.
- void RelOffsetForMany(Reg reg_base, int offset, uint32_t reg_mask,
- int reg_size) {
+ void ALWAYS_INLINE RelOffsetForMany(Reg reg_base, int offset,
+ uint32_t reg_mask, int reg_size) {
DCHECK(reg_size == 4 || reg_size == 8);
- for (int i = 0; reg_mask != 0u; reg_mask >>= 1, i++) {
- if ((reg_mask & 1) != 0u) {
+ if (UNLIKELY(enabled_)) {
+ for (int i = 0; reg_mask != 0u; reg_mask >>= 1, i++) {
+ // Skip zero bits and go to the set bit.
+ int num_zeros = CTZ(reg_mask);
+ i += num_zeros;
+ reg_mask >>= num_zeros;
RelOffset(Reg(reg_base.num() + i), offset);
offset += reg_size;
}
@@ -87,175 +94,214 @@
}
// Custom alias - unspill many registers based on bitmask.
- void RestoreMany(Reg reg_base, uint32_t reg_mask) {
- for (int i = 0; reg_mask != 0u; reg_mask >>= 1, i++) {
- if ((reg_mask & 1) != 0u) {
+ void ALWAYS_INLINE RestoreMany(Reg reg_base, uint32_t reg_mask) {
+ if (UNLIKELY(enabled_)) {
+ for (int i = 0; reg_mask != 0u; reg_mask >>= 1, i++) {
+ // Skip zero bits and go to the set bit.
+ int num_zeros = CTZ(reg_mask);
+ i += num_zeros;
+ reg_mask >>= num_zeros;
Restore(Reg(reg_base.num() + i));
}
}
}
- void Nop() {
- this->PushUint8(DW_CFA_nop);
+ void ALWAYS_INLINE Nop() {
+ if (UNLIKELY(enabled_)) {
+ this->PushUint8(DW_CFA_nop);
+ }
}
- void Offset(Reg reg, int offset) {
- ImplicitlyAdvancePC();
- int factored_offset = FactorDataOffset(offset); // May change sign.
- if (factored_offset >= 0) {
- if (0 <= reg.num() && reg.num() <= 0x3F) {
- this->PushUint8(DW_CFA_offset | reg.num());
- this->PushUleb128(factored_offset);
+ void ALWAYS_INLINE Offset(Reg reg, int offset) {
+ if (UNLIKELY(enabled_)) {
+ ImplicitlyAdvancePC();
+ int factored_offset = FactorDataOffset(offset); // May change sign.
+ if (factored_offset >= 0) {
+ if (0 <= reg.num() && reg.num() <= 0x3F) {
+ this->PushUint8(DW_CFA_offset | reg.num());
+ this->PushUleb128(factored_offset);
+ } else {
+ this->PushUint8(DW_CFA_offset_extended);
+ this->PushUleb128(reg.num());
+ this->PushUleb128(factored_offset);
+ }
} else {
- this->PushUint8(DW_CFA_offset_extended);
+ uses_dwarf3_features_ = true;
+ this->PushUint8(DW_CFA_offset_extended_sf);
this->PushUleb128(reg.num());
- this->PushUleb128(factored_offset);
+ this->PushSleb128(factored_offset);
}
- } else {
- uses_dwarf3_features_ = true;
- this->PushUint8(DW_CFA_offset_extended_sf);
- this->PushUleb128(reg.num());
- this->PushSleb128(factored_offset);
}
}
- void Restore(Reg reg) {
- ImplicitlyAdvancePC();
- if (0 <= reg.num() && reg.num() <= 0x3F) {
- this->PushUint8(DW_CFA_restore | reg.num());
- } else {
- this->PushUint8(DW_CFA_restore_extended);
+ void ALWAYS_INLINE Restore(Reg reg) {
+ if (UNLIKELY(enabled_)) {
+ ImplicitlyAdvancePC();
+ if (0 <= reg.num() && reg.num() <= 0x3F) {
+ this->PushUint8(DW_CFA_restore | reg.num());
+ } else {
+ this->PushUint8(DW_CFA_restore_extended);
+ this->PushUleb128(reg.num());
+ }
+ }
+ }
+
+ void ALWAYS_INLINE Undefined(Reg reg) {
+ if (UNLIKELY(enabled_)) {
+ ImplicitlyAdvancePC();
+ this->PushUint8(DW_CFA_undefined);
this->PushUleb128(reg.num());
}
}
- void Undefined(Reg reg) {
- ImplicitlyAdvancePC();
- this->PushUint8(DW_CFA_undefined);
- this->PushUleb128(reg.num());
- }
-
- void SameValue(Reg reg) {
- ImplicitlyAdvancePC();
- this->PushUint8(DW_CFA_same_value);
- this->PushUleb128(reg.num());
+ void ALWAYS_INLINE SameValue(Reg reg) {
+ if (UNLIKELY(enabled_)) {
+ ImplicitlyAdvancePC();
+ this->PushUint8(DW_CFA_same_value);
+ this->PushUleb128(reg.num());
+ }
}
// The previous value of "reg" is stored in register "new_reg".
- void Register(Reg reg, Reg new_reg) {
- ImplicitlyAdvancePC();
- this->PushUint8(DW_CFA_register);
- this->PushUleb128(reg.num());
- this->PushUleb128(new_reg.num());
- }
-
- void RememberState() {
- ImplicitlyAdvancePC();
- this->PushUint8(DW_CFA_remember_state);
- }
-
- void RestoreState() {
- ImplicitlyAdvancePC();
- this->PushUint8(DW_CFA_restore_state);
- }
-
- void DefCFA(Reg reg, int offset) {
- ImplicitlyAdvancePC();
- if (offset >= 0) {
- this->PushUint8(DW_CFA_def_cfa);
+ void ALWAYS_INLINE Register(Reg reg, Reg new_reg) {
+ if (UNLIKELY(enabled_)) {
+ ImplicitlyAdvancePC();
+ this->PushUint8(DW_CFA_register);
this->PushUleb128(reg.num());
- this->PushUleb128(offset); // Non-factored.
- } else {
- uses_dwarf3_features_ = true;
- this->PushUint8(DW_CFA_def_cfa_sf);
- this->PushUleb128(reg.num());
- this->PushSleb128(FactorDataOffset(offset));
+ this->PushUleb128(new_reg.num());
}
- current_cfa_offset_ = offset;
}
- void DefCFARegister(Reg reg) {
- ImplicitlyAdvancePC();
- this->PushUint8(DW_CFA_def_cfa_register);
- this->PushUleb128(reg.num());
+ void ALWAYS_INLINE RememberState() {
+ if (UNLIKELY(enabled_)) {
+ ImplicitlyAdvancePC();
+ this->PushUint8(DW_CFA_remember_state);
+ }
}
- void DefCFAOffset(int offset) {
- if (current_cfa_offset_ != offset) {
+ void ALWAYS_INLINE RestoreState() {
+ if (UNLIKELY(enabled_)) {
+ ImplicitlyAdvancePC();
+ this->PushUint8(DW_CFA_restore_state);
+ }
+ }
+
+ void ALWAYS_INLINE DefCFA(Reg reg, int offset) {
+ if (UNLIKELY(enabled_)) {
ImplicitlyAdvancePC();
if (offset >= 0) {
- this->PushUint8(DW_CFA_def_cfa_offset);
+ this->PushUint8(DW_CFA_def_cfa);
+ this->PushUleb128(reg.num());
this->PushUleb128(offset); // Non-factored.
} else {
uses_dwarf3_features_ = true;
- this->PushUint8(DW_CFA_def_cfa_offset_sf);
+ this->PushUint8(DW_CFA_def_cfa_sf);
+ this->PushUleb128(reg.num());
this->PushSleb128(FactorDataOffset(offset));
}
- current_cfa_offset_ = offset;
}
- }
-
- void ValOffset(Reg reg, int offset) {
- ImplicitlyAdvancePC();
- uses_dwarf3_features_ = true;
- int factored_offset = FactorDataOffset(offset); // May change sign.
- if (factored_offset >= 0) {
- this->PushUint8(DW_CFA_val_offset);
- this->PushUleb128(reg.num());
- this->PushUleb128(factored_offset);
- } else {
- this->PushUint8(DW_CFA_val_offset_sf);
- this->PushUleb128(reg.num());
- this->PushSleb128(factored_offset);
- }
- }
-
- void DefCFAExpression(void* expr, int expr_size) {
- ImplicitlyAdvancePC();
- uses_dwarf3_features_ = true;
- this->PushUint8(DW_CFA_def_cfa_expression);
- this->PushUleb128(expr_size);
- this->PushData(expr, expr_size);
- }
-
- void Expression(Reg reg, void* expr, int expr_size) {
- ImplicitlyAdvancePC();
- uses_dwarf3_features_ = true;
- this->PushUint8(DW_CFA_expression);
- this->PushUleb128(reg.num());
- this->PushUleb128(expr_size);
- this->PushData(expr, expr_size);
- }
-
- void ValExpression(Reg reg, void* expr, int expr_size) {
- ImplicitlyAdvancePC();
- uses_dwarf3_features_ = true;
- this->PushUint8(DW_CFA_val_expression);
- this->PushUleb128(reg.num());
- this->PushUleb128(expr_size);
- this->PushData(expr, expr_size);
- }
-
- int GetCurrentPC() const {
- return current_pc_;
- }
-
- int GetCurrentCFAOffset() const {
- return current_cfa_offset_;
- }
-
- void SetCurrentCFAOffset(int offset) {
current_cfa_offset_ = offset;
}
+ void ALWAYS_INLINE DefCFARegister(Reg reg) {
+ if (UNLIKELY(enabled_)) {
+ ImplicitlyAdvancePC();
+ this->PushUint8(DW_CFA_def_cfa_register);
+ this->PushUleb128(reg.num());
+ }
+ }
+
+ void ALWAYS_INLINE DefCFAOffset(int offset) {
+ if (UNLIKELY(enabled_)) {
+ if (current_cfa_offset_ != offset) {
+ ImplicitlyAdvancePC();
+ if (offset >= 0) {
+ this->PushUint8(DW_CFA_def_cfa_offset);
+ this->PushUleb128(offset); // Non-factored.
+ } else {
+ uses_dwarf3_features_ = true;
+ this->PushUint8(DW_CFA_def_cfa_offset_sf);
+ this->PushSleb128(FactorDataOffset(offset));
+ }
+ }
+ }
+ // Uncoditional so that the user can still get and check the value.
+ current_cfa_offset_ = offset;
+ }
+
+ void ALWAYS_INLINE ValOffset(Reg reg, int offset) {
+ if (UNLIKELY(enabled_)) {
+ ImplicitlyAdvancePC();
+ uses_dwarf3_features_ = true;
+ int factored_offset = FactorDataOffset(offset); // May change sign.
+ if (factored_offset >= 0) {
+ this->PushUint8(DW_CFA_val_offset);
+ this->PushUleb128(reg.num());
+ this->PushUleb128(factored_offset);
+ } else {
+ this->PushUint8(DW_CFA_val_offset_sf);
+ this->PushUleb128(reg.num());
+ this->PushSleb128(factored_offset);
+ }
+ }
+ }
+
+ void ALWAYS_INLINE DefCFAExpression(void * expr, int expr_size) {
+ if (UNLIKELY(enabled_)) {
+ ImplicitlyAdvancePC();
+ uses_dwarf3_features_ = true;
+ this->PushUint8(DW_CFA_def_cfa_expression);
+ this->PushUleb128(expr_size);
+ this->PushData(expr, expr_size);
+ }
+ }
+
+ void ALWAYS_INLINE Expression(Reg reg, void * expr, int expr_size) {
+ if (UNLIKELY(enabled_)) {
+ ImplicitlyAdvancePC();
+ uses_dwarf3_features_ = true;
+ this->PushUint8(DW_CFA_expression);
+ this->PushUleb128(reg.num());
+ this->PushUleb128(expr_size);
+ this->PushData(expr, expr_size);
+ }
+ }
+
+ void ALWAYS_INLINE ValExpression(Reg reg, void * expr, int expr_size) {
+ if (UNLIKELY(enabled_)) {
+ ImplicitlyAdvancePC();
+ uses_dwarf3_features_ = true;
+ this->PushUint8(DW_CFA_val_expression);
+ this->PushUleb128(reg.num());
+ this->PushUleb128(expr_size);
+ this->PushData(expr, expr_size);
+ }
+ }
+
+ bool IsEnabled() const { return enabled_; }
+
+ void SetEnabled(bool value) { enabled_ = value; }
+
+ int GetCurrentPC() const { return current_pc_; }
+
+ int GetCurrentCFAOffset() const { return current_cfa_offset_; }
+
+ void SetCurrentCFAOffset(int offset) { current_cfa_offset_ = offset; }
+
using Writer<Allocator>::data;
- DebugFrameOpCodeWriter(const Allocator& alloc = Allocator())
+ DebugFrameOpCodeWriter(bool enabled = true,
+ const Allocator& alloc = Allocator())
: Writer<Allocator>(&opcodes_),
+ enabled_(enabled),
opcodes_(alloc),
current_cfa_offset_(0),
current_pc_(0),
uses_dwarf3_features_(false) {
+ if (enabled) {
+ // Best guess based on couple of observed outputs.
+ opcodes_.reserve(16);
+ }
}
virtual ~DebugFrameOpCodeWriter() { }
@@ -271,6 +317,7 @@
return offset / kCodeAlignmentFactor;
}
+ bool enabled_; // If disabled all writes are no-ops.
std::vector<uint8_t, Allocator> opcodes_;
int current_cfa_offset_;
int current_pc_;