Skip to content
This repository has been archived by the owner on Jun 26, 2020. It is now read-only.

Commit

Permalink
Track frame layout changes.
Browse files Browse the repository at this point in the history
  • Loading branch information
yurydelendik committed Jun 20, 2019
1 parent f3f7cad commit 1c185ff
Show file tree
Hide file tree
Showing 4 changed files with 169 additions and 12 deletions.
63 changes: 63 additions & 0 deletions cranelift-codegen/src/ir/framelayout.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
//! Frame layout item changes.
use crate::ir::entities::Inst;
use crate::isa::RegUnit;
use std::boxed::Box;

#[cfg(not(feature = "std"))]
use hashmap_core::HashMap;
#[cfg(feature = "std")]
use std::collections::HashMap;

/// Change in the frame layout information.
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub enum FrameLayoutChange {
/// Base CFA pointer moved to different register/offset.
CallFrameAddressAt {
/// CFA register.
reg: RegUnit,
/// CFA offset.
offset: isize,
},
/// Register saved at.
RegAt {
/// Saved register.
reg: RegUnit,
/// Offset in the frame (offset from CFA).
cfa_offset: isize,
},
/// Return address saved at.
RaAt {
/// Offset in the frame (offset from CFA).
cfa_offset: isize,
},
}

/// Set of frame layout changes.
pub type FrameLayoutChanges = Box<[FrameLayoutChange]>;

/// Frame items layout for (prologue/epilog) instructions.
#[derive(Debug, Clone)]
pub struct FrameLayout {
/// Initial frame layout.
pub initial: FrameLayoutChanges,

/// Instruction frame layout (changes).
pub instructions: HashMap<Inst, FrameLayoutChanges>,
}

impl FrameLayout {
/// Creates instance of FrameLayout.
pub fn new() -> Self {
FrameLayout {
initial: vec![].into_boxed_slice(),
instructions: HashMap::new(),
}
}

/// Clear the structure.
pub fn clear(&mut self) {
self.initial = vec![].into_boxed_slice();
self.instructions.clear();
}
}
11 changes: 10 additions & 1 deletion cranelift-codegen/src/ir/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use crate::ir::{
Ebb, ExtFuncData, FuncRef, GlobalValue, GlobalValueData, Heap, HeapData, JumpTable,
JumpTableData, SigRef, StackSlot, StackSlotData, Table, TableData,
};
use crate::ir::{EbbOffsets, InstEncodings, SourceLocs, StackSlots, ValueLocations};
use crate::ir::{EbbOffsets, FrameLayout, InstEncodings, SourceLocs, StackSlots, ValueLocations};
use crate::ir::{JumpTableOffsets, JumpTables};
use crate::isa::{CallConv, EncInfo, Encoding, Legalize, TargetIsa};
use crate::regalloc::RegDiversions;
Expand Down Expand Up @@ -74,6 +74,13 @@ pub struct Function {
/// Track the original source location for each instruction. The source locations are not
/// interpreted by Cranelift, only preserved.
pub srclocs: SourceLocs,

/// Frame layout for the instructions.
///
/// The stack unwinding requires to have information about which registers and where they
/// are saved in the frame. This information is created during the prologue and epilogue
/// passes.
pub frame_layout: FrameLayout,
}

impl Function {
Expand All @@ -94,6 +101,7 @@ impl Function {
offsets: SecondaryMap::new(),
jt_offsets: SecondaryMap::new(),
srclocs: SecondaryMap::new(),
frame_layout: FrameLayout::new(),
}
}

Expand All @@ -111,6 +119,7 @@ impl Function {
self.locations.clear();
self.offsets.clear();
self.srclocs.clear();
self.frame_layout.clear();
}

/// Create a new empty, anonymous function with a Fast calling convention.
Expand Down
2 changes: 2 additions & 0 deletions cranelift-codegen/src/ir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ pub mod dfg;
pub mod entities;
mod extfunc;
mod extname;
mod framelayout;
pub mod function;
mod globalvalue;
mod heap;
Expand All @@ -32,6 +33,7 @@ pub use crate::ir::extfunc::{
AbiParam, ArgumentExtension, ArgumentPurpose, ExtFuncData, Signature,
};
pub use crate::ir::extname::ExternalName;
pub use crate::ir::framelayout::{FrameLayout, FrameLayoutChange, FrameLayoutChanges};
pub use crate::ir::function::{DisplayFunctionAnnotations, Function};
pub use crate::ir::globalvalue::GlobalValueData;
pub use crate::ir::heap::{HeapData, HeapStyle};
Expand Down
105 changes: 94 additions & 11 deletions cranelift-codegen/src/isa/x86/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ use crate::ir;
use crate::ir::immediates::Imm64;
use crate::ir::stackslot::{StackOffset, StackSize};
use crate::ir::{
get_probestack_funcref, AbiParam, ArgumentExtension, ArgumentLoc, ArgumentPurpose, InstBuilder,
ValueLoc,
get_probestack_funcref, AbiParam, ArgumentExtension, ArgumentLoc, ArgumentPurpose,
FrameLayoutChange, InstBuilder, ValueLoc,
};
use crate::isa::{CallConv, RegClass, RegUnit, TargetIsa};
use crate::regalloc::RegisterSet;
Expand Down Expand Up @@ -300,6 +300,12 @@ fn baldrdash_prologue_epilogue(func: &mut ir::Function, isa: &TargetIsa) -> Code
Ok(())
}

struct CFAState {
reg: RegUnit,
offset: isize,
position: isize,
}

/// Implementation of the fastcall-based Win64 calling convention described at [1]
/// [1] https://msdn.microsoft.com/en-us/library/ms235286.aspx
fn fastcall_prologue_epilogue(func: &mut ir::Function, isa: &TargetIsa) -> CodegenResult<()> {
Expand Down Expand Up @@ -361,14 +367,27 @@ fn fastcall_prologue_epilogue(func: &mut ir::Function, isa: &TargetIsa) -> Codeg
func.signature.returns.push(csr_arg);
}

let mut cfa_state = CFAState {
reg: RU::rsp as RegUnit,
offset: word_size as isize,
position: -(word_size as isize),
};

// Set up the cursor and insert the prologue
let entry_ebb = func.layout.entry_block().expect("missing entry block");
let mut pos = EncCursor::new(func, isa).at_first_insertion_point(entry_ebb);
insert_common_prologue(&mut pos, local_stack_size, reg_type, &csrs, isa);
insert_common_prologue(
&mut pos,
local_stack_size,
reg_type,
&csrs,
isa,
&mut cfa_state,
);

// Reset the cursor and insert the epilogue
let mut pos = pos.at_position(CursorPosition::Nowhere);
insert_common_epilogues(&mut pos, local_stack_size, reg_type, &csrs);
insert_common_epilogues(&mut pos, local_stack_size, reg_type, &csrs, &cfa_state);

Ok(())
}
Expand Down Expand Up @@ -416,14 +435,27 @@ fn system_v_prologue_epilogue(func: &mut ir::Function, isa: &TargetIsa) -> Codeg
func.signature.returns.push(csr_arg);
}

let mut cfa_state = CFAState {
reg: RU::rsp as RegUnit,
offset: word_size as isize,
position: -(word_size as isize),
};

// Set up the cursor and insert the prologue
let entry_ebb = func.layout.entry_block().expect("missing entry block");
let mut pos = EncCursor::new(func, isa).at_first_insertion_point(entry_ebb);
insert_common_prologue(&mut pos, local_stack_size, reg_type, &csrs, isa);
insert_common_prologue(
&mut pos,
local_stack_size,
reg_type,
&csrs,
isa,
&mut cfa_state,
);

// Reset the cursor and insert the epilogue
let mut pos = pos.at_position(CursorPosition::Nowhere);
insert_common_epilogues(&mut pos, local_stack_size, reg_type, &csrs);
insert_common_epilogues(&mut pos, local_stack_size, reg_type, &csrs, &cfa_state);

Ok(())
}
Expand All @@ -436,7 +468,9 @@ fn insert_common_prologue(
reg_type: ir::types::Type,
csrs: &RegisterSet,
isa: &TargetIsa,
cfa_state: &mut CFAState,
) {
let word_size = isa.pointer_bytes();
if stack_size > 0 {
// Check if there is a special stack limit parameter. If so insert stack check.
if let Some(stack_limit_arg) = pos.func.special_param(ArgumentPurpose::StackLimit) {
Expand All @@ -445,21 +479,58 @@ fn insert_common_prologue(
// Also, the size of a return address, implicitly pushed by a x86 `call` instruction,
// also should be accounted for.
// TODO: Check if the function body actually contains a `call` instruction.
let word_size = isa.pointer_bytes();
let total_stack_size = (csrs.iter(GPR).len() + 1 + 1) as i64 * word_size as i64;

insert_stack_check(pos, total_stack_size, stack_limit_arg);
}
}

pos.func.frame_layout.initial = vec![
FrameLayoutChange::CallFrameAddressAt {
reg: cfa_state.reg,
offset: cfa_state.offset,
},
FrameLayoutChange::RaAt {
cfa_offset: cfa_state.position,
},
]
.into_boxed_slice();

// Append param to entry EBB
let ebb = pos.current_ebb().expect("missing ebb under cursor");
let fp = pos.func.dfg.append_ebb_param(ebb, reg_type);
pos.func.locations[fp] = ir::ValueLoc::Reg(RU::rbp as RegUnit);

pos.ins().x86_push(fp);
pos.ins()
let push_fp_inst = pos.ins().x86_push(fp);
let word_size = word_size as isize;
cfa_state.position -= word_size;
cfa_state.offset += word_size;
pos.func.frame_layout.instructions.insert(
push_fp_inst,
vec![
FrameLayoutChange::CallFrameAddressAt {
reg: cfa_state.reg,
offset: cfa_state.offset,
},
FrameLayoutChange::RegAt {
reg: RU::rbp as RegUnit,
cfa_offset: cfa_state.position,
},
]
.into_boxed_slice(),
);
let mov_sp_inst = pos
.ins()
.copy_special(RU::rsp as RegUnit, RU::rbp as RegUnit);
cfa_state.reg = RU::rbp as RegUnit;
pos.func.frame_layout.instructions.insert(
mov_sp_inst,
vec![FrameLayoutChange::CallFrameAddressAt {
reg: cfa_state.reg,
offset: cfa_state.offset,
}]
.into_boxed_slice(),
);

for reg in csrs.iter(GPR) {
// Append param to entry EBB
Expand All @@ -469,7 +540,16 @@ fn insert_common_prologue(
pos.func.locations[csr_arg] = ir::ValueLoc::Reg(reg);

// Remember it so we can push it momentarily
pos.ins().x86_push(csr_arg);
let reg_push_inst = pos.ins().x86_push(csr_arg);
cfa_state.position -= word_size;
pos.func.frame_layout.instructions.insert(
reg_push_inst,
vec![FrameLayoutChange::RegAt {
reg: reg,
cfa_offset: cfa_state.position,
}]
.into_boxed_slice(),
);
}

// Allocate stack frame storage.
Expand Down Expand Up @@ -547,25 +627,28 @@ fn insert_common_epilogues(
stack_size: i64,
reg_type: ir::types::Type,
csrs: &RegisterSet,
cfa_state: &CFAState,
) {
while let Some(ebb) = pos.next_ebb() {
pos.goto_last_inst(ebb);
if let Some(inst) = pos.current_inst() {
if pos.func.dfg[inst].opcode().is_return() {
insert_common_epilogue(inst, stack_size, pos, reg_type, csrs);
insert_common_epilogue(inst, stack_size, pos, reg_type, csrs, &cfa_state);
}
}
}
}

/// Insert an epilogue given a specific `return` instruction.
/// This is used by common calling conventions such as System V.
/// TODO implement and handle _cfa_state more than one epilogue.
fn insert_common_epilogue(
inst: ir::Inst,
stack_size: i64,
pos: &mut EncCursor,
reg_type: ir::types::Type,
csrs: &RegisterSet,
_cfa_state: &CFAState,
) {
if stack_size > 0 {
pos.ins().adjust_sp_up_imm(Imm64::new(stack_size));
Expand Down

0 comments on commit 1c185ff

Please sign in to comment.