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

Cleanup old trans #38302

Merged
merged 103 commits into from
Dec 21, 2016
Merged

Cleanup old trans #38302

merged 103 commits into from
Dec 21, 2016

Conversation

Mark-Simulacrum
Copy link
Member

@Mark-Simulacrum Mark-Simulacrum commented Dec 11, 2016

This is a cleanup of old trans, with the following main points:

  • Remove the build.rs API (prefer using Builder directly, which is now passed where needed through BlockAndBuilder).
  • Remove Block (inlining it into BlockAndBuilder)
  • Remove Callee::call, primarily through inlining and simplification of code.
  • Thinned FunctionContext:
    • mir, debug_scopes, scopes, and fn_ty are moved to MirContext.
    • param_env is moved to SharedCrateContext and renamed to empty_param_env.
    • llretslotptr is removed, replaced with more careful management of the return values in calls.
    • landingpad_alloca is inlined into cleanup.
    • param_substs are moved to MirContext.
    • span is removed, it was never set to anything but None.
    • block_arena and lpad_arena are removed, since neither was necessary (landing pads and block are quite small, and neither needs arena allocation).
  • Fixed drop_in_place not running other destructors in the same function.

Fixes #35566 (thanks to @est31 for confirming).

@arielb1
Copy link
Contributor

arielb1 commented Dec 11, 2016

Nice! You don't have to wait for my shim-MIR PR - I can handle that.

val: MaybeSizedValue, discr: Disr, ix: usize) -> ValueRef {
trans_field_ptr_builder(&bcx.build(), t, val, discr, ix)
trans_field_ptr_builder(bcx, t, val, discr, ix)
Copy link
Contributor

Choose a reason for hiding this comment

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

IIUC this function is redundant now. Is cleaning that up in scope for this PR?

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, I also commented on it, there's a bunch like this.

@nagisa
Copy link
Member

nagisa commented Dec 11, 2016

I will want to look over the PR later.

Copy link
Member

@eddyb eddyb left a comment

Choose a reason for hiding this comment

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

Great work! Most of changes I have in mind are further cleanup (fully removing the global builder and its debuginfo machinery, _builder fn variants, Block etc.).

@@ -327,6 +327,8 @@ language_item_table! {
PanicBoundsCheckFnLangItem, "panic_bounds_check", panic_bounds_check_fn;
PanicFmtLangItem, "panic_fmt", panic_fmt;

// ExchangeMallocFnLangItem cannot unwind, or MIR trans will break. See note
// on `malloc_raw_dyn` in librustc_trans/base.rs.
Copy link
Member

Choose a reason for hiding this comment

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

What I meant was actually to add this at the implementation site in liballoc, in case someone changes it.

Copy link
Member

Choose a reason for hiding this comment

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

Good to put this into liballoc. Is this a new requirement, or just a new comment? Since we know the discussion about panic on oom will come back.

Copy link
Member

Choose a reason for hiding this comment

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

If we ever decide to allow allocation to panic, we’ll have to change trans. Not a hard requirement, but not a comment either.

Copy link
Member Author

Choose a reason for hiding this comment

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

Hmm, actually it looks like it's possible to make this panic friendly; but I may be wrong. @eddyb what we need for that is ownership of BAB, right? We have that, so if that's what we need, I can "fix" this.

Copy link
Member

@nagisa nagisa Dec 12, 2016

Choose a reason for hiding this comment

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

You can simply change how MIR for allocation is generated. No need to fiddle in trans… unless there’s some old cruft in old trans that still generates calls to this itself… in which case you’ll have a hard and bad time fixing this :)

Copy link
Member Author

Choose a reason for hiding this comment

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

No, this is only called from MIR. In fact, the actual function should be inlined.

val: MaybeSizedValue, discr: Disr, ix: usize) -> ValueRef {
trans_field_ptr_builder(&bcx.build(), t, val, discr, ix)
trans_field_ptr_builder(bcx, t, val, discr, ix)
Copy link
Member

Choose a reason for hiding this comment

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

You can change trans_field_ptr_builder to trans_field_ptr, including its signature. The _builder split was for the old B(.

pub fn load_ty<'blk, 'tcx>(
cx: &BlockAndBuilder<'blk, 'tcx>, ptr: ValueRef, t: Ty<'tcx>
) -> ValueRef {
load_ty_builder(cx, ptr, t)
Copy link
Member

Choose a reason for hiding this comment

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

Same thing here, load_ty_builder should not exist anymore.

}

load_fat_ptr_builder(&B(cx), src, ty)
load_fat_ptr_builder(cx, src, ty)
Copy link
Member

Choose a reason for hiding this comment

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

Same _builder cleanup needed.

Lifetime::End.call(&bcx.build(), ptr);
}
pub fn call_lifetime_end(bcx: &BlockAndBuilder, ptr: ValueRef) {
Lifetime::End.call(bcx, ptr);
Copy link
Member

Choose a reason for hiding this comment

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

You can probably inline these 2 at their callsites now.

}

let llret_ty = type_of::type_of(ccx, ret_ty);

let simple = get_simple_intrinsic(ccx, name);
let llval = match (simple, name) {
(Some(llfn), _) => {
Call(bcx, llfn, &llargs, call_debug_location)
bcx.call(llfn, &llargs, bcx.lpad().and_then(|b| b.bundle()))
Copy link
Member

@eddyb eddyb Dec 11, 2016

Choose a reason for hiding this comment

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

Oh, everywhere you have a call that's not to drop glue or box_free you can just put None instead of bcx.lpad().and_then(|b| b.bundle()) - the latter is for cleanup blocks (unwinding landing pads) which are restricted in MIR, e.g. they can't just have arbitrary function calls like intrinsics.

return;
}

let ptr = self.trans_operand(&bcx, &args[0]);
Copy link
Member

Choose a reason for hiding this comment

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

Extraneous space after =.

}

pub fn store_operand_direct(&mut self,
bcx: Block<'bcx, 'tcx>,
bcx: &BlockAndBuilder<'bcx, 'tcx>,
Copy link
Member

Choose a reason for hiding this comment

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

Rename this function to store_operand.

llval = Some(val);
bcx
});
let val = base::malloc_raw_dyn(&bcx, llty_ptr, box_ty, llsize, llalign);
Copy link
Member

Choose a reason for hiding this comment

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

Is this the only call? Because then you could inline it.

AddIncomingToPhi(current, next, body_bcx.llbb);
Br(body_bcx, header_bcx.llbb, DebugLoc::None);
// FIXME(simulacrum): The code below is identical to the closure (add) above, but using the
// closure doesn't compile due to body_bcx still being borrowed when dropped.
Copy link
Member

Choose a reason for hiding this comment

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

Is this due to over-zealous inference 😞?

@est31
Copy link
Member

est31 commented Dec 11, 2016

Let me test whether this fixes issue 35566...

@est31
Copy link
Member

est31 commented Dec 11, 2016

Nope, #35566 is still not fixed.

@eddyb
Copy link
Member

eddyb commented Dec 11, 2016

Update: looks like @est31 was hitting the bug at stage0, which ofc is not fixed. I've suggested a manual way to test that the bug is indeed fixed at stage1 and later.

@est31
Copy link
Member

est31 commented Dec 11, 2016

Now it fails when compiling stage1 std artifacts:

Basic Block in function '_ZN4core3num7flt2dec15to_shortest_str17hd586f26308963365E' does not have terminator!
label %bb1
LLVM ERROR: Broken function found, compilation aborted!
Build failed, waiting for other jobs to finish...
error: Could not compile `core`.

To learn more, run the command again with --verbose.

@bluss
Copy link
Member

bluss commented Dec 12, 2016

About the drop_in_place change, is this an example of a program that changes behaviour? Just to understand. Current stable/beta/nightly doesn't run the "B" drop after the panic of "A".

playground

struct PrintDrop(&'static str);

impl Drop for PrintDrop {
    fn drop(&mut self) {
        println!("Dropping {}", self.0);
        if self.0 == "A" {
            panic!();
        }
    }
}

use std::mem::forget;
use std::ptr::drop_in_place;

fn main() {
    let mut x = (PrintDrop("A"), PrintDrop("B"));
    unsafe {
        drop_in_place(&mut x);
        forget(x);
    }
}

@Mark-Simulacrum
Copy link
Member Author

The example @eddyb gave me for the drop_in_place change: https://is.gd/i42PKb vs https://is.gd/UfTJjA. The first one panics only once because it uses drop_in_place, while the second panics twice, since it doesn't utilize drop_in_place. I believe your example is equivalent to this, though.

@@ -314,18 +312,109 @@ pub struct FunctionContext<'a, 'tcx: 'a> {

// Used and maintained by the debuginfo module.
pub debug_context: debuginfo::FunctionDebugContext,

owned_builder: OwnedBuilder<'a, 'tcx>,
Copy link
Member

Choose a reason for hiding this comment

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

You should probably name this alloca_builder.

@@ -303,9 +304,6 @@ pub struct FunctionContext<'a, 'tcx: 'a> {
// error reporting and symbol generation.
pub span: Option<Span>,

// The arena that blocks are allocated from.
//pub block_arena: &'a TypedArena<BlockS<'a, 'tcx>>,

// The arena that landing pads are allocated from.
pub funclet_arena: TypedArena<Funclet>,
Copy link
Member

Choose a reason for hiding this comment

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

Still need to kill this.

// This is later removed in FunctionContext::cleanup.
self.alloca_insert_pt.set(Some(unsafe {
entry_bcx.load(C_null(Type::i8p(self.ccx)));
llvm::LLVMGetFirstInstruction(entry_bcx.llbb())
Copy link
Member

Choose a reason for hiding this comment

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

I don't think the LLVMGetFirstInstruction call is needed and you can just the result of the .load(...).

Copy link
Member Author

Choose a reason for hiding this comment

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

The load returns the ValueRef for the first instruction?

Copy link
Member

Choose a reason for hiding this comment

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

The load is the first instruction.


self.owned_builder.builder.position_at_start(entry_bcx.llbb());

if !self.fn_ty.ret.is_ignore() && !skip_retptr {
Copy link
Member

Choose a reason for hiding this comment

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

Oh this is weird, I wonder if this should maybe be inlined.

let slot = if self.fn_ty.ret.is_indirect() {
get_param(self.llfn, 0)
} else {
self.alloca(llty, "sret_slot")
Copy link
Member

Choose a reason for hiding this comment

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

llty can be computed on this branch alone. Which IMO should not exist anymore.

let b = self.ccx.builder();
b.position_before(self.alloca_insert_pt.get().unwrap());
b.alloca(ty, name)
self.owned_builder.builder.alloca(ty, name)
Copy link
Member

Choose a reason for hiding this comment

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

Should rename the method on Builder to dynamic_alloca or something.

pub fn builder<'a>(&'a self) -> Builder<'a, 'tcx> {
Builder::new(self)
}

pub fn raw_builder<'a>(&'a self) -> BuilderRef {
self.local().builder.b
Copy link
Member

Choose a reason for hiding this comment

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

Looks like the underlying builder is not exactly gone.

Copy link
Member

Choose a reason for hiding this comment

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

Also OwnedBuilder should be the only Builder, i.e. the latter should not be a distinct type.

mir: mir,
llfn: llfndecl,
llretslotptr: Cell::new(None),
param_env: ccx.tcx().empty_parameter_environment(),
Copy link
Member

Choose a reason for hiding this comment

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

Is this used at all anymore?

landingpad_alloca: Cell::new(None),
fn_ty: fn_ty,
param_substs: param_substs,
span: None,
Copy link
Member

Choose a reason for hiding this comment

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

... or this?

@Mark-Simulacrum Mark-Simulacrum force-pushed the trans-cleanup branch 3 times, most recently from f02257a to bcfe02d Compare December 16, 2016 21:02
// have been instructed to skip it for immediate return
// values, or there is nothing to return at all.
// This is later removed in the drop of FunctionContext.
fcx.alloca_insert_pt = Some(val);
Copy link
Member

Choose a reason for hiding this comment

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

Can you avoid having two variables named val, i.e. can you move this up a bit more? Maybe rename it to dummy.

@Mark-Simulacrum Mark-Simulacrum force-pushed the trans-cleanup branch 2 times, most recently from 5a2f7d8 to 11fb0a1 Compare December 17, 2016 03:09
@Mark-Simulacrum Mark-Simulacrum changed the title Remove build.rs from trans Cleanup old trans Dec 17, 2016
let mir = fcx.mir();
pub fn create_mir_scopes<'tcx>(
fcx: &FunctionContext,
mir: Ref<'tcx, Mir<'tcx>>,
Copy link
Member

Choose a reason for hiding this comment

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

Can this be a regular reference?

pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) {
pub fn trans_mir<'blk, 'tcx: 'blk>(
fcx: &'blk FunctionContext<'blk, 'tcx>,
mir: Ref<'tcx, Mir<'tcx>>
Copy link
Member

Choose a reason for hiding this comment

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

Can this get away with being a reference?


/// Access a field, at a point when the value's case is known.
pub fn trans_field_ptr_builder<'blk, 'tcx>(bcx: &BlockAndBuilder<'blk, 'tcx>,
pub fn trans_field_ptr<'blk, 'tcx>(bcx: &BlockAndBuilder<'blk, 'tcx>,
t: Ty<'tcx>,
val: MaybeSizedValue,
discr: Disr, ix: usize)
-> ValueRef {
Copy link
Member

Choose a reason for hiding this comment

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

Indentation is off here.

DebugLoc::None.apply(self);
self.cleanup();
pub fn finish(&'blk self, ret_cx: &BlockAndBuilder<'blk, 'tcx>) {
self.build_return_block(ret_cx);
Copy link
Member

Choose a reason for hiding this comment

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

This is just an alias for build_return_block and the latter should be inlined.
In some cases there is no return value, in the rest the ABI should match so it's just a matter of passing a return pointer in where applicable and returning the result of the call elsewhere.

Copy link
Member

Choose a reason for hiding this comment

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

Looked again and I think trans_ctor_shim is the only place that needs an alloca for the return value, sometimes (and then only a few cases in build_return_block apply).

}
(_, None) => {
let retval = if llty == Type::i1(self.ccx) {
let val = LoadRangeAssert(ret_cx, retslot, 0, 2, llvm::False);
Trunc(ret_cx, val, llty)
let val = ret_cx.load_range_assert(retslot, 0, 2, llvm::False);
Copy link
Member

Choose a reason for hiding this comment

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

Yeah, all of this should be unnecessary, it'd be a waste of time to actually write something to an alloca and read it later.

-> Result<'blk, 'tcx> {
trans_call_inner(bcx, debug_loc, self, args, dest)
dest: Option<ValueRef>,
lpad: Option<&'blk llvm::OperandBundleDef>) {
Copy link
Member

Choose a reason for hiding this comment

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

Something tells me all remaining uses have None for lpad. Also, trans_call_inner can be inlined.

Copy link
Member

Choose a reason for hiding this comment

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

Wait, that's poorly named, it should be cleanup_bundle.

opt_llretslot: Option<ValueRef>)
-> Result<'blk, 'tcx> {
dest: Option<ValueRef>,
lpad: Option<&'blk llvm::OperandBundleDef>) {
// Introduce a temporary cleanup scope that will contain cleanups
// for the arguments while they are being evaluated. The purpose
// this cleanup is to ensure that, should a panic occur while
// evaluating argument N, the values for arguments 0...N-1 are all
// cleaned up. If no panic occurs, the values are handed off to
// the callee, and hence none of the cleanups in this temporary
// scope will ever execute.
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 outdated heh.

Call(bcx, llfn, &[], call_debug_location);
Unreachable(bcx);
return Result::new(bcx, C_undef(Type::nil(ccx).ptr_to()));
bcx.call(llfn, &[], None);
Copy link
Member

Choose a reason for hiding this comment

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

I think this can be folded into the get_simple_intrinsic use below.

ret_bcx.at_start(|ret_bcx| {
debug_loc.apply_to_bcx(ret_bcx);
bcx.set_source_location(scope, debug_span);
Copy link
Member

Choose a reason for hiding this comment

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

Needs to be ret_bcx.set_source_location(...).

let bcx = self.bcx(bb);
pub fn init_cpad(&mut self, bb: mir::BasicBlock,
funclets: &mut IndexVec<mir::BasicBlock, Option<Funclet>>) {
let bcx = self.build_block(bb);
Copy link
Member

Choose a reason for hiding this comment

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

This function can probably just iterate over cleanup_kinds to produce funclets, without the RPO visit order (as CleanupKind::Internal doesn't need to reference a different functlets entry).

@@ -290,32 +278,32 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) {

let mut rpo = traversal::reverse_postorder(&mir);

let mut funclets: IndexVec<mir::BasicBlock, Option<Funclet>> =
IndexVec::from_elem(None, mir.basic_blocks());

// Prepare each block for translation.
for (bb, _) in rpo.by_ref() {
Copy link
Member

Choose a reason for hiding this comment

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

This is what I mean, the order is irrelevant here now.

Br(body_bcx, header_bcx.llbb, DebugLoc::None);
f(&body_bcx, if zst { data_ptr } else { current });
let next = add(&body_bcx, current, C_uint(bcx.ccx(), 1usize));
body_bcx.add_incoming_to_phi(current, next, body_bcx.llbb());
Copy link
Member

Choose a reason for hiding this comment

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

This is mildly confusing, heh, it'd make more sense as header_bcx.add_incoming_to_phi(...).

@bors
Copy link
Contributor

bors commented Dec 18, 2016

☔ The latest upstream changes (presumably #37429) made this pull request unmergeable. Please resolve the merge conflicts.


if fn_ret.0.is_never() {
bcx.unreachable();
}
Copy link
Member

Choose a reason for hiding this comment

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

This can't ever be the case.

InternalDebugLocation::new(scope, loc.line, loc.col.to_usize())
} else {
UnknownLocation
};
set_debug_location(fcx.ccx, builder, dbg_loc);
set_debug_location(mir.ccx(), builder, dbg_loc);
Copy link
Member

Choose a reason for hiding this comment

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

FWIW, here and a few times above, builder has a ccx of its own. trans::debuginfo should maybe take just debug_context, not the whole mir context.

mir: &'a mir::Mir<'tcx>,
pub mir: &'a mir::Mir<'tcx>,

pub debug_context: debuginfo::FunctionDebugContext,
Copy link
Member

Choose a reason for hiding this comment

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

Yeah I'd rather these not be made public.

@Mark-Simulacrum
Copy link
Member Author

Rebased.

@eddyb
Copy link
Member

eddyb commented Dec 21, 2016

@bors r+

@bors
Copy link
Contributor

bors commented Dec 21, 2016

📌 Commit 0013d4c has been approved by eddyb

@bors
Copy link
Contributor

bors commented Dec 21, 2016

⌛ Testing commit 0013d4c with merge 1b38776...

bors added a commit that referenced this pull request Dec 21, 2016
Cleanup old trans

This is a cleanup of old trans, with the following main points:
 - Remove the `build.rs` API (prefer using `Builder` directly, which is now passed where needed through `BlockAndBuilder`).
 - Remove `Block` (inlining it into `BlockAndBuilder`)
 - Remove `Callee::call`, primarily through inlining and simplification of code.
 - Thinned `FunctionContext`:
   - `mir`, `debug_scopes`, `scopes`, and `fn_ty` are moved to `MirContext`.
   - `param_env` is moved to `SharedCrateContext` and renamed to `empty_param_env`.
   - `llretslotptr` is removed, replaced with more careful management of the return values in calls.
   - `landingpad_alloca` is inlined into cleanup.
   - `param_substs` are moved to `MirContext`.
   - `span` is removed, it was never set to anything but `None`.
   - `block_arena` and `lpad_arena` are removed, since neither was necessary (landing pads and block are quite small, and neither needs arena allocation).
 - Fixed `drop_in_place` not running other destructors in the same function.

Fixes #35566 (thanks to @est31 for confirming).
@bors bors merged commit 0013d4c into rust-lang:master Dec 21, 2016
@Mark-Simulacrum Mark-Simulacrum deleted the trans-cleanup branch December 27, 2016 00:26
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.

9 participants