Change:
// (e.g. structs) into an alloca unconditionally, just so // that we don't have to deal with having two pathways // (gep vs extractvalue etc). <del> analyzer.mark_as_memory(mir::Local::new(index)); </del> <ins> analyzer.not_ssa(mir::Local::new(index)); </ins> } } <del> analyzer.memory_locals </del> <ins> analyzer.non_ssa_locals </ins> } struct LocalAnalyzer<'mir, 'a: 'mir, 'tcx: 'a> { fx: &'mir FunctionCx<'a, 'tcx>, <del> memory_locals: BitVector, seen_assigned: BitVector </del> <ins> dominators: Dominators<mir::BasicBlock>, non_ssa_locals: BitVector, // The location of the first visited direct assignment to each // local, or an invalid location (out of bounds `block` index). first_assignment: IndexVec<mir::Local, Location> </ins> } impl<'mir, 'a, 'tcx> LocalAnalyzer<'mir, 'a, 'tcx> { fn new(fx: &'mir FunctionCx<'a, 'tcx>) -> LocalAnalyzer<'mir, 'a, 'tcx> { <ins> let invalid_location = mir::BasicBlock::new(fx.mir.basic_blocks().len()).start_location(); </ins> let mut analyzer = LocalAnalyzer { fx, <del> memory_locals: BitVector::new(fx.mir.local_decls.len()), seen_assigned: BitVector::new(fx.mir.local_decls.len()) </del> <ins> dominators: fx.mir.dominators(), non_ssa_locals: BitVector::new(fx.mir.local_decls.len()), first_assignment: IndexVec::from_elem(invalid_location, &fx.mir.local_decls) </ins> }; // Arguments get assigned to by means of the function being called <del> for idx in 0..fx.mir.arg_count { analyzer.seen_assigned.insert(idx + 1); </del> <ins> for arg in fx.mir.args_iter() { analyzer.first_assignment[arg] = mir::START_BLOCK.start_location(); </ins> } analyzer } <del> fn mark_as_memory(&mut self, local: mir::Local) { debug!("marking {:?} as memory", local); self.memory_locals.insert(local.index()); </del> <ins> fn first_assignment(&self, local: mir::Local) -> Option<Location> { let location = self.first_assignment[local]; if location.block.index() < self.fx.mir.basic_blocks().len() { Some(location) } else { None } </ins> } <del> fn mark_assigned(&mut self, local: mir::Local) { if !self.seen_assigned.insert(local.index()) { self.mark_as_memory(local); </del> <ins> fn not_ssa(&mut self, local: mir::Local) { debug!("marking {:?} as non-SSA", local); self.non_ssa_locals.insert(local.index()); } fn assign(&mut self, local: mir::Local, location: Location) { if self.first_assignment(local).is_some() { self.not_ssa(local); } else { self.first_assignment[local] = location; </ins> } } }

Discussion:
Context: In order to experiment with an -aware , I was preparing to modify the lang item signature, and my first step was to ensure MIR passes the right type to begin with, as noted in Legitimately, I assumed that the MIR inline code was doing the right thing, so I mimicked it in . The resulting code actually works fine, as far as completing the 3 stages of rustc bootstrapping is involved. But before going the route for the -aware , I first tried removing the special handling of 's , trying to leave it to , shortcutting the lang item. This didn't go well, and produced a stage 1 compiler that would crash on a bad in libsyntax's . From which I derived a small test case that would exhibit the problem with my code. Anyways, I was going well over my head with this approach, thus switched to the signature change. So, what's the deal with this issue, will you ask? Well, it turns out that my MIR changes, essentially copied from MIR inlining, while they worked to produce an apparently working compiler, failed to compile that reduced testcase with an LLVM ERROR. I was wondering if I did something significantly different from what the MIT inlining pass was doing, so I tried to trigger it manually (since it's not enabled by default), and after some trial and error, got it to happen on a plain nightly compiler, with the following reduced testcase: Building with yields: (Note the is only there to force MIR inlining to happen without having to go over the required threshold ; liballoc's has ; similarly, the on avoids inlining of dealloc, to limit the effects on the MIR) Interestingly enough, and fail with the same error. The former outputs a truncated MIR (truncated at the entry of the first basic block), and the latter outputs nothing.
This is what from a run with looks like: Edit: and what the llvm-ir, as disassembled from output, looks like:
The problem is that the code path bb1-bb9-bb21-bb20 in IR, corresponding to bb0-bb1-bb6-bb5 in MIR, is impossible, yet exists. And in that code path, in MIR, in IR, is never initialized, and that's what LLVM doesn't like.

I guess one way to look at is it to wonder why in MIR has a switch in the first place.
And the answer to that last question is false edges, according to the following MIR from the mir_map pass:
Actually, the path bb0-bb7-bb4-bb10-bb12 has without ever being initialized, but that's pre-elaborate-drops, and I hear drop is different then, so I don't know if it bad or not.
The weird switch that eventually causes the LLVM problem is by the elaborate-drops pass. The MIR after that phase looks like the following:
I think the most relevant fact is this difference between MIR inlining being enabled or not: with MIR inlining disabled, this basic block in MIR: becomes the following IR: with MIR inlining enabled, this basic block in MIR: becomes whiich is weird... where did the extra code go?
The changes from fix this issue.