-
Notifications
You must be signed in to change notification settings - Fork 7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Replace return variable with its destination. #67
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -117,6 +117,10 @@ impl TirTrace { | |
} | ||
}; | ||
|
||
// Initialise VarRenamer's accumulator (and thus also set the first offset) to the | ||
// traces most outer number of locals. | ||
rnm.init_acc(body.num_locals); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. would this be better as an argument to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The problem is when we create There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see. Never mind then. |
||
|
||
// When adding statements to the trace, we clone them (rather than referencing the | ||
// statements in the SIR) so that we have the freedom to mutate them later. | ||
let user_bb_idx_usize = usize::try_from(loc.bb_idx).unwrap(); | ||
|
@@ -158,24 +162,24 @@ impl TirTrace { | |
} => { | ||
if let Some(callee_sym) = op.symbol() { | ||
// We know the symbol name of the callee at least. | ||
let op = if SIR.bodies.contains_key(callee_sym) { | ||
let op = if let Some(callbody) = SIR.bodies.get(callee_sym) { | ||
// We have SIR for the callee, so it will appear inlined in the trace | ||
// and we only need to emit Enter/Leave statements. | ||
|
||
// Rename the destination if there is one. | ||
let newdest = dest | ||
.as_ref() | ||
.map(|(ret_val, _ret_bb)| rnm.rename_place(&ret_val)); | ||
// Rename all `Local`s within the arguments. | ||
let newargs = rnm.rename_args(&args); | ||
// Inform VarRenamer about this function's offset, which is equal to the | ||
// number of variables assigned in the outer body. | ||
rnm.enter(body.num_locals); | ||
// FIXME It seems that calls always have a destination despite it being | ||
// an `Option`. If this is not always the case, we may want add the | ||
// `Local` offset (`var_len`) to this statement so we can assign the | ||
// arguments to the correct `Local`s during trace compilation. | ||
assert!(newdest.is_some()); | ||
// Rename all `Local`s within the arguments. | ||
let newargs = rnm.rename_args(&args); | ||
// Inform VarRenamer about this function's offset, which is equal to the | ||
// number of variables assigned in the outer body. | ||
rnm.enter(callbody.num_locals, newdest.as_ref().unwrap().clone()); | ||
TirOp::Statement(Statement::Enter( | ||
op.clone(), | ||
newargs, | ||
|
@@ -313,33 +317,53 @@ struct VarRenamer { | |
/// restored again after leaving that call. | ||
stack: Vec<u32>, | ||
/// Current offset used to rename variables. | ||
offset: u32 | ||
offset: u32, | ||
/// Accumulator keeping track of total number of variables used. Needed to use different | ||
/// offsets for consecutive inlined function calls. | ||
acc: Option<u32>, | ||
/// Stores the return variables of inlined function calls. Used to replace `$0` during | ||
/// renaming. | ||
returns: Vec<Place> | ||
} | ||
|
||
impl VarRenamer { | ||
fn new() -> Self { | ||
VarRenamer { | ||
stack: vec![0], | ||
offset: 0 | ||
offset: 0, | ||
acc: None, | ||
returns: Vec::new() | ||
} | ||
} | ||
|
||
fn offset(&self) -> u32 { | ||
self.offset | ||
} | ||
|
||
fn enter(&mut self, num_locals: usize) { | ||
// When entering an inlined function call update the current offset by adding the number of | ||
// assigned variables in the outer context. Also add this offset to the stack, so we can | ||
// restore it once we leave the inlined function call again. | ||
self.offset += num_locals as u32; | ||
fn init_acc(&mut self, num_locals: usize) { | ||
if self.acc.is_none() { | ||
self.acc.replace(num_locals as u32); | ||
} | ||
} | ||
|
||
fn enter(&mut self, num_locals: usize, dest: Place) { | ||
// When entering an inlined function call set the offset to the current accumulator. Then | ||
// increment the accumulator by the number of locals in the current function. Also add the | ||
// offset to the stack, so we can restore it once we leave the inlined function call again. | ||
self.offset = self.acc.unwrap(); | ||
self.stack.push(self.offset); | ||
match self.acc.as_mut() { | ||
Some(v) => *v += num_locals as u32, | ||
None => {} | ||
} | ||
self.returns.push(dest); | ||
} | ||
|
||
fn leave(&mut self) { | ||
// When we leave an inlined function call, we pop the previous offset from the stack, | ||
// reverting the offset to what it was before the function was entered. | ||
self.stack.pop(); | ||
self.returns.pop(); | ||
if let Some(v) = self.stack.last() { | ||
self.offset = *v; | ||
} else { | ||
|
@@ -380,8 +404,14 @@ impl VarRenamer { | |
|
||
fn rename_place(&self, place: &Place) -> Place { | ||
if &place.local == &Local(0) { | ||
// Local(0) is used for returning values from calls, so it doesn't need to be renamed. | ||
place.clone() | ||
// Replace the default return variable $0 with the variable in the outer context where | ||
// the return value will end up after leaving the function. This saves us an | ||
// instruction when we compile the trace. | ||
if let Some(v) = self.returns.last() { | ||
v.clone() | ||
} else { | ||
panic!("Expected return value!") | ||
} | ||
} else { | ||
let mut p = place.clone(); | ||
p.local = self.rename_local(&p.local); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Gross hack, but OK to get us going ;)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed. It'll be a while before we won't need this workaround anymore.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah! In fact I need this for my non-inline call work, which -- like for inlined calls -- puts the return value in the destination register, which may not be rax.