Skip to content

Commit e4d4fa3

Browse files
committed
Handle operand temps for function calls
This allows temporary destinations for function calls to have their allocas omitted.
1 parent 9c2186d commit e4d4fa3

File tree

5 files changed

+191
-45
lines changed

5 files changed

+191
-45
lines changed

src/librustc/middle/region.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,9 @@ impl fmt::Debug for CodeExtent {
4545

4646
ty::tls::with_opt(|opt_tcx| {
4747
if let Some(tcx) = opt_tcx {
48-
let data = tcx.region_maps.code_extents.borrow()[self.0 as usize];
49-
write!(f, "/{:?}", data)?;
48+
if let Some(data) = tcx.region_maps.code_extents.borrow().get(self.0 as usize) {
49+
write!(f, "/{:?}", data)?;
50+
}
5051
}
5152
Ok(())
5253
})?;

src/librustc/mir/visit.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -407,7 +407,7 @@ macro_rules! make_mir_visitor {
407407
self.visit_operand(arg);
408408
}
409409
if let Some((ref $($mutability)* destination, target)) = *destination {
410-
self.visit_lvalue(destination, LvalueContext::Store);
410+
self.visit_lvalue(destination, LvalueContext::Call);
411411
self.visit_branch(block, target);
412412
}
413413
cleanup.map(|t| self.visit_branch(block, t));
@@ -692,9 +692,12 @@ make_mir_visitor!(MutVisitor,mut);
692692

693693
#[derive(Copy, Clone, Debug)]
694694
pub enum LvalueContext {
695-
// Appears as LHS of an assignment or as dest of a call
695+
// Appears as LHS of an assignment
696696
Store,
697697

698+
// Dest of a call
699+
Call,
700+
698701
// Being dropped
699702
Drop,
700703

src/librustc_trans/mir/analyze.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ impl<'tcx> Visitor<'tcx> for TempAnalyzer {
105105
match *lvalue {
106106
mir::Lvalue::Temp(index) => {
107107
match context {
108+
LvalueContext::Call |
108109
LvalueContext::Consume => {
109110
}
110111
LvalueContext::Store |

src/librustc_trans/mir/block.rs

Lines changed: 151 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
use llvm::{self, BasicBlockRef, ValueRef, OperandBundleDef};
1212
use rustc::ty;
1313
use rustc::mir::repr as mir;
14-
use abi::{Abi, FnType};
14+
use abi::{Abi, FnType, ArgType};
1515
use adt;
1616
use base;
1717
use build;
@@ -25,7 +25,7 @@ use type_of;
2525
use glue;
2626
use type_::Type;
2727

28-
use super::{MirContext, drop};
28+
use super::{MirContext, TempRef, drop};
2929
use super::lvalue::{LvalueRef, load_fat_ptr};
3030
use super::operand::OperandRef;
3131
use super::operand::OperandValue::{self, FatPtr, Immediate, Ref};
@@ -191,25 +191,10 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
191191

192192
if intrinsic == Some("transmute") {
193193
let &(ref dest, target) = destination.as_ref().unwrap();
194-
let dst = self.trans_lvalue(&bcx, dest);
195-
let mut val = self.trans_operand(&bcx, &args[0]);
196-
if let ty::TyFnDef(def_id, substs, _) = val.ty.sty {
197-
let llouttype = type_of::type_of(bcx.ccx(), dst.ty.to_ty(bcx.tcx()));
198-
let out_type_size = llbitsize_of_real(bcx.ccx(), llouttype);
199-
if out_type_size != 0 {
200-
// FIXME #19925 Remove this hack after a release cycle.
201-
let f = Callee::def(bcx.ccx(), def_id, substs);
202-
let datum = f.reify(bcx.ccx());
203-
val = OperandRef {
204-
val: OperandValue::Immediate(datum.val),
205-
ty: datum.ty
206-
};
207-
}
208-
}
194+
self.with_lvalue_ref(&bcx, dest, |this, dest| {
195+
this.trans_transmute(&bcx, &args[0], dest);
196+
});
209197

210-
let llty = type_of::type_of(bcx.ccx(), val.ty);
211-
let cast_ptr = bcx.pointercast(dst.llval, llty.ptr_to());
212-
self.store_operand(&bcx, cast_ptr, val);
213198
self.set_operand_dropped(&bcx, &args[0]);
214199
funclet_br(bcx, self.llblock(target));
215200
return;
@@ -227,17 +212,71 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
227212

228213
// Prepare the return value destination
229214
let ret_dest = if let Some((ref d, _)) = *destination {
230-
let dest = self.trans_lvalue(&bcx, d);
231-
if fn_ty.ret.is_indirect() {
232-
llargs.push(dest.llval);
233-
None
234-
} else if fn_ty.ret.is_ignore() {
235-
None
236-
} else {
237-
Some(dest)
215+
match *d {
216+
// Handle temporary lvalues, specifically Operand ones, as
217+
// they don't have allocas
218+
mir::Lvalue::Temp(idx) => {
219+
let lvalue_ty = self.mir.lvalue_ty(bcx.tcx(), d);
220+
let ret_ty = lvalue_ty.to_ty(bcx.tcx());
221+
match self.temps[idx as usize] {
222+
TempRef::Lvalue(dest) => {
223+
if fn_ty.ret.is_indirect() {
224+
llargs.push(dest.llval);
225+
ReturnDest::Nothing
226+
} else if fn_ty.ret.is_ignore() {
227+
ReturnDest::Nothing
228+
} else {
229+
ReturnDest::Store(dest.llval)
230+
}
231+
}
232+
TempRef::Operand(None) => {
233+
let is_intrinsic = if let Intrinsic = callee.data {
234+
true
235+
} else {
236+
false
237+
};
238+
239+
if fn_ty.ret.is_indirect() {
240+
// Odd, but possible, case, we have an operand temporary,
241+
// but the calling convention has an indirect return.
242+
let tmp = bcx.with_block(|bcx| {
243+
base::alloc_ty(bcx, ret_ty, "tmp_ret")
244+
});
245+
llargs.push(tmp);
246+
ReturnDest::IndirectOperand(tmp, idx)
247+
} else if is_intrinsic {
248+
// Currently, intrinsics always need a location to store
249+
// the result. so we create a temporary alloca for the
250+
// result
251+
let tmp = bcx.with_block(|bcx| {
252+
base::alloc_ty(bcx, ret_ty, "tmp_ret")
253+
});
254+
ReturnDest::IndirectOperand(tmp, idx)
255+
} else if fn_ty.ret.is_ignore() {
256+
ReturnDest::Nothing
257+
} else {
258+
ReturnDest::DirectOperand(idx)
259+
}
260+
}
261+
TempRef::Operand(Some(_)) => {
262+
bug!("lvalue temp already assigned to");
263+
}
264+
}
265+
}
266+
_ => {
267+
let dest = self.trans_lvalue(&bcx, d);
268+
if fn_ty.ret.is_indirect() {
269+
llargs.push(dest.llval);
270+
ReturnDest::Nothing
271+
} else if fn_ty.ret.is_ignore() {
272+
ReturnDest::Nothing
273+
} else {
274+
ReturnDest::Store(dest.llval)
275+
}
276+
}
238277
}
239278
} else {
240-
None
279+
ReturnDest::Nothing
241280
};
242281

243282
// Split the rust-call tupled arguments off.
@@ -269,12 +308,15 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
269308
use expr::{Ignore, SaveIn};
270309
use intrinsic::trans_intrinsic_call;
271310

272-
let (dest, llargs) = if fn_ty.ret.is_indirect() {
273-
(SaveIn(llargs[0]), &llargs[1..])
274-
} else if let Some(dest) = ret_dest {
275-
(SaveIn(dest.llval), &llargs[..])
276-
} else {
277-
(Ignore, &llargs[..])
311+
let (dest, llargs) = match ret_dest {
312+
_ if fn_ty.ret.is_indirect() => {
313+
(SaveIn(llargs[0]), &llargs[1..])
314+
}
315+
ReturnDest::Nothing => (Ignore, &llargs[..]),
316+
ReturnDest::IndirectOperand(dst, _) |
317+
ReturnDest::Store(dst) => (SaveIn(dst), &llargs[..]),
318+
ReturnDest::DirectOperand(_) =>
319+
bug!("Cannot use direct operand with an intrinsic call")
278320
};
279321

280322
bcx.with_block(|bcx| {
@@ -292,6 +334,16 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
292334
// bcx.unreachable();
293335
}
294336
});
337+
338+
if let ReturnDest::IndirectOperand(dst, _) = ret_dest {
339+
// Make a fake operand for store_return
340+
let op = OperandRef {
341+
val: OperandValue::Ref(dst),
342+
ty: sig.0.output.unwrap()
343+
};
344+
self.store_return(&bcx, ret_dest, fn_ty.ret, op);
345+
}
346+
295347
return;
296348
}
297349
Fn(f) => f,
@@ -321,9 +373,11 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
321373
if destination.is_some() {
322374
let ret_bcx = ret_bcx.build();
323375
ret_bcx.at_start(|ret_bcx| {
324-
if let Some(ret_dest) = ret_dest {
325-
fn_ty.ret.store(&ret_bcx, invokeret, ret_dest.llval);
326-
}
376+
let op = OperandRef {
377+
val: OperandValue::Immediate(invokeret),
378+
ty: sig.0.output.unwrap()
379+
};
380+
self.store_return(&ret_bcx, ret_dest, fn_ty.ret, op);
327381
for op in args {
328382
self.set_operand_dropped(&ret_bcx, op);
329383
}
@@ -333,9 +387,11 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
333387
let llret = bcx.call(fn_ptr, &llargs, cleanup_bundle.as_ref());
334388
fn_ty.apply_attrs_callsite(llret);
335389
if let Some((_, target)) = *destination {
336-
if let Some(ret_dest) = ret_dest {
337-
fn_ty.ret.store(&bcx, llret, ret_dest.llval);
338-
}
390+
let op = OperandRef {
391+
val: OperandValue::Immediate(llret),
392+
ty: sig.0.output.unwrap()
393+
};
394+
self.store_return(&bcx, ret_dest, fn_ty.ret, op);
339395
for op in args {
340396
self.set_operand_dropped(&bcx, op);
341397
}
@@ -544,4 +600,58 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
544600
pub fn llblock(&self, bb: mir::BasicBlock) -> BasicBlockRef {
545601
self.blocks[bb.index()].llbb
546602
}
603+
604+
fn trans_transmute(&mut self, bcx: &BlockAndBuilder<'bcx, 'tcx>,
605+
src: &mir::Operand<'tcx>, dst: LvalueRef<'tcx>) {
606+
let mut val = self.trans_operand(bcx, src);
607+
if let ty::TyFnDef(def_id, substs, _) = val.ty.sty {
608+
let llouttype = type_of::type_of(bcx.ccx(), dst.ty.to_ty(bcx.tcx()));
609+
let out_type_size = llbitsize_of_real(bcx.ccx(), llouttype);
610+
if out_type_size != 0 {
611+
// FIXME #19925 Remove this hack after a release cycle.
612+
let f = Callee::def(bcx.ccx(), def_id, substs);
613+
let datum = f.reify(bcx.ccx());
614+
val = OperandRef {
615+
val: OperandValue::Immediate(datum.val),
616+
ty: datum.ty
617+
};
618+
}
619+
}
620+
621+
let llty = type_of::type_of(bcx.ccx(), val.ty);
622+
let cast_ptr = bcx.pointercast(dst.llval, llty.ptr_to());
623+
self.store_operand(bcx, cast_ptr, val);
624+
}
625+
626+
// Stores the return value of a function call into it's final location.
627+
fn store_return(&mut self,
628+
bcx: &BlockAndBuilder<'bcx, 'tcx>,
629+
dest: ReturnDest,
630+
ret_ty: ArgType,
631+
op: OperandRef<'tcx>) {
632+
use self::ReturnDest::*;
633+
634+
match dest {
635+
Nothing => (),
636+
Store(dst) => ret_ty.store(bcx, op.immediate(), dst),
637+
IndirectOperand(tmp, idx) => {
638+
let op = self.trans_load(bcx, tmp, op.ty);
639+
self.temps[idx as usize] = TempRef::Operand(Some(op));
640+
}
641+
DirectOperand(idx) => {
642+
self.temps[idx as usize] = TempRef::Operand(Some(op));
643+
}
644+
}
645+
}
646+
}
647+
648+
enum ReturnDest {
649+
// Do nothing, the return value is indirect or ignored
650+
Nothing,
651+
// Store the return value to the pointer
652+
Store(ValueRef),
653+
// Stores an indirect return value to an operand temporary lvalue
654+
IndirectOperand(ValueRef, u32),
655+
// Stores a direct return value to an operand temporary lvalue
656+
DirectOperand(u32)
547657
}

src/librustc_trans/mir/lvalue.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,37 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
207207
}
208208
}
209209

210+
// Perform an action using the given Lvalue.
211+
// If the Lvalue is an empty TempRef::Operand, then a temporary stack slot
212+
// is created first, then used as an operand to update the Lvalue.
213+
pub fn with_lvalue_ref<F, U>(&mut self, bcx: &BlockAndBuilder<'bcx, 'tcx>,
214+
lvalue: &mir::Lvalue<'tcx>, f: F) -> U
215+
where F: FnOnce(&mut Self, LvalueRef<'tcx>) -> U
216+
{
217+
match *lvalue {
218+
mir::Lvalue::Temp(idx) => {
219+
match self.temps[idx as usize] {
220+
TempRef::Lvalue(lvalue) => f(self, lvalue),
221+
TempRef::Operand(None) => {
222+
let lvalue_ty = self.mir.lvalue_ty(bcx.tcx(), lvalue);
223+
let lvalue = LvalueRef::alloca(bcx, lvalue_ty.to_ty(bcx.tcx()), "lvalue_temp");
224+
let ret = f(self, lvalue);
225+
let op = self.trans_load(bcx, lvalue.llval, lvalue_ty.to_ty(bcx.tcx()));
226+
self.temps[idx as usize] = TempRef::Operand(Some(op));
227+
ret
228+
}
229+
TempRef::Operand(Some(_)) => {
230+
bug!("Lvalue temp already set");
231+
}
232+
}
233+
}
234+
_ => {
235+
let lvalue = self.trans_lvalue(bcx, lvalue);
236+
f(self, lvalue)
237+
}
238+
}
239+
}
240+
210241
/// Adjust the bitwidth of an index since LLVM is less forgiving
211242
/// than we are.
212243
///

0 commit comments

Comments
 (0)