lower move_val_init during MIR construction

Because of its "magic" order-of-evaluation semantics, `move_val_init`
must be lowered during MIR construction in order to work.
This commit is contained in:
Ariel Ben-Yehuda 2017-04-18 15:05:27 +03:00
parent 3e473b1aaa
commit ed3810bf5e
4 changed files with 77 additions and 31 deletions

View File

@ -38,9 +38,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
debug!("expr_as_temp(block={:?}, expr={:?})", block, expr);
let this = self;
if let ExprKind::Scope { .. } = expr.kind {
span_bug!(expr.span, "unexpected scope expression in as_temp: {:?}",
expr);
if let ExprKind::Scope { extent, value } = expr.kind {
return this.in_scope(extent, block, |this| {
this.as_temp(block, temp_lifetime, value)
});
}
let expr_ty = expr.ty.clone();

View File

@ -16,6 +16,8 @@ use hair::*;
use rustc::ty;
use rustc::mir::*;
use syntax::abi::Abi;
impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
/// Compile `expr`, storing the result into `destination`, which
/// is assumed to be uninitialized.
@ -206,25 +208,49 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
}
_ => false
};
let fun = unpack!(block = this.as_local_operand(block, fun));
let args: Vec<_> =
args.into_iter()
.map(|arg| unpack!(block = this.as_local_operand(block, arg)))
.collect();
let success = this.cfg.start_new_block();
let cleanup = this.diverge_cleanup();
this.cfg.terminate(block, source_info, TerminatorKind::Call {
func: fun,
args: args,
cleanup: cleanup,
destination: if diverges {
None
} else {
Some ((destination.clone(), success))
let intrinsic = match ty.sty {
ty::TyFnDef(def_id, _, ref f) if
f.abi() == Abi::RustIntrinsic ||
f.abi() == Abi::PlatformIntrinsic =>
{
Some(this.hir.tcx().item_name(def_id).as_str())
}
});
success.unit()
_ => None
};
let intrinsic = intrinsic.as_ref().map(|s| &s[..]);
let fun = unpack!(block = this.as_local_operand(block, fun));
if intrinsic == Some("move_val_init") {
// `move_val_init` has "magic" semantics - the second argument is
// always evaluated "directly" into the first one.
let mut args = args.into_iter();
let ptr = args.next().expect("0 arguments to `move_val_init`");
let val = args.next().expect("1 argument to `move_val_init`");
assert!(args.next().is_none(), ">2 arguments to `move_val_init`");
let topmost_scope = this.topmost_scope();
let ptr = unpack!(block = this.as_temp(block, Some(topmost_scope), ptr));
this.into(&ptr.deref(), block, val)
} else {
let args: Vec<_> =
args.into_iter()
.map(|arg| unpack!(block = this.as_local_operand(block, arg)))
.collect();
let success = this.cfg.start_new_block();
let cleanup = this.diverge_cleanup();
this.cfg.terminate(block, source_info, TerminatorKind::Call {
func: fun,
args: args,
cleanup: cleanup,
destination: if diverges {
None
} else {
Some ((destination.clone(), success))
}
});
success.unit()
}
}
// These cases don't actually need a destination

View File

@ -418,16 +418,6 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
};
let intrinsic = intrinsic.as_ref().map(|s| &s[..]);
if intrinsic == Some("move_val_init") {
let &(_, target) = destination.as_ref().unwrap();
// The first argument is a thin destination pointer.
let llptr = self.trans_operand(&bcx, &args[0]).immediate();
let val = self.trans_operand(&bcx, &args[1]);
self.store_operand(&bcx, llptr, None, val);
funclet_br(self, bcx, target);
return;
}
if intrinsic == Some("transmute") {
let &(ref dest, target) = destination.as_ref().unwrap();
self.trans_transmute(&bcx, &args[0], dest);

View File

@ -0,0 +1,29 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// compile-flags: -C no-prepopulate-passes
#![feature(core_intrinsics)]
#![crate_type = "lib"]
// test that `move_val_init` actually avoids big allocas
use std::intrinsics::move_val_init;
pub struct Big {
pub data: [u8; 65536]
}
// CHECK-LABEL: @test_mvi
#[no_mangle]
pub unsafe fn test_mvi(target: *mut Big, make_big: fn() -> Big) {
// CHECK: call void %1(%Big*{{[^%]*}} %0)
move_val_init(target, make_big());
}