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

Track frame layout changes. #679

Closed

Conversation

yurydelendik
Copy link
Collaborator

Adds basic frame layout (or unwinding) capabilities to the x86 abi.

@bjorn3
Copy link
Contributor

bjorn3 commented Feb 21, 2019

Using .debug_frame will hopefully fix gdb backtraces in debug mode. (gdb gets confused by rex push rbp https://github.com/bjorn3/rustc_codegen_cranelift/issues/146#issuecomment-449474527)

@lachlansneff
Copy link
Contributor

It'd be useful to generate the .eh_frame and equivalent sections so that we can use the system unwinder.

cranelift-codegen/src/ir/function.rs Outdated Show resolved Hide resolved
cranelift-codegen/src/ir/function.rs Outdated Show resolved Hide resolved
cranelift-codegen/src/isa/x86/abi.rs Show resolved Hide resolved
@yurydelendik yurydelendik force-pushed the frame-layout branch 5 times, most recently from f1d8669 to 3fb342b Compare April 9, 2019 16:08
@philipc
Copy link
Contributor

philipc commented Apr 20, 2019

It'd be useful to generate the .eh_frame

I've implemented write support for .eh_frame in gimli (gimli-rs/gimli#419). I expect that it should require no changes to this PR, and only a minor change to the wasmtime PR, but I haven't tested.

Copy link
Member

@bnjbvr bnjbvr left a comment

Choose a reason for hiding this comment

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

A few high-level questions below, to clarify the purpose of this PR. Thanks!

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;
Copy link
Member

Choose a reason for hiding this comment

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

Are we sure no compiler pass won't add more instructions in between? (in particular thinking about register allocation which could insert copies/fills, or constant pools being generated, whenever we have them?)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

the prologue_epilogue runs after regalloc but before shrink_instructions, relax_branches -- I hope the latter does not insert anything to change stack layout.

Copy link
Member

Choose a reason for hiding this comment

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

Right. There should be @julian-seward1 's late stage optimizations happening after that (and they can remove instructions), so we should be careful to put this somewhere the data flow graph can't change (i.e. at the very end).

cranelift-codegen/src/isa/x86/abi.rs Outdated Show resolved Hide resolved
cranelift-codegen/src/ir/framelayout.rs Show resolved Hide resolved
cranelift-codegen/src/ir/framelayout.rs Outdated Show resolved Hide resolved
pos.func.frame_layout.instructions.insert(
reg_push_inst,
vec![FrameLayoutChange::RegAt {
reg: reg,
Copy link
Member

Choose a reason for hiding this comment

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

Are we interested in the position of every register? (I guess so, if I understand correctly this is for tracking where local variables are.) If so, should every instruction in the function's body also have its own frame layout changes (in case regalloc moves a variable from one register to another)?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

With this PR (and .debug_frame) we are interested in basics: proper frame unwinding (i.e. BP, SP, IP) and maybe registers used for args. (The tracking of original variables values is happening somewhere else).

cranelift-codegen/src/ir/function.rs Outdated Show resolved Hide resolved
cranelift-codegen/src/ir/framelayout.rs Outdated Show resolved Hide resolved
@bnjbvr
Copy link
Member

bnjbvr commented Jul 10, 2019

Deferring to @sunfishcode, since my understanding is that this is mostly for wasmtime.

@bjorn3
Copy link
Contributor

bjorn3 commented Jul 10, 2019

This is useful for cg_clif for debuginfo and unwinding too.

@sunfishcode
Copy link
Member

I think this patch looks good overall, though @iximeow's comment looks worth addressing.

@iximeow
Copy link
Collaborator

iximeow commented Sep 24, 2019

Just a note: the above merged PR adds frame changes to track the last few instructions as we exit functions on x86. I'd forgotten when commenting but we can have multiple returns, so it's a little more complex than I'd initially thought. I'm not sure how to get multi-return sys-v functions out of Cranelift to test, but to my understanding of the spec (and a comparison against gcc) it looks correct.

So much as my word matters here: these changes look ✔️

cranelift-codegen/src/ir/framelayout.rs Outdated Show resolved Hide resolved
cranelift-codegen/src/ir/framelayout.rs Outdated Show resolved Hide resolved
cranelift-codegen/src/isa/x86/abi.rs Show resolved Hide resolved
cranelift-codegen/src/isa/x86/abi.rs Outdated Show resolved Hide resolved
cranelift-codegen/src/isa/x86/abi.rs Outdated Show resolved Hide resolved
cranelift-codegen/src/isa/x86/abi.rs Outdated Show resolved Hide resolved
@iximeow
Copy link
Collaborator

iximeow commented Oct 14, 2019

I just realized that I tried to re-request a review on this a few weeks ago, but it didn't go through. Maybe I can't re-request reviews for other folks' PRs? Anyway, if you have a moment @bjorn3 I'd like to get this one sorted out and resume getting #902 ship-shape soon.

.and_modify(|insts| {
*insts = insts
.into_iter()
.map(|x| *x)
Copy link
Contributor

Choose a reason for hiding this comment

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

.cloned()

@bjorn3
Copy link
Contributor

bjorn3 commented Oct 14, 2019

@iximeow I am not a member of @CraneStation.

@iximeow
Copy link
Collaborator

iximeow commented Oct 14, 2019

oh whoops, I'd thought you were. Sorry about that!

@bnjbvr
Copy link
Member

bnjbvr commented Oct 21, 2019

Hi! Just as a preamble to the review, can you make sure this commit doesn't introduce merge commits please? (Rebasing should work just fine.)

@bnjbvr
Copy link
Member

bnjbvr commented Oct 22, 2019

@yurydelendik Hey, can you help with this rebase, please? (Once that's done, I'm happy to review this.)

@yurydelendik
Copy link
Collaborator Author

can you help with this rebase

rebased all commits

Copy link
Member

@bnjbvr bnjbvr left a comment

Choose a reason for hiding this comment

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

A few suggestions below, and a few questions about logic too. It should be pretty much ready to land after this. (I think we'll eventually squash commits on merge in this PR, since each commit tends to modify the behavior of its predecessors.)

#[cfg(not(feature = "std"))]
use crate::HashMap;
#[cfg(feature = "std")]
use std::collections::HashMap;
Copy link
Member

Choose a reason for hiding this comment

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

nit: we don't need to guard against the std feature here and we can just use crate::HashMap;, right?

/// Change in the frame layout information.
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub enum FrameLayoutChange {
/// Base CFA pointer moved to different register/offset.
Copy link
Member

Choose a reason for hiding this comment

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

Even if it's obvious from context here, would be nice to explicit CFA in this file at least once.

/// The entire frame layout must be preserved somewhere to be restored at a corresponding
/// `Restore` change.
///
/// This likely maps to the DWARF call frame instruction `.cfa_remember_state`
Copy link
Member

Choose a reason for hiding this comment

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

nit: can you end this sentence with a dot please? (ditto below for restore)

@@ -371,6 +372,29 @@ fn baldrdash_prologue_epilogue(func: &mut ir::Function, isa: &dyn TargetIsa) ->
Ok(())
}

/// CFAState is `cranelift`'s model of the call frame layout at any particular point in a function.
Copy link
Member

Choose a reason for hiding this comment

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

It'd be nice to explicit the CFA acronym once in this file too.

cf_ptr_offset: isize,
/// The offset between the start of the call frame and the current stack pointer. This is
/// primarily useful to point to where on the stack preserved registers are, but is maintained
/// through the whole function for consistency.
Copy link
Member

Choose a reason for hiding this comment

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

This comment is slightly lying :) It is only preserved in the prologue and epilogue, as far as I can tell, not in the function's body itself. It would be nice to reword it a bit.

Copy link
Contributor

Choose a reason for hiding this comment

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

We dont have alloca support, so both esp and ebp arent modified between prologue and epilogue, right?

Copy link
Member

Choose a reason for hiding this comment

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

I was thinking about regalloc's spills, but maybe regalloc just mandates a specific amount of spill slots, and something else allocates them in advance once and for all?

Copy link
Contributor

Choose a reason for hiding this comment

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

Spill slots are a kind of stack slots. I believe they get allocated together with the rest of the stack slots in prologue.

Copy link
Member

Choose a reason for hiding this comment

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

And i imagine the emergency spill slot belongs to that group? In this case, current comment is fine.

Copy link
Contributor

Choose a reason for hiding this comment

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

}
}
}
}

/// 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.
Copy link
Member

Choose a reason for hiding this comment

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

nit: outdated comment, can be removed I think?

cranelift-codegen/src/isa/x86/abi.rs Show resolved Hide resolved
if stack_size > 0 {
pos.ins().adjust_sp_up_imm(Imm64::new(stack_size));
}

// Pop all the callee-saved registers, stepping backward each time to
// preserve the correct order.
let fp_ret = pos.ins().x86_pop(reg_type);
// account for CFA state in the reverse of `insert_common_prologue`.
Copy link
Member

Choose a reason for hiding this comment

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

nit: for all the comments here, please capitalize the first letter.

.into_iter()
.map(|x| *x)
.chain(std::iter::once(new_cfa))
.collect::<Box<[_]>>();
Copy link
Member

Choose a reason for hiding this comment

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

Any reason we don't use a vec! for the changes? We could then use push here.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

That was based on the fact that FrameLayoutChanges is a Box<[_]> and not a Vec<_>. Using Vec<_> for FrameLayoutChanges and then using pop would be faster. I didn't actually look why there was a Box<[_]> in the first place when writing that comment.

Copy link
Member

Choose a reason for hiding this comment

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

Ok, we could use a Vec here, or keep the Box. Not sure it's very performance-sensitive since it's tied to debug collection mode.

Copy link
Contributor

Choose a reason for hiding this comment

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

Not sure it's very performance-sensitive since it's tied to debug collection mode.

It would be perf sensitive for cg_clif once I add unwinding support based on this.

cfa_state.current_depth += word_size;
cfa_state.cf_ptr_offset -= word_size;
// and now that we're going to overwrite `rbp`, `rsp` is the only way to get to the call frame.
cfa_state.cf_ptr_reg = RU::rsp as RegUnit;
Copy link
Member

Choose a reason for hiding this comment

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

Don't we need to insert the layout changes after this instruction?

Copy link
Collaborator

Choose a reason for hiding this comment

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

@iximeow
Copy link
Collaborator

iximeow commented Nov 5, 2019

@yurydelendik will you have time to address the comments here or should I do so and PR into your fork again?

@yurydelendik
Copy link
Collaborator Author

@yurydelendik will you have time to address the comments here or should I do so and PR into your fork again?

@iximeow at this moment, I lost track on what is going on with this PR. It will take me some time to address the review comments. If you can address the comments, which will be awesome, I recommend to just submit your fork's branch as a new/different PR -- and I'll close this one.

@iximeow
Copy link
Collaborator

iximeow commented Nov 5, 2019

I'll open a new PR and address the remaining comments there. Thanks!

@iximeow iximeow mentioned this pull request Nov 6, 2019
4 tasks
@bnjbvr
Copy link
Member

bnjbvr commented Nov 6, 2019

Thanks both! I'll close this one since work has been continued in #1204.

@bnjbvr bnjbvr closed this Nov 6, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants