Skip to content

Commit e1dbf5e

Browse files
committed
Rollup merge of rust-lang#32738 - Aatch:mir-operand-fn-ret, r=arielb1
Previously, all non-void function returns required an on-stack location for the value to be stored to. This code improves translation of function calls so this is no longer necessary.
2 parents ec5258e + cb1bec9 commit e1dbf5e

File tree

6 files changed

+220
-60
lines changed

6 files changed

+220
-60
lines changed

src/librustc/middle/region.rs

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

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

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: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,9 @@ impl<'tcx> Visitor<'tcx> for TempAnalyzer {
105105
match *lvalue {
106106
mir::Lvalue::Temp(index) => {
107107
match context {
108+
LvalueContext::Call => {
109+
self.mark_assigned(index as usize);
110+
}
108111
LvalueContext::Consume => {
109112
}
110113
LvalueContext::Store |

src/librustc_trans/mir/block.rs

Lines changed: 174 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@
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;
1818
use callee::{Callee, CalleeData, Fn, Intrinsic, NamedTupleConstructor, Virtual};
19-
use common::{self, Block, BlockAndBuilder, C_undef};
19+
use common::{self, type_is_fat_ptr, Block, BlockAndBuilder, C_undef};
2020
use debuginfo::DebugLoc;
2121
use Disr;
2222
use machine::{llalign_of_min, llbitsize_of_real};
@@ -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};
@@ -169,6 +169,8 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
169169
_ => bug!("{} is not callable", callee.ty)
170170
};
171171

172+
let sig = bcx.tcx().erase_late_bound_regions(sig);
173+
172174
// Handle intrinsics old trans wants Expr's for, ourselves.
173175
let intrinsic = match (&callee.ty.sty, &callee.data) {
174176
(&ty::TyFnDef(def_id, _, _), &Intrinsic) => {
@@ -191,31 +193,16 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
191193

192194
if intrinsic == Some("transmute") {
193195
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-
}
196+
self.with_lvalue_ref(&bcx, dest, |this, dest| {
197+
this.trans_transmute(&bcx, &args[0], dest);
198+
});
209199

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);
213200
self.set_operand_dropped(&bcx, &args[0]);
214201
funclet_br(bcx, self.llblock(target));
215202
return;
216203
}
217204

218-
let extra_args = &args[sig.0.inputs.len()..];
205+
let extra_args = &args[sig.inputs.len()..];
219206
let extra_args = extra_args.iter().map(|op_arg| {
220207
self.mir.operand_ty(bcx.tcx(), op_arg)
221208
}).collect::<Vec<_>>();
@@ -226,18 +213,15 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
226213
let mut llargs = Vec::with_capacity(arg_count);
227214

228215
// Prepare the return value destination
229-
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
216+
let ret_dest = if let Some((ref dest, _)) = *destination {
217+
let is_intrinsic = if let Intrinsic = callee.data {
218+
true
236219
} else {
237-
Some(dest)
238-
}
220+
false
221+
};
222+
self.make_return_dest(&bcx, dest, &fn_ty.ret, &mut llargs, is_intrinsic)
239223
} else {
240-
None
224+
ReturnDest::Nothing
241225
};
242226

243227
// Split the rust-call tupled arguments off.
@@ -269,29 +253,42 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
269253
use expr::{Ignore, SaveIn};
270254
use intrinsic::trans_intrinsic_call;
271255

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[..])
256+
let (dest, llargs) = match ret_dest {
257+
_ if fn_ty.ret.is_indirect() => {
258+
(SaveIn(llargs[0]), &llargs[1..])
259+
}
260+
ReturnDest::Nothing => (Ignore, &llargs[..]),
261+
ReturnDest::IndirectOperand(dst, _) |
262+
ReturnDest::Store(dst) => (SaveIn(dst), &llargs[..]),
263+
ReturnDest::DirectOperand(_) =>
264+
bug!("Cannot use direct operand with an intrinsic call")
278265
};
279266

280267
bcx.with_block(|bcx| {
281-
let res = trans_intrinsic_call(bcx, callee.ty, &fn_ty,
268+
trans_intrinsic_call(bcx, callee.ty, &fn_ty,
282269
ArgVals(llargs), dest,
283270
DebugLoc::None);
284-
let bcx = res.bcx.build();
285-
if let Some((_, target)) = *destination {
286-
for op in args {
287-
self.set_operand_dropped(&bcx, op);
288-
}
289-
funclet_br(bcx, self.llblock(target));
290-
} else {
291-
// trans_intrinsic_call already used Unreachable.
292-
// bcx.unreachable();
293-
}
294271
});
272+
273+
if let ReturnDest::IndirectOperand(dst, _) = ret_dest {
274+
// Make a fake operand for store_return
275+
let op = OperandRef {
276+
val: OperandValue::Ref(dst),
277+
ty: sig.output.unwrap()
278+
};
279+
self.store_return(&bcx, ret_dest, fn_ty.ret, op);
280+
}
281+
282+
if let Some((_, target)) = *destination {
283+
for op in args {
284+
self.set_operand_dropped(&bcx, op);
285+
}
286+
funclet_br(bcx, self.llblock(target));
287+
} else {
288+
// trans_intrinsic_call already used Unreachable.
289+
// bcx.unreachable();
290+
}
291+
295292
return;
296293
}
297294
Fn(f) => f,
@@ -321,9 +318,11 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
321318
if destination.is_some() {
322319
let ret_bcx = ret_bcx.build();
323320
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-
}
321+
let op = OperandRef {
322+
val: OperandValue::Immediate(invokeret),
323+
ty: sig.output.unwrap()
324+
};
325+
self.store_return(&ret_bcx, ret_dest, fn_ty.ret, op);
327326
for op in args {
328327
self.set_operand_dropped(&ret_bcx, op);
329328
}
@@ -333,9 +332,11 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
333332
let llret = bcx.call(fn_ptr, &llargs, cleanup_bundle.as_ref());
334333
fn_ty.apply_attrs_callsite(llret);
335334
if let Some((_, target)) = *destination {
336-
if let Some(ret_dest) = ret_dest {
337-
fn_ty.ret.store(&bcx, llret, ret_dest.llval);
338-
}
335+
let op = OperandRef {
336+
val: OperandValue::Immediate(llret),
337+
ty: sig.output.unwrap()
338+
};
339+
self.store_return(&bcx, ret_dest, fn_ty.ret, op);
339340
for op in args {
340341
self.set_operand_dropped(&bcx, op);
341342
}
@@ -544,4 +545,122 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
544545
pub fn llblock(&self, bb: mir::BasicBlock) -> BasicBlockRef {
545546
self.blocks[bb.index()].llbb
546547
}
548+
549+
fn make_return_dest(&mut self, bcx: &BlockAndBuilder<'bcx, 'tcx>,
550+
dest: &mir::Lvalue<'tcx>, fn_ret_ty: &ArgType,
551+
llargs: &mut Vec<ValueRef>, is_intrinsic: bool) -> ReturnDest {
552+
// If the return is ignored, we can just return a do-nothing ReturnDest
553+
if fn_ret_ty.is_ignore() {
554+
return ReturnDest::Nothing;
555+
}
556+
let dest = match *dest {
557+
mir::Lvalue::Temp(idx) => {
558+
let lvalue_ty = self.mir.lvalue_ty(bcx.tcx(), dest);
559+
let lvalue_ty = bcx.monomorphize(&lvalue_ty);
560+
let ret_ty = lvalue_ty.to_ty(bcx.tcx());
561+
match self.temps[idx as usize] {
562+
TempRef::Lvalue(dest) => dest,
563+
TempRef::Operand(None) => {
564+
// Handle temporary lvalues, specifically Operand ones, as
565+
// they don't have allocas
566+
return if fn_ret_ty.is_indirect() {
567+
// Odd, but possible, case, we have an operand temporary,
568+
// but the calling convention has an indirect return.
569+
let tmp = bcx.with_block(|bcx| {
570+
base::alloc_ty(bcx, ret_ty, "tmp_ret")
571+
});
572+
llargs.push(tmp);
573+
ReturnDest::IndirectOperand(tmp, idx)
574+
} else if is_intrinsic {
575+
// Currently, intrinsics always need a location to store
576+
// the result. so we create a temporary alloca for the
577+
// result
578+
let tmp = bcx.with_block(|bcx| {
579+
base::alloc_ty(bcx, ret_ty, "tmp_ret")
580+
});
581+
ReturnDest::IndirectOperand(tmp, idx)
582+
} else {
583+
ReturnDest::DirectOperand(idx)
584+
};
585+
}
586+
TempRef::Operand(Some(_)) => {
587+
bug!("lvalue temp already assigned to");
588+
}
589+
}
590+
}
591+
_ => self.trans_lvalue(bcx, dest)
592+
};
593+
if fn_ret_ty.is_indirect() {
594+
llargs.push(dest.llval);
595+
ReturnDest::Nothing
596+
} else {
597+
ReturnDest::Store(dest.llval)
598+
}
599+
}
600+
601+
fn trans_transmute(&mut self, bcx: &BlockAndBuilder<'bcx, 'tcx>,
602+
src: &mir::Operand<'tcx>, dst: LvalueRef<'tcx>) {
603+
let mut val = self.trans_operand(bcx, src);
604+
if let ty::TyFnDef(def_id, substs, _) = val.ty.sty {
605+
let llouttype = type_of::type_of(bcx.ccx(), dst.ty.to_ty(bcx.tcx()));
606+
let out_type_size = llbitsize_of_real(bcx.ccx(), llouttype);
607+
if out_type_size != 0 {
608+
// FIXME #19925 Remove this hack after a release cycle.
609+
let f = Callee::def(bcx.ccx(), def_id, substs);
610+
let datum = f.reify(bcx.ccx());
611+
val = OperandRef {
612+
val: OperandValue::Immediate(datum.val),
613+
ty: datum.ty
614+
};
615+
}
616+
}
617+
618+
let llty = type_of::type_of(bcx.ccx(), val.ty);
619+
let cast_ptr = bcx.pointercast(dst.llval, llty.ptr_to());
620+
self.store_operand(bcx, cast_ptr, val);
621+
}
622+
623+
// Stores the return value of a function call into it's final location.
624+
fn store_return(&mut self,
625+
bcx: &BlockAndBuilder<'bcx, 'tcx>,
626+
dest: ReturnDest,
627+
ret_ty: ArgType,
628+
op: OperandRef<'tcx>) {
629+
use self::ReturnDest::*;
630+
631+
match dest {
632+
Nothing => (),
633+
Store(dst) => ret_ty.store(bcx, op.immediate(), dst),
634+
IndirectOperand(tmp, idx) => {
635+
let op = self.trans_load(bcx, tmp, op.ty);
636+
self.temps[idx as usize] = TempRef::Operand(Some(op));
637+
}
638+
DirectOperand(idx) => {
639+
let op = if type_is_fat_ptr(bcx.tcx(), op.ty) {
640+
let llval = op.immediate();
641+
let ptr = bcx.extract_value(llval, 0);
642+
let meta = bcx.extract_value(llval, 1);
643+
644+
OperandRef {
645+
val: OperandValue::FatPtr(ptr, meta),
646+
ty: op.ty
647+
}
648+
} else {
649+
op
650+
};
651+
self.temps[idx as usize] = TempRef::Operand(Some(op));
652+
}
653+
}
654+
}
655+
}
656+
657+
enum ReturnDest {
658+
// Do nothing, the return value is indirect or ignored
659+
Nothing,
660+
// Store the return value to the pointer
661+
Store(ValueRef),
662+
// Stores an indirect return value to an operand temporary lvalue
663+
IndirectOperand(ValueRef, u32),
664+
// Stores a direct return value to an operand temporary lvalue
665+
DirectOperand(u32)
547666
}

src/librustc_trans/mir/lvalue.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,40 @@ 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_ty = bcx.monomorphize(&lvalue_ty);
224+
let lvalue = LvalueRef::alloca(bcx,
225+
lvalue_ty.to_ty(bcx.tcx()),
226+
"lvalue_temp");
227+
let ret = f(self, lvalue);
228+
let op = self.trans_load(bcx, lvalue.llval, lvalue_ty.to_ty(bcx.tcx()));
229+
self.temps[idx as usize] = TempRef::Operand(Some(op));
230+
ret
231+
}
232+
TempRef::Operand(Some(_)) => {
233+
bug!("Lvalue temp already set");
234+
}
235+
}
236+
}
237+
_ => {
238+
let lvalue = self.trans_lvalue(bcx, lvalue);
239+
f(self, lvalue)
240+
}
241+
}
242+
}
243+
210244
/// Adjust the bitwidth of an index since LLVM is less forgiving
211245
/// than we are.
212246
///

0 commit comments

Comments
 (0)