Skip to content

Commit 2f74eee

Browse files
committed
[lldb] Clean up UnwindAssemblyInstEmulation
My main motivation was trying to understand how the function and whether the rows need to be (shared) pointers. I noticed that the function essentially constructs two copies unwind plans in parallel (the second being the saved_unwind_states). If we delay the construction of the unwind plan to the end of the function, then we never need two copies of a single row (we can just move it into the final result), so we can just use them as value types. This makes the overall logic of the function stand out better as it avoids the laborious deep copies of the Row shared pointer. I've also noticed that a large portion of the function is devoted to recomputing certain properties of the unwind state (e.g. the `m_fp_is_cfa` field). Instead of doing that, this patch just saves/restores them together with the rest of the state.
1 parent 0e3ba99 commit 2f74eee

File tree

2 files changed

+75
-132
lines changed

2 files changed

+75
-132
lines changed

lldb/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.cpp

Lines changed: 64 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -86,19 +86,21 @@ bool UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly(
8686
const bool show_address = true;
8787
const bool show_bytes = true;
8888
const bool show_control_flow_kind = false;
89-
m_cfa_reg_info = *m_inst_emulator_up->GetRegisterInfo(
89+
90+
m_state.cfa_reg_info = *m_inst_emulator_up->GetRegisterInfo(
9091
unwind_plan.GetRegisterKind(), unwind_plan.GetInitialCFARegister());
91-
m_fp_is_cfa = false;
92-
m_register_values.clear();
92+
m_state.fp_is_cfa = false;
93+
m_state.register_values.clear();
94+
9395
m_pushed_regs.clear();
9496

9597
// Initialize the CFA with a known value. In the 32 bit case it will be
9698
// 0x80000000, and in the 64 bit case 0x8000000000000000. We use the address
9799
// byte size to be safe for any future address sizes
98100
m_initial_sp = (1ull << ((addr_byte_size * 8) - 1));
99101
RegisterValue cfa_reg_value;
100-
cfa_reg_value.SetUInt(m_initial_sp, m_cfa_reg_info.byte_size);
101-
SetRegisterValue(m_cfa_reg_info, cfa_reg_value);
102+
cfa_reg_value.SetUInt(m_initial_sp, m_state.cfa_reg_info.byte_size);
103+
SetRegisterValue(m_state.cfa_reg_info, cfa_reg_value);
102104

103105
const InstructionList &inst_list = disasm_sp->GetInstructionList();
104106
const size_t num_instructions = inst_list.GetSize();
@@ -107,33 +109,25 @@ bool UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly(
107109
Instruction *inst = inst_list.GetInstructionAtIndex(0).get();
108110
const lldb::addr_t base_addr = inst->GetAddress().GetFileAddress();
109111

110-
// Map for storing the unwind plan row and the value of the registers at a
111-
// given offset. When we see a forward branch we add a new entry to this map
112-
// with the actual unwind plan row and register context for the target
113-
// address of the branch as the current data have to be valid for the target
114-
// address of the branch too if we are in the same function.
115-
std::map<lldb::addr_t, std::pair<UnwindPlan::RowSP, RegisterValueMap>>
116-
saved_unwind_states;
112+
// Map for storing the unwind state at a given offset. When we see a forward
113+
// branch we add a new entry to this map with the actual unwind plan row and
114+
// register context for the target address of the branch as the current data
115+
// have to be valid for the target address of the branch too if we are in
116+
// the same function.
117+
std::map<lldb::addr_t, UnwindState> saved_unwind_states;
117118

118-
// Make a copy of the current instruction Row and save it in m_curr_row so
119+
// Make a copy of the current instruction Row and save it in m_state so
119120
// we can add updates as we process the instructions.
120-
UnwindPlan::RowSP last_row =
121-
std::make_shared<UnwindPlan::Row>(*unwind_plan.GetLastRow());
122-
m_curr_row = std::make_shared<UnwindPlan::Row>(*last_row);
121+
m_state.row = *unwind_plan.GetLastRow();
123122

124123
// Add the initial state to the save list with offset 0.
125-
saved_unwind_states.insert({0, {last_row, m_register_values}});
126-
127-
// cache the stack pointer register number (in whatever register numbering
128-
// this UnwindPlan uses) for quick reference during instruction parsing.
129-
RegisterInfo sp_reg_info = *m_inst_emulator_up->GetRegisterInfo(
130-
eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP);
124+
auto condition_block_start_state =
125+
saved_unwind_states.emplace(0, m_state).first;
131126

132127
// The architecture dependent condition code of the last processed
133128
// instruction.
134129
EmulateInstruction::InstructionCondition last_condition =
135130
EmulateInstruction::UnconditionalCondition;
136-
lldb::addr_t condition_block_start_offset = 0;
137131

138132
for (size_t idx = 0; idx < num_instructions; ++idx) {
139133
m_curr_row_modified = false;
@@ -151,78 +145,28 @@ bool UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly(
151145
--it; // Move it to the row corresponding to the current offset
152146

153147
// If the offset of m_curr_row don't match with the offset we see in
154-
// saved_unwind_states then we have to update m_curr_row and
155-
// m_register_values based on the saved values. It is happening after we
156-
// processed an epilogue and a return to caller instruction.
157-
if (it->second.first->GetOffset() != m_curr_row->GetOffset()) {
158-
UnwindPlan::Row *newrow = new UnwindPlan::Row;
159-
*newrow = *it->second.first;
160-
m_curr_row.reset(newrow);
161-
m_register_values = it->second.second;
162-
// re-set the CFA register ivars to match the new m_curr_row.
163-
if (sp_reg_info.name &&
164-
m_curr_row->GetCFAValue().IsRegisterPlusOffset()) {
165-
uint32_t row_cfa_regnum =
166-
m_curr_row->GetCFAValue().GetRegisterNumber();
167-
lldb::RegisterKind row_kind = m_unwind_plan_ptr->GetRegisterKind();
168-
// set m_cfa_reg_info to the row's CFA reg.
169-
m_cfa_reg_info =
170-
*m_inst_emulator_up->GetRegisterInfo(row_kind, row_cfa_regnum);
171-
// set m_fp_is_cfa.
172-
if (sp_reg_info.kinds[row_kind] == row_cfa_regnum)
173-
m_fp_is_cfa = false;
174-
else
175-
m_fp_is_cfa = true;
176-
}
177-
}
148+
// saved_unwind_states then we have to update current unwind state to
149+
// the saved values. It is happening after we processed an epilogue and a
150+
// return to caller instruction.
151+
if (it->second.row.GetOffset() != m_state.row.GetOffset())
152+
m_state = it->second;
178153

179154
m_inst_emulator_up->SetInstruction(inst->GetOpcode(), inst->GetAddress(),
180155
nullptr);
181156

182157
if (last_condition != m_inst_emulator_up->GetInstructionCondition()) {
183-
if (m_inst_emulator_up->GetInstructionCondition() !=
184-
EmulateInstruction::UnconditionalCondition &&
185-
saved_unwind_states.count(current_offset) == 0) {
186-
// If we don't have a saved row for the current offset then save our
187-
// current state because we will have to restore it after the
188-
// conditional block.
189-
auto new_row = std::make_shared<UnwindPlan::Row>(*m_curr_row.get());
190-
saved_unwind_states.insert(
191-
{current_offset, {new_row, m_register_values}});
192-
}
193-
194158
// If the last instruction was conditional with a different condition
195-
// then the then current condition then restore the condition.
159+
// than the current condition then restore the state.
196160
if (last_condition != EmulateInstruction::UnconditionalCondition) {
197-
const auto &saved_state =
198-
saved_unwind_states.at(condition_block_start_offset);
199-
m_curr_row = std::make_shared<UnwindPlan::Row>(*saved_state.first);
200-
m_curr_row->SetOffset(current_offset);
201-
m_register_values = saved_state.second;
202-
// re-set the CFA register ivars to match the new m_curr_row.
203-
if (sp_reg_info.name &&
204-
m_curr_row->GetCFAValue().IsRegisterPlusOffset()) {
205-
uint32_t row_cfa_regnum =
206-
m_curr_row->GetCFAValue().GetRegisterNumber();
207-
lldb::RegisterKind row_kind = m_unwind_plan_ptr->GetRegisterKind();
208-
// set m_cfa_reg_info to the row's CFA reg.
209-
m_cfa_reg_info =
210-
*m_inst_emulator_up->GetRegisterInfo(row_kind, row_cfa_regnum);
211-
// set m_fp_is_cfa.
212-
if (sp_reg_info.kinds[row_kind] == row_cfa_regnum)
213-
m_fp_is_cfa = false;
214-
else
215-
m_fp_is_cfa = true;
216-
}
161+
m_state = condition_block_start_state->second;
162+
m_state.row.SetOffset(current_offset);
217163
// The last instruction might already created a row for this offset
218164
// and we want to overwrite it.
219-
bool replace_existing = true;
220-
unwind_plan.InsertRow(std::make_shared<UnwindPlan::Row>(*m_curr_row),
221-
replace_existing);
165+
saved_unwind_states.insert_or_assign(current_offset, m_state);
222166
}
223167

224168
// We are starting a new conditional block at the actual offset
225-
condition_block_start_offset = current_offset;
169+
condition_block_start_state = it;
226170
}
227171

228172
if (log && log->GetVerbose()) {
@@ -245,11 +189,10 @@ bool UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly(
245189
if (m_forward_branch_offset != 0 &&
246190
range.ContainsFileAddress(inst->GetAddress().GetFileAddress() +
247191
m_forward_branch_offset)) {
248-
auto newrow = std::make_shared<UnwindPlan::Row>(*m_curr_row.get());
249-
newrow->SetOffset(current_offset + m_forward_branch_offset);
250-
saved_unwind_states.insert({current_offset + m_forward_branch_offset,
251-
{newrow, m_register_values}});
252-
unwind_plan.InsertRow(newrow);
192+
if (auto [it, inserted] = saved_unwind_states.emplace(
193+
current_offset + m_forward_branch_offset, m_state);
194+
inserted)
195+
it->second.row.SetOffset(current_offset + m_forward_branch_offset);
253196
}
254197

255198
// Were there any changes to the CFI while evaluating this instruction?
@@ -258,21 +201,18 @@ bool UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly(
258201
// current address
259202
if (saved_unwind_states.count(current_offset +
260203
inst->GetOpcode().GetByteSize()) == 0) {
261-
m_curr_row->SetOffset(current_offset +
204+
m_state.row.SetOffset(current_offset +
262205
inst->GetOpcode().GetByteSize());
263-
unwind_plan.InsertRow(m_curr_row);
264-
saved_unwind_states.insert(
265-
{current_offset + inst->GetOpcode().GetByteSize(),
266-
{m_curr_row, m_register_values}});
267-
268-
// Allocate a new Row for m_curr_row, copy the current state
269-
// into it
270-
UnwindPlan::Row *newrow = new UnwindPlan::Row;
271-
*newrow = *m_curr_row.get();
272-
m_curr_row.reset(newrow);
206+
saved_unwind_states.emplace(
207+
current_offset + inst->GetOpcode().GetByteSize(), m_state);
273208
}
274209
}
275210
}
211+
for (auto &[_, state] : saved_unwind_states) {
212+
unwind_plan.InsertRow(
213+
std::make_shared<UnwindPlan::Row>(std::move(state.row)),
214+
/*replace_existing=*/true);
215+
}
276216
}
277217

278218
if (log && log->GetVerbose()) {
@@ -339,14 +279,14 @@ uint64_t UnwindAssemblyInstEmulation::MakeRegisterKindValuePair(
339279

340280
void UnwindAssemblyInstEmulation::SetRegisterValue(
341281
const RegisterInfo &reg_info, const RegisterValue &reg_value) {
342-
m_register_values[MakeRegisterKindValuePair(reg_info)] = reg_value;
282+
m_state.register_values[MakeRegisterKindValuePair(reg_info)] = reg_value;
343283
}
344284

345285
bool UnwindAssemblyInstEmulation::GetRegisterValue(const RegisterInfo &reg_info,
346286
RegisterValue &reg_value) {
347287
const uint64_t reg_id = MakeRegisterKindValuePair(reg_info);
348-
RegisterValueMap::const_iterator pos = m_register_values.find(reg_id);
349-
if (pos != m_register_values.end()) {
288+
RegisterValueMap::const_iterator pos = m_state.register_values.find(reg_id);
289+
if (pos != m_state.register_values.end()) {
350290
reg_value = pos->second;
351291
return true; // We had a real value that comes from an opcode that wrote
352292
// to it...
@@ -444,7 +384,7 @@ size_t UnwindAssemblyInstEmulation::WriteMemory(
444384
generic_regnum != LLDB_REGNUM_GENERIC_SP) {
445385
if (m_pushed_regs.try_emplace(reg_num, addr).second) {
446386
const int32_t offset = addr - m_initial_sp;
447-
m_curr_row->SetRegisterLocationToAtCFAPlusOffset(reg_num, offset,
387+
m_state.row.SetRegisterLocationToAtCFAPlusOffset(reg_num, offset,
448388
/*can_replace=*/true);
449389
m_curr_row_modified = true;
450390
}
@@ -548,13 +488,14 @@ bool UnwindAssemblyInstEmulation::WriteRegister(
548488
// CFA offset
549489
// with the same amount.
550490
lldb::RegisterKind kind = m_unwind_plan_ptr->GetRegisterKind();
551-
if (m_fp_is_cfa && reg_info->kinds[kind] == m_cfa_reg_info.kinds[kind] &&
491+
if (m_state.fp_is_cfa &&
492+
reg_info->kinds[kind] == m_state.cfa_reg_info.kinds[kind] &&
552493
context.GetInfoType() ==
553494
EmulateInstruction::eInfoTypeRegisterPlusOffset &&
554495
context.info.RegisterPlusOffset.reg.kinds[kind] ==
555-
m_cfa_reg_info.kinds[kind]) {
496+
m_state.cfa_reg_info.kinds[kind]) {
556497
const int64_t offset = context.info.RegisterPlusOffset.signed_offset;
557-
m_curr_row->GetCFAValue().IncOffset(-1 * offset);
498+
m_state.row.GetCFAValue().IncOffset(-1 * offset);
558499
m_curr_row_modified = true;
559500
}
560501
} break;
@@ -590,25 +531,25 @@ bool UnwindAssemblyInstEmulation::WriteRegister(
590531
case EmulateInstruction::eInfoTypeAddress:
591532
if (auto it = m_pushed_regs.find(reg_num);
592533
it != m_pushed_regs.end() && context.info.address == it->second) {
593-
m_curr_row->SetRegisterLocationToSame(reg_num,
534+
m_state.row.SetRegisterLocationToSame(reg_num,
594535
false /*must_replace*/);
595536
m_curr_row_modified = true;
596537

597538
// FP has been restored to its original value, we are back
598539
// to using SP to calculate the CFA.
599-
if (m_fp_is_cfa) {
600-
m_fp_is_cfa = false;
540+
if (m_state.fp_is_cfa) {
541+
m_state.fp_is_cfa = false;
601542
lldb::RegisterKind sp_reg_kind = eRegisterKindGeneric;
602543
uint32_t sp_reg_num = LLDB_REGNUM_GENERIC_SP;
603544
RegisterInfo sp_reg_info =
604545
*m_inst_emulator_up->GetRegisterInfo(sp_reg_kind, sp_reg_num);
605546
RegisterValue sp_reg_val;
606547
if (GetRegisterValue(sp_reg_info, sp_reg_val)) {
607-
m_cfa_reg_info = sp_reg_info;
548+
m_state.cfa_reg_info = sp_reg_info;
608549
const uint32_t cfa_reg_num =
609550
sp_reg_info.kinds[m_unwind_plan_ptr->GetRegisterKind()];
610551
assert(cfa_reg_num != LLDB_INVALID_REGNUM);
611-
m_curr_row->GetCFAValue().SetIsRegisterPlusOffset(
552+
m_state.row.GetCFAValue().SetIsRegisterPlusOffset(
612553
cfa_reg_num, m_initial_sp - sp_reg_val.GetAsUInt64());
613554
}
614555
}
@@ -620,7 +561,7 @@ bool UnwindAssemblyInstEmulation::WriteRegister(
620561
generic_regnum == LLDB_REGNUM_GENERIC_FLAGS) &&
621562
"eInfoTypeISA used for popping a register other the PC/FLAGS");
622563
if (generic_regnum != LLDB_REGNUM_GENERIC_FLAGS) {
623-
m_curr_row->SetRegisterLocationToSame(reg_num,
564+
m_state.row.SetRegisterLocationToSame(reg_num,
624565
false /*must_replace*/);
625566
m_curr_row_modified = true;
626567
}
@@ -633,26 +574,26 @@ bool UnwindAssemblyInstEmulation::WriteRegister(
633574
} break;
634575

635576
case EmulateInstruction::eContextSetFramePointer:
636-
if (!m_fp_is_cfa) {
637-
m_fp_is_cfa = true;
638-
m_cfa_reg_info = *reg_info;
577+
if (!m_state.fp_is_cfa) {
578+
m_state.fp_is_cfa = true;
579+
m_state.cfa_reg_info = *reg_info;
639580
const uint32_t cfa_reg_num =
640581
reg_info->kinds[m_unwind_plan_ptr->GetRegisterKind()];
641582
assert(cfa_reg_num != LLDB_INVALID_REGNUM);
642-
m_curr_row->GetCFAValue().SetIsRegisterPlusOffset(
583+
m_state.row.GetCFAValue().SetIsRegisterPlusOffset(
643584
cfa_reg_num, m_initial_sp - reg_value.GetAsUInt64());
644585
m_curr_row_modified = true;
645586
}
646587
break;
647588

648589
case EmulateInstruction::eContextRestoreStackPointer:
649-
if (m_fp_is_cfa) {
650-
m_fp_is_cfa = false;
651-
m_cfa_reg_info = *reg_info;
590+
if (m_state.fp_is_cfa) {
591+
m_state.fp_is_cfa = false;
592+
m_state.cfa_reg_info = *reg_info;
652593
const uint32_t cfa_reg_num =
653594
reg_info->kinds[m_unwind_plan_ptr->GetRegisterKind()];
654595
assert(cfa_reg_num != LLDB_INVALID_REGNUM);
655-
m_curr_row->GetCFAValue().SetIsRegisterPlusOffset(
596+
m_state.row.GetCFAValue().SetIsRegisterPlusOffset(
656597
cfa_reg_num, m_initial_sp - reg_value.GetAsUInt64());
657598
m_curr_row_modified = true;
658599
}
@@ -661,9 +602,9 @@ bool UnwindAssemblyInstEmulation::WriteRegister(
661602
case EmulateInstruction::eContextAdjustStackPointer:
662603
// If we have created a frame using the frame pointer, don't follow
663604
// subsequent adjustments to the stack pointer.
664-
if (!m_fp_is_cfa) {
665-
m_curr_row->GetCFAValue().SetIsRegisterPlusOffset(
666-
m_curr_row->GetCFAValue().GetRegisterNumber(),
605+
if (!m_state.fp_is_cfa) {
606+
m_state.row.GetCFAValue().SetIsRegisterPlusOffset(
607+
m_state.row.GetCFAValue().GetRegisterNumber(),
667608
m_initial_sp - reg_value.GetAsUInt64());
668609
m_curr_row_modified = true;
669610
}

lldb/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.h

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,8 @@ class UnwindAssemblyInstEmulation : public lldb_private::UnwindAssembly {
6363
UnwindAssemblyInstEmulation(const lldb_private::ArchSpec &arch,
6464
lldb_private::EmulateInstruction *inst_emulator)
6565
: UnwindAssembly(arch), m_inst_emulator_up(inst_emulator),
66-
m_range_ptr(nullptr), m_unwind_plan_ptr(nullptr), m_curr_row(),
67-
m_initial_sp(0), m_cfa_reg_info(), m_fp_is_cfa(false),
68-
m_register_values(), m_pushed_regs(), m_curr_row_modified(false),
69-
m_forward_branch_offset(0) {
66+
m_range_ptr(nullptr), m_unwind_plan_ptr(nullptr), m_initial_sp(0),
67+
m_curr_row_modified(false), m_forward_branch_offset(0) {
7068
if (m_inst_emulator_up) {
7169
m_inst_emulator_up->SetBaton(this);
7270
m_inst_emulator_up->SetCallbacks(ReadMemory, WriteMemory, ReadRegister,
@@ -124,16 +122,20 @@ class UnwindAssemblyInstEmulation : public lldb_private::UnwindAssembly {
124122
bool GetRegisterValue(const lldb_private::RegisterInfo &reg_info,
125123
lldb_private::RegisterValue &reg_value);
126124

125+
typedef std::map<uint64_t, lldb_private::RegisterValue> RegisterValueMap;
126+
struct UnwindState {
127+
lldb_private::UnwindPlan::Row row = {};
128+
lldb_private::RegisterInfo cfa_reg_info = {};
129+
bool fp_is_cfa = false;
130+
RegisterValueMap register_values = {};
131+
};
132+
127133
std::unique_ptr<lldb_private::EmulateInstruction> m_inst_emulator_up;
128134
lldb_private::AddressRange *m_range_ptr;
129135
lldb_private::UnwindPlan *m_unwind_plan_ptr;
130-
lldb_private::UnwindPlan::RowSP m_curr_row;
136+
UnwindState m_state;
131137
typedef std::map<uint64_t, uint64_t> PushedRegisterToAddrMap;
132138
uint64_t m_initial_sp;
133-
lldb_private::RegisterInfo m_cfa_reg_info;
134-
bool m_fp_is_cfa;
135-
typedef std::map<uint64_t, lldb_private::RegisterValue> RegisterValueMap;
136-
RegisterValueMap m_register_values;
137139
PushedRegisterToAddrMap m_pushed_regs;
138140

139141
// While processing the instruction stream, we need to communicate some state

0 commit comments

Comments
 (0)