Skip to content

Commit b10e027

Browse files
authored
Refactor UnwindInfo codes and frame_register (#2307)
* Refactor UnwindInfo codes and frame_register * use isa word_size * fix filetests * Add comment about UnwindCode::PushRegister
1 parent 4f104d3 commit b10e027

File tree

8 files changed

+219
-128
lines changed

8 files changed

+219
-128
lines changed

cranelift/codegen/src/isa/unwind.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,12 @@ pub(crate) mod input {
2727
SaveRegister {
2828
offset: CodeOffset,
2929
reg: Reg,
30+
stack_offset: u32,
3031
},
3132
RestoreRegister {
3233
offset: CodeOffset,
3334
reg: Reg,
3435
},
35-
SaveXmmRegister {
36-
offset: CodeOffset,
37-
reg: Reg,
38-
stack_offset: u32,
39-
},
4036
StackAlloc {
4137
offset: CodeOffset,
4238
size: u32,
@@ -64,5 +60,6 @@ pub(crate) mod input {
6460
pub(crate) prologue_unwind_codes: Vec<UnwindCode<Reg>>,
6561
pub(crate) epilogues_unwind_codes: Vec<Vec<UnwindCode<Reg>>>,
6662
pub(crate) function_size: CodeOffset,
63+
pub(crate) word_size: u8,
6764
}
6865
}

cranelift/codegen/src/isa/unwind/systemv.rs

Lines changed: 64 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -115,12 +115,10 @@ pub struct UnwindInfo {
115115
impl UnwindInfo {
116116
pub(crate) fn build<'b>(
117117
unwind: input::UnwindInfo<RegUnit>,
118-
word_size: u8,
119-
frame_register: Option<RegUnit>,
120118
map_reg: &'b dyn RegisterMapper,
121119
) -> CodegenResult<Self> {
122120
use input::UnwindCode;
123-
let mut builder = InstructionBuilder::new(word_size, frame_register, map_reg);
121+
let mut builder = InstructionBuilder::new(unwind.word_size, map_reg);
124122

125123
for c in unwind.prologue_unwind_codes.iter().chain(
126124
unwind
@@ -130,9 +128,13 @@ impl UnwindInfo {
130128
.flatten(),
131129
) {
132130
match c {
133-
UnwindCode::SaveRegister { offset, reg } => {
131+
UnwindCode::SaveRegister {
132+
offset,
133+
reg,
134+
stack_offset: 0,
135+
} => {
134136
builder
135-
.push_reg(*offset, *reg)
137+
.save_reg(*offset, *reg)
136138
.map_err(CodegenError::RegisterMappingError)?;
137139
}
138140
UnwindCode::StackAlloc { offset, size } => {
@@ -143,7 +145,7 @@ impl UnwindInfo {
143145
}
144146
UnwindCode::RestoreRegister { offset, reg } => {
145147
builder
146-
.pop_reg(*offset, *reg)
148+
.restore_reg(*offset, *reg)
147149
.map_err(CodegenError::RegisterMappingError)?;
148150
}
149151
UnwindCode::SetFramePointer { offset, reg } => {
@@ -180,46 +182,29 @@ impl UnwindInfo {
180182
}
181183

182184
struct InstructionBuilder<'a> {
183-
word_size: u8,
184-
cfa_offset: i32,
185-
saved_state: Option<i32>,
185+
sp_offset: i32,
186186
frame_register: Option<RegUnit>,
187+
saved_state: Option<(i32, Option<RegUnit>)>,
187188
map_reg: &'a dyn RegisterMapper,
188189
instructions: Vec<(u32, CallFrameInstruction)>,
189190
}
190191

191192
impl<'a> InstructionBuilder<'a> {
192-
fn new(
193-
word_size: u8,
194-
frame_register: Option<RegUnit>,
195-
map_reg: &'a (dyn RegisterMapper + 'a),
196-
) -> Self {
193+
fn new(word_size: u8, map_reg: &'a (dyn RegisterMapper + 'a)) -> Self {
197194
Self {
198-
word_size,
199-
cfa_offset: word_size as i32, // CFA offset starts at word size offset to account for the return address on stack
195+
sp_offset: word_size as i32, // CFA offset starts at word size offset to account for the return address on stack
200196
saved_state: None,
201-
frame_register,
197+
frame_register: None,
202198
map_reg,
203199
instructions: Vec::new(),
204200
}
205201
}
206202

207-
fn push_reg(&mut self, offset: u32, reg: RegUnit) -> Result<(), RegisterMappingError> {
208-
self.cfa_offset += self.word_size as i32;
209-
// Update the CFA if this is the save of the frame pointer register or if a frame pointer isn't being used
210-
// When using a frame pointer, we only need to update the CFA to account for the push of the frame pointer itself
211-
if match self.frame_register {
212-
Some(fp) => reg == fp,
213-
None => true,
214-
} {
215-
self.instructions
216-
.push((offset, CallFrameInstruction::CfaOffset(self.cfa_offset)));
217-
}
218-
203+
fn save_reg(&mut self, offset: u32, reg: RegUnit) -> Result<(), RegisterMappingError> {
219204
// Pushes in the prologue are register saves, so record an offset of the save
220205
self.instructions.push((
221206
offset,
222-
CallFrameInstruction::Offset(self.map_reg.map(reg)?, -self.cfa_offset),
207+
CallFrameInstruction::Offset(self.map_reg.map(reg)?, -self.sp_offset),
223208
));
224209

225210
Ok(())
@@ -228,76 +213,92 @@ impl<'a> InstructionBuilder<'a> {
228213
fn adjust_sp_down_imm(&mut self, offset: u32, imm: i64) {
229214
assert!(imm <= core::u32::MAX as i64);
230215

216+
self.sp_offset += imm as i32;
217+
231218
// Don't adjust the CFA if we're using a frame pointer
232219
if self.frame_register.is_some() {
233220
return;
234221
}
235222

236-
self.cfa_offset += imm as i32;
237223
self.instructions
238-
.push((offset, CallFrameInstruction::CfaOffset(self.cfa_offset)));
224+
.push((offset, CallFrameInstruction::CfaOffset(self.sp_offset)));
239225
}
240226

241227
fn adjust_sp_up_imm(&mut self, offset: u32, imm: i64) {
242228
assert!(imm <= core::u32::MAX as i64);
243229

230+
self.sp_offset -= imm as i32;
231+
244232
// Don't adjust the CFA if we're using a frame pointer
245233
if self.frame_register.is_some() {
246234
return;
247235
}
248236

249-
self.cfa_offset -= imm as i32;
250-
self.instructions
251-
.push((offset, CallFrameInstruction::CfaOffset(self.cfa_offset)));
237+
let cfa_inst_ofs = {
238+
// Scan to find and merge with CFA instruction with the same offset.
239+
let mut it = self.instructions.iter_mut();
240+
loop {
241+
match it.next_back() {
242+
Some((i_offset, i)) if *i_offset == offset => {
243+
if let CallFrameInstruction::Cfa(_, o) = i {
244+
break Some(o);
245+
}
246+
}
247+
_ => {
248+
break None;
249+
}
250+
}
251+
}
252+
};
253+
254+
if let Some(o) = cfa_inst_ofs {
255+
// Update previous CFA instruction.
256+
*o = self.sp_offset;
257+
} else {
258+
// Add just CFA offset instruction.
259+
self.instructions
260+
.push((offset, CallFrameInstruction::CfaOffset(self.sp_offset)));
261+
}
252262
}
253263

254264
fn set_cfa_reg(&mut self, offset: u32, reg: RegUnit) -> Result<(), RegisterMappingError> {
255265
self.instructions.push((
256266
offset,
257267
CallFrameInstruction::CfaRegister(self.map_reg.map(reg)?),
258268
));
269+
self.frame_register = Some(reg);
259270
Ok(())
260271
}
261272

262-
fn pop_reg(&mut self, offset: u32, reg: RegUnit) -> Result<(), RegisterMappingError> {
263-
self.cfa_offset -= self.word_size as i32;
264-
265-
// Update the CFA if this is the restore of the frame pointer register or if a frame pointer isn't being used
266-
match self.frame_register {
267-
Some(fp) => {
268-
if reg == fp {
269-
self.instructions.push((
270-
offset,
271-
CallFrameInstruction::Cfa(self.map_reg.rsp(), self.cfa_offset),
272-
));
273-
}
274-
}
275-
None => {
276-
self.instructions
277-
.push((offset, CallFrameInstruction::CfaOffset(self.cfa_offset)));
278-
279-
// Pops in the epilogue are register restores, so record a "same value" for the register
280-
// This isn't necessary when using a frame pointer as the CFA doesn't change for CSR restores
281-
self.instructions.push((
282-
offset,
283-
CallFrameInstruction::SameValue(self.map_reg.map(reg)?),
284-
));
285-
}
286-
};
273+
fn restore_reg(&mut self, offset: u32, reg: RegUnit) -> Result<(), RegisterMappingError> {
274+
// Update the CFA if this is the restore of the frame pointer register.
275+
if Some(reg) == self.frame_register {
276+
self.frame_register = None;
277+
self.instructions.push((
278+
offset,
279+
CallFrameInstruction::Cfa(self.map_reg.rsp(), self.sp_offset),
280+
));
281+
}
282+
// Pops in the epilogue are register restores, so record a "same value" for the register
283+
self.instructions.push((
284+
offset,
285+
CallFrameInstruction::SameValue(self.map_reg.map(reg)?),
286+
));
287287

288288
Ok(())
289289
}
290290

291291
fn remember_state(&mut self, offset: u32) {
292-
self.saved_state = Some(self.cfa_offset);
292+
self.saved_state = Some((self.sp_offset, self.frame_register));
293293

294294
self.instructions
295295
.push((offset, CallFrameInstruction::RememberState));
296296
}
297297

298298
fn restore_state(&mut self, offset: u32) {
299-
let cfa_offset = self.saved_state.take().unwrap();
300-
self.cfa_offset = cfa_offset;
299+
let (sp_offset, frame_register) = self.saved_state.take().unwrap();
300+
self.sp_offset = sp_offset;
301+
self.frame_register = frame_register;
301302

302303
self.instructions
303304
.push((offset, CallFrameInstruction::RestoreState));

cranelift/codegen/src/isa/unwind/winx64.rs

Lines changed: 47 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -137,10 +137,15 @@ impl UnwindCode {
137137
}
138138
}
139139

140+
pub(crate) enum MappedRegister {
141+
Int(u8),
142+
Xmm(u8),
143+
}
144+
140145
/// Maps UnwindInfo register to Windows x64 unwind data.
141146
pub(crate) trait RegisterMapper {
142147
/// Maps RegUnit.
143-
fn map(reg: RegUnit) -> u8;
148+
fn map(reg: RegUnit) -> MappedRegister;
144149
}
145150

146151
/// Represents Windows x64 unwind information.
@@ -219,30 +224,55 @@ impl UnwindInfo {
219224
) -> CodegenResult<Self> {
220225
use crate::isa::unwind::input::UnwindCode as InputUnwindCode;
221226

227+
let word_size: u32 = unwind.word_size.into();
222228
let mut unwind_codes = Vec::new();
223229
for c in unwind.prologue_unwind_codes.iter() {
224230
match c {
225-
InputUnwindCode::SaveRegister { offset, reg } => {
226-
unwind_codes.push(UnwindCode::PushRegister {
227-
offset: ensure_unwind_offset(*offset)?,
228-
reg: MR::map(*reg),
229-
});
230-
}
231-
InputUnwindCode::StackAlloc { offset, size } => {
232-
unwind_codes.push(UnwindCode::StackAlloc {
233-
offset: ensure_unwind_offset(*offset)?,
234-
size: *size,
235-
});
236-
}
237-
InputUnwindCode::SaveXmmRegister {
231+
InputUnwindCode::SaveRegister {
238232
offset,
239233
reg,
240234
stack_offset,
241235
} => {
242-
unwind_codes.push(UnwindCode::SaveXmm {
236+
let reg = MR::map(*reg);
237+
let offset = ensure_unwind_offset(*offset)?;
238+
match reg {
239+
MappedRegister::Int(reg) => {
240+
// Attempt to convert sequence of the `InputUnwindCode`:
241+
// `StackAlloc { size = word_size }`, `SaveRegister { stack_offset: 0 }`
242+
// to the shorter `UnwindCode::PushRegister`.
243+
let push_reg_sequence = if let Some(UnwindCode::StackAlloc {
244+
offset: alloc_offset,
245+
size,
246+
}) = unwind_codes.last()
247+
{
248+
*size == word_size && offset == *alloc_offset && *stack_offset == 0
249+
} else {
250+
false
251+
};
252+
if push_reg_sequence {
253+
*unwind_codes.last_mut().unwrap() =
254+
UnwindCode::PushRegister { offset, reg };
255+
} else {
256+
// TODO add `UnwindCode::SaveRegister` to handle multiple register
257+
// pushes with single `UnwindCode::StackAlloc`.
258+
return Err(CodegenError::Unsupported(
259+
"Unsupported UnwindCode::PushRegister sequence".into(),
260+
));
261+
}
262+
}
263+
MappedRegister::Xmm(reg) => {
264+
unwind_codes.push(UnwindCode::SaveXmm {
265+
offset,
266+
reg,
267+
stack_offset: *stack_offset,
268+
});
269+
}
270+
}
271+
}
272+
InputUnwindCode::StackAlloc { offset, size } => {
273+
unwind_codes.push(UnwindCode::StackAlloc {
243274
offset: ensure_unwind_offset(*offset)?,
244-
reg: MR::map(*reg),
245-
stack_offset: *stack_offset,
275+
size: *size,
246276
});
247277
}
248278
_ => {}

cranelift/codegen/src/isa/x86/abi.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1083,8 +1083,7 @@ pub fn create_unwind_info(
10831083
// In the future, we should be omitting frame pointer as an optimization, so this will change
10841084
Ok(match func.signature.call_conv {
10851085
CallConv::Fast | CallConv::Cold | CallConv::SystemV => {
1086-
super::unwind::systemv::create_unwind_info(func, isa, Some(RU::rbp.into()))?
1087-
.map(|u| UnwindInfo::SystemV(u))
1086+
super::unwind::systemv::create_unwind_info(func, isa)?.map(|u| UnwindInfo::SystemV(u))
10881087
}
10891088
CallConv::WindowsFastcall => {
10901089
super::unwind::winx64::create_unwind_info(func, isa)?.map(|u| UnwindInfo::WindowsX64(u))

0 commit comments

Comments
 (0)