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

Translate array drop glue using MIR #41917

Merged
merged 11 commits into from
May 28, 2017
Merged

Translate array drop glue using MIR #41917

merged 11 commits into from
May 28, 2017

Conversation

arielb1
Copy link
Contributor

@arielb1 arielb1 commented May 11, 2017

I was a bit lazy here and used a usize-based index instead of a pointer iteration. Do you think this is important @eddyb?

r? @eddyb

@eddyb
Copy link
Member

eddyb commented May 11, 2017

Can you check the optimized IR? Ideally we'd even have a test - I believe LLVM will turn it into a pointer loop.

@@ -578,6 +578,15 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
bug!("empty_substs_for_def_id: {:?} has type parameters", item_def_id)
})
}

pub fn const_usize(&self, val: usize) -> ConstInt {
Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't this usize be a u64 to be correct when compiling from 32 bit to 64?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In practice val is either 0 or 1.

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, it's probably ok then (still, it'd be nice to prevent misuses of this function)

Copy link
Member

Choose a reason for hiding this comment

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

u16 is probably a good option for the type then, so one can never pass a value too large.

@alexcrichton alexcrichton added the S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. label May 11, 2017
Copy link
Member

@nagisa nagisa 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 nits. I only skimmed translator changes, nothing obviously wrong there.

is_cleanup,
terminator: Some(Terminator {
source_info: self.source_info,
kind: TerminatorKind::Resume,
Copy link
Member

Choose a reason for hiding this comment

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

NIT: Unreachable here would make it much more obvious this terminator is patched out a bit later.

/// can_go = index < len
/// if can_go then drop-block else succ
/// drop-block:
/// ptr = &mut LV[len]
Copy link
Member

Choose a reason for hiding this comment

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

NIT: s/len/index/?


let is_cleanup = self.is_cleanup;
let succ = self.succ; // FIXME(#6393)
let loop_block = self.drop_loop(unwind, succ, index, length, ety, is_cleanup);
Copy link
Member

Choose a reason for hiding this comment

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

Is it guaranteed that is_cleanup is always false here? IIRC all the details related to this, it is guaranteed, in which this is_cleanup flag is redundant (unwind being None or Some already carries that information).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

is_cleanup is technically always false here because this can't be reached from "normal" drop elaboration, but if we fixed drop glue for arrays it would be true.

@@ -691,4 +808,12 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D>
let mir = self.elaborator.mir();
self.elaborator.patch().terminator_loc(mir, bb)
}

fn constant_usize(&self, val: usize) -> Operand<'tcx> {
Copy link
Member

Choose a reason for hiding this comment

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

Ditto here wrt usize/u16.


let is_cleanup = self.is_cleanup;
let succ = self.succ; // FIXME(#6393)
let loop_block = self.drop_loop(unwind, succ, index, length, ety, is_cleanup);
Copy link
Member

Choose a reason for hiding this comment

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

I'm worried about this code repeating itself for all used array lengths - would it be possible to have the array drop coerce to a *[T] and drop that? Or is thst premature optimization?

Copy link
Member

Choose a reason for hiding this comment

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

The code in question is fairly compact. I feel like LLVM would inline it in all cases anyway. The only really troublesome case I can imagine is somebody having a very large number of small arrays on stack.

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 more worried about us instantiating so many copies of pretty much identical IR. But thinking more about it, various lengths of fixed-length arrays are somewhat rare in practice, so this might not be a problem at all.

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 expect LLVM to always inline this anyway. Note that this is translated inline today.

@arielb1
Copy link
Contributor Author

arielb1 commented May 12, 2017

Can you check the optimized IR? Ideally we'd even have a test - I believe LLVM will turn it into a pointer loop.

Nope it doesn't. I'll try to write up pointer loops tomorrow.

@@ -323,7 +316,8 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D>
self.elaborator.field_subpath(self.path, Field::new(i)))
}).collect();

self.drop_ladder(fields).0
let (succ, unwind) = (self.succ, self.unwind); // FIXME(#6393)
Copy link
Member

Choose a reason for hiding this comment

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

The issue noted in the FIXME (#6393) is closed... should it be changed to a different issue? This is also the case in a few other places in the code.

Copy link
Member

@nagisa nagisa May 15, 2017

Choose a reason for hiding this comment

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

You can still easily track where to look from the comments in the issue (i.e. the niko’s in favour comment).

EDIT: nice trick. Amusing.

@nagisa
Copy link
Member

nagisa commented May 15, 2017

There’s some travis failures due to MIR variants being boxed now, @arielb1. You might want to rebase.

succ = self.drop_subpath(lv, path, succ, unwind_succ);
succ
}).collect()
Some(succ).into_iter().chain(
Copy link
Member

Choose a reason for hiding this comment

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

nit: iter::once(succ) is a more compact way to express this.

@nagisa
Copy link
Member

nagisa commented May 15, 2017

The last commit looks good.

@arielb1 arielb1 force-pushed the mir-array branch 2 times, most recently from 42e03a2 to 1d9c6d2 Compare May 15, 2017 15:56
@alexcrichton
Copy link
Member

@arielb1 is this waiting on review now? I couldn't immediately tell from the state of the PR. Just curious for what tags too apply!

@arielb1
Copy link
Contributor Author

arielb1 commented May 18, 2017

@alexcrichton

I'm trying to see whether I can get pointer-based iteration to work out nicely enough I'll want to use it.

@nagisa
Copy link
Member

nagisa commented May 20, 2017

I feel like code could be a bit more clear if the index-based loop and the pointer-based loop were separate, rather than so closely intermingled in the same function.

It is also not entirely clear to me that both BinOp::Offset and NullaryOp::SizeOf are necessary. That is, the Offset could be just a Add(x, SizeOf::<T>()).

These are the nits, feel free to ignore them.


Now… I think it could be possible to avoid indexing the array altogether in the loop. That is, instead of generating:

    /// loop-block:
    ///    can_go = index_or_cur == length_or_end
    ///    if can_go then succ else drop-block
    /// drop-block:
    ///    if size_of::<T> != 0 {
    ///        ptr = index_or_cur
    ///        index_or_cur = index_or_cur + size_of::<T>()
    ///    } else {
    ///        ptr = &mut LV[index_or_cur]
    ///        index_or_cur = index_or_cur + 1
    ///    }
    ///    drop(ptr);

It could be simplified(?) to this instead:

drop_selector:
    ptr = &mut LV[0];
    index = 0;
    len = len(LV);
    is_zero_sized = size_of::<T>() == 0;
    if is_zero_sized then zs_head else loop_head;
zs_head:
    finished = index == len;
    if finished then succ else zs_body;
zs_body:
    drop(ptr);
    index = index + 1;
    goto zs_head;
loop_head:
    finished = ptr == end;
    if finished then succ else loop_body
loop_body:
    drop(ptr);
    ptr = ptr + size_of::<T>();
    goto loop_head;

note the lack of actual “indexing” in the zero-sized case, which could plausibly make it easier for LLVM to optimise/unroll/etc the loop.

I was actually going to propose just using ptr as a index here – dereferencing a ZST is a no-op and therefore it doesn’t really matter what the pointer is, but then refrained because it is most likely UB.

It would also be worthwhile to have a FIXME somewhere to make SizeOf const-evaluated when possible, so SimplifyCfg could easily get rid of the other loop.

@arielb1
Copy link
Contributor Author

arielb1 commented May 21, 2017

That is, the Offset could be just a Add(x, SizeOf::<T>()).

Can you do an add with LLVM pointers? Plus, I want to emit a getelementptr inbounds.

@arielb1 arielb1 force-pushed the mir-array branch 2 times, most recently from c8478fc to e99766b Compare May 23, 2017 17:58
@arielb1
Copy link
Contributor Author

arielb1 commented May 23, 2017

this should fix the segfault


// Create the cleanup bundle, if needed.
let tcx = bcx.tcx();
let span = terminator.source_info.span;
let funclet_bb = match self.cleanup_kinds[bb] {
Copy link
Member

Choose a reason for hiding this comment

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

You added a method (funclet_bb) to CleanupKind that does this, shouldn't we just call it here? Unless I'm missing some detail.

Copy link
Contributor Author

@arielb1 arielb1 May 23, 2017

Choose a reason for hiding this comment

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

umm yeah. fixed

@nagisa
Copy link
Member

nagisa commented May 24, 2017

LGTM. I asked on IRC:

eddyb, I believe you asked for a codegen test for the arielby’s array dropping PR. Do you still want one given that the PR now uses the pointer loop when possible?

but got no response. Feel free to r- if you feel like one is still necessary.

@bors r+

@bors
Copy link
Contributor

bors commented May 24, 2017

📌 Commit 9bfe40e has been approved by nagisa

@eddyb
Copy link
Member

eddyb commented May 25, 2017

I replied to both @nagisa and @arielb1 but on IRC it's easy to miss a message 😆.
There is no test necessary as my point was specifically about LLVM's optimization ability.

@bors
Copy link
Contributor

bors commented May 25, 2017

⌛ Testing commit 9bfe40e with merge 8889b73...

@rust-highfive rust-highfive assigned nagisa and unassigned eddyb May 27, 2017
Copy link
Member

@nagisa nagisa left a comment

Choose a reason for hiding this comment

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

Two minor nits. Looks great otherwise. r=me.

I won’t be able to look at github for a few upcoming days and won’t be able to instruct bors either.

block_bcxs.iter_enumerated().zip(cleanup_kinds).map(|((bb, &llbb), cleanup_kind)| {
match *cleanup_kind {
CleanupKind::Funclet if base::wants_msvc_seh(ccx.sess()) => {
let bcx = Builder::with_ccx(ccx);
Copy link
Member

Choose a reason for hiding this comment

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

You could create this builder outside the loop, avoiding an allocation and deallocation for each iteration, I think?

}
CleanupKind::Internal { .. } => bcx.br(lltarget),
CleanupKind::NotCleanup => bug!("jump from cleanup bb to bb {:?}", bb)
let llblock2 = |this: &mut Self, target: mir::BasicBlock| {
Copy link
Member

Choose a reason for hiding this comment

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

This could use a better name. Something like cleanup_adjusted_llblock maybe? lltarget_inner or lltarget_common could also work.

@arielb1
Copy link
Contributor Author

arielb1 commented May 28, 2017

@bors r=nagisa

@bors
Copy link
Contributor

bors commented May 28, 2017

📌 Commit 94f65c5 has been approved by nagisa

@arielb1
Copy link
Contributor Author

arielb1 commented May 28, 2017

@bors r=nagisa

@bors
Copy link
Contributor

bors commented May 28, 2017

📌 Commit 137e710 has been approved by nagisa

@arielb1
Copy link
Contributor Author

arielb1 commented May 28, 2017

@bors r=nagisa

@bors
Copy link
Contributor

bors commented May 28, 2017

📌 Commit ee982d4 has been approved by nagisa

@bors
Copy link
Contributor

bors commented May 28, 2017

⌛ Testing commit ee982d4 with merge 924898f...

bors added a commit that referenced this pull request May 28, 2017
Translate array drop glue using MIR

I was a bit lazy here and used a usize-based index instead of a pointer iteration. Do you think this is important @eddyb?

r? @eddyb
@bors
Copy link
Contributor

bors commented May 28, 2017

☀️ Test successful - status-appveyor, status-travis
Approved by: nagisa
Pushing 924898f to master...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants