Skip to content
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

Merged
merged 2 commits into from
Jun 1, 2020

Conversation

ptersilie
Copy link
Contributor

This change replaces 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.
More importantly however, this guards us against a future optimisation
in rustc that allows SIR to assign to $0 multiple times and at the
beginning of a block, which could lead to another function overwriting
its value (see rust-lang/rust#72205).

While we are here, also fix a bug in the variable renaming code.
Currently, functions that are called consecutively (i.e. they are not
nested) use the same offset to rename their variables (the offset of
their outer context), which leads to them sharing the same variable
names. By using an accumulator which is continuously increased to
calculate the offset, we make sure consecutive functions have increasing
variable names, even when after leaving a function the offset is
temporarily reset to that of the outer context.

@ptersilie
Copy link
Contributor Author

Last commit needs squashing.

@@ -125,8 +129,26 @@ impl TraceCompiler {
}
}

/// Returns the currently assigned register for a given `Local`. Similar to `local_to_reg` but
/// read-only, i.e. this function doesn't assign `Local`'s to registers.
fn get_reg(&self, local: &Local) -> Result<u8, CompileError> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAICS this function is dead, so we should (later) squash d3394dd into this commit?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see you already mentioned this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I noticed shortly after pushing, but didn't know if force pushing is okay if I haven't assigned anyone yet.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You did the right thing.

@@ -27,6 +27,8 @@ pub enum CompileError {
/// We ran out of registers.
/// In the long-run, when we have a proper register allocator, this won't be needed.
OutOfRegisters,
/// We tried to retrieve the register for a local which doesn't have a register assigned to it.
UnknownLocal,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shall we put the Local inside and report it when we crash?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just realised this isn't even used anymore, so I removed it in
61e680e.

@@ -96,8 +99,9 @@ pub struct TraceCompiler {
available_regs: Vec<u8>,
/// Maps locals to their assigned registers.
assigned_regs: HashMap<Local, u8>,
/// Stores the destination locals to which we copy RAX to after leaving an inlined call.
leaves: Vec<Option<Place>>,
/// Stores the destination local of the outer most function and moves its content into RAX at
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/outer most/outermost/

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in bff4293.

fn free_register(&mut self, local: &Local) {
if let Some(reg) = self.assigned_regs.remove(local) {
if local == &self.rtn_var.as_ref().unwrap().local {
// We currently assume that we only trace a single function which leaves its return
Copy link
Contributor

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 ;)

Copy link
Contributor Author

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.

Copy link
Contributor

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.

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. More importantly however, this guards us
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd kill the "More importantly however, this guards us against a future optimisation..." bit and just leave it for the commit message. The future will soon catch up with us and the comment will go stale.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 35abcb1

@@ -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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would this be better as an argument to VarRename::new()? It's only done once?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem is when we create VarRename we don't have access to body yet (at least not without jumping through some hoops).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. Never mind then.

@@ -314,6 +318,11 @@ struct VarRenamer {
stack: Vec<u32>,
/// Current offset used to rename variables.
offset: u32,
/// Accumulator keeping track of total amount of variables used. Needed to use different
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this would read better as "Accumulator keeping track of the total number of variables used"

I might be wrong, but "amount" feels like it implies an uncountable quantity?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 61cadb7

// restore it once we leave the inlined function call again.
self.offset += num_locals as u32;
// When entering an inlined function call set the offset to the current accumulator. Then
// increase the accumulator with the number of locals in the current function. Also add the
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor wording tweak:
"increase the accumulator with the number of locals" -> "increment the accumulator by the number of locals"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 61cadb7

@vext01
Copy link
Contributor

vext01 commented May 29, 2020

Just a handful of small comments from me.

@ptersilie
Copy link
Contributor Author

Think I have addressed all comments.

@vext01
Copy link
Contributor

vext01 commented May 30, 2020

So UnknownLocal was in fact unused?

@ptersilie
Copy link
Contributor Author

It was used in get_reg which I removed since it wasn't needed anymore. I just missed that the error was only needed for that.

@vext01
Copy link
Contributor

vext01 commented Jun 1, 2020

OK. Please squash.

This change replaces 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.
More importantly however, this guards us against a future optimisation
in rustc that allows SIR to assign to $0 multiple times and at the
beginning of a block, which could lead to another function overwriting
its value (see rust-lang/rust#72205).
Currently, functions that are called consecutively (i.e. they are not
nested) use the same offset to rename their variables (the offset of
their outer context), which leads to them sharing the same variable
names. By using an accumulator which is continuously increased to
calculate the offset, we make sure consecutive functions have increasing
variable names, even when after leaving a function the offset is
temporarily reset to that of the outer context.
@ptersilie
Copy link
Contributor Author

Squashed.

@vext01
Copy link
Contributor

vext01 commented Jun 1, 2020

bors r+

@bors
Copy link
Contributor

bors bot commented Jun 1, 2020

Build succeeded:

@bors bors bot merged commit 31f9aec into ykjit:master Jun 1, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants