Skip to content

Commit

Permalink
auto merge of rust-lang#16081 : luqmana/rust/nr, r=pcwalton
Browse files Browse the repository at this point in the history
  • Loading branch information
bors committed Aug 12, 2014
2 parents c7d0b52 + 71e19d5 commit e2273d9
Show file tree
Hide file tree
Showing 14 changed files with 406 additions and 52 deletions.
7 changes: 7 additions & 0 deletions src/libcore/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,13 @@ extern "rust-intrinsic" {
/// ```
pub fn transmute<T,U>(e: T) -> U;

/// Gives the address for the return value of the enclosing function.
///
/// Using this instrinsic in a function that does not use an out pointer
/// will trigger a compiler error.
#[cfg(not(stage0))]
pub fn return_address() -> *const u8;

/// Returns `true` if a type requires drop glue.
pub fn needs_drop<T>() -> bool;

Expand Down
190 changes: 162 additions & 28 deletions src/librustc/middle/trans/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1122,7 +1122,7 @@ pub fn memcpy_ty(bcx: &Block, dst: ValueRef, src: ValueRef, t: ty::t) {
let llalign = llalign_of_min(ccx, llty);
call_memcpy(bcx, dst, src, llsz, llalign as u32);
} else {
Store(bcx, Load(bcx, src), dst);
store_ty(bcx, Load(bcx, src), dst, t);
}
}

Expand Down Expand Up @@ -1210,15 +1210,120 @@ pub fn arrayalloca(cx: &Block, ty: Type, v: ValueRef) -> ValueRef {
p
}

// Creates and returns space for, or returns the argument representing, the
// slot where the return value of the function must go.
pub fn make_return_pointer(fcx: &FunctionContext, output_type: ty::t)
-> ValueRef {
if type_of::return_uses_outptr(fcx.ccx, output_type) {
get_param(fcx.llfn, 0)
// Creates the alloca slot which holds the pointer to the slot for the final return value
pub fn make_return_slot_pointer(fcx: &FunctionContext, output_type: ty::t) -> ValueRef {
let lloutputtype = type_of::type_of(fcx.ccx, output_type);

// We create an alloca to hold a pointer of type `output_type`
// which will hold the pointer to the right alloca which has the
// final ret value
if fcx.needs_ret_allocas {
// Let's create the stack slot
let slot = AllocaFcx(fcx, lloutputtype.ptr_to(), "llretslotptr");

// and if we're using an out pointer, then store that in our newly made slot
if type_of::return_uses_outptr(fcx.ccx, output_type) {
let outptr = get_param(fcx.llfn, 0);

let b = fcx.ccx.builder();
b.position_before(fcx.alloca_insert_pt.get().unwrap());
b.store(outptr, slot);
}

slot

// But if there are no nested returns, we skip the indirection and have a single
// retslot
} else {
let lloutputtype = type_of::type_of(fcx.ccx, output_type);
AllocaFcx(fcx, lloutputtype, "__make_return_pointer")
if type_of::return_uses_outptr(fcx.ccx, output_type) {
get_param(fcx.llfn, 0)
} else {
AllocaFcx(fcx, lloutputtype, "sret_slot")
}
}
}

struct CheckForNestedReturnsVisitor {
found: bool
}

impl Visitor<bool> for CheckForNestedReturnsVisitor {
fn visit_expr(&mut self, e: &ast::Expr, in_return: bool) {
match e.node {
ast::ExprRet(..) if in_return => {
self.found = true;
return;
}
ast::ExprRet(..) => visit::walk_expr(self, e, true),
_ => visit::walk_expr(self, e, in_return)
}
}
}

fn has_nested_returns(tcx: &ty::ctxt, id: ast::NodeId) -> bool {
match tcx.map.find(id) {
Some(ast_map::NodeItem(i)) => {
match i.node {
ast::ItemFn(_, _, _, _, blk) => {
let mut explicit = CheckForNestedReturnsVisitor { found: false };
let mut implicit = CheckForNestedReturnsVisitor { found: false };
visit::walk_item(&mut explicit, &*i, false);
visit::walk_expr_opt(&mut implicit, blk.expr, true);
explicit.found || implicit.found
}
_ => tcx.sess.bug("unexpected item variant in has_nested_returns")
}
}
Some(ast_map::NodeTraitMethod(trait_method)) => {
match *trait_method {
ast::Provided(m) => {
match m.node {
ast::MethDecl(_, _, _, _, _, _, blk, _) => {
let mut explicit = CheckForNestedReturnsVisitor { found: false };
let mut implicit = CheckForNestedReturnsVisitor { found: false };
visit::walk_method_helper(&mut explicit, &*m, false);
visit::walk_expr_opt(&mut implicit, blk.expr, true);
explicit.found || implicit.found
}
ast::MethMac(_) => tcx.sess.bug("unexpanded macro")
}
}
ast::Required(_) => tcx.sess.bug("unexpected variant: required trait method in \
has_nested_returns")
}
}
Some(ast_map::NodeMethod(m)) => {
match m.node {
ast::MethDecl(_, _, _, _, _, _, blk, _) => {
let mut explicit = CheckForNestedReturnsVisitor { found: false };
let mut implicit = CheckForNestedReturnsVisitor { found: false };
visit::walk_method_helper(&mut explicit, &*m, false);
visit::walk_expr_opt(&mut implicit, blk.expr, true);
explicit.found || implicit.found
}
ast::MethMac(_) => tcx.sess.bug("unexpanded macro")
}
}
Some(ast_map::NodeExpr(e)) => {
match e.node {
ast::ExprFnBlock(_, blk) | ast::ExprProc(_, blk) | ast::ExprUnboxedFn(_, blk) => {
let mut explicit = CheckForNestedReturnsVisitor { found: false };
let mut implicit = CheckForNestedReturnsVisitor { found: false };
visit::walk_expr(&mut explicit, &*e, false);
visit::walk_expr_opt(&mut implicit, blk.expr, true);
explicit.found || implicit.found
}
_ => tcx.sess.bug("unexpected expr variant in has_nested_returns")
}
}

Some(ast_map::NodeVariant(..)) | Some(ast_map::NodeStructCtor(..)) => false,

// glue, shims, etc
None if id == ast::DUMMY_NODE_ID => false,

_ => tcx.sess.bug(format!("unexpected variant in has_nested_returns: {}",
tcx.map.path_to_string(id)).as_slice())
}
}

Expand Down Expand Up @@ -1254,13 +1359,15 @@ pub fn new_fn_ctxt<'a>(ccx: &'a CrateContext,
let substd_output_type = output_type.substp(ccx.tcx(), param_substs);
let uses_outptr = type_of::return_uses_outptr(ccx, substd_output_type);
let debug_context = debuginfo::create_function_debug_context(ccx, id, param_substs, llfndecl);
let nested_returns = has_nested_returns(ccx.tcx(), id);

let mut fcx = FunctionContext {
llfn: llfndecl,
llenv: None,
llretptr: Cell::new(None),
llretslotptr: Cell::new(None),
alloca_insert_pt: Cell::new(None),
llreturn: Cell::new(None),
needs_ret_allocas: nested_returns,
personality: Cell::new(None),
caller_expects_out_pointer: uses_outptr,
llargs: RefCell::new(NodeMap::new()),
Expand Down Expand Up @@ -1303,12 +1410,12 @@ pub fn init_function<'a>(fcx: &'a FunctionContext<'a>,

if !return_type_is_void(fcx.ccx, substd_output_type) {
// If the function returns nil/bot, there is no real return
// value, so do not set `llretptr`.
// value, so do not set `llretslotptr`.
if !skip_retptr || fcx.caller_expects_out_pointer {
// Otherwise, we normally allocate the llretptr, unless we
// Otherwise, we normally allocate the llretslotptr, unless we
// have been instructed to skip it for immediate return
// values.
fcx.llretptr.set(Some(make_return_pointer(fcx, substd_output_type)));
fcx.llretslotptr.set(Some(make_return_slot_pointer(fcx, substd_output_type)));
}
}

Expand Down Expand Up @@ -1533,13 +1640,18 @@ pub fn finish_fn<'a>(fcx: &'a FunctionContext<'a>,

// Builds the return block for a function.
pub fn build_return_block(fcx: &FunctionContext, ret_cx: &Block, retty: ty::t) {
// Return the value if this function immediate; otherwise, return void.
if fcx.llretptr.get().is_none() || fcx.caller_expects_out_pointer {
if fcx.llretslotptr.get().is_none() ||
(!fcx.needs_ret_allocas && fcx.caller_expects_out_pointer) {
return RetVoid(ret_cx);
}

let retptr = Value(fcx.llretptr.get().unwrap());
let retval = match retptr.get_dominating_store(ret_cx) {
let retslot = if fcx.needs_ret_allocas {
Load(ret_cx, fcx.llretslotptr.get().unwrap())
} else {
fcx.llretslotptr.get().unwrap()
};
let retptr = Value(retslot);
match retptr.get_dominating_store(ret_cx) {
// If there's only a single store to the ret slot, we can directly return
// the value that was stored and omit the store and the alloca
Some(s) => {
Expand All @@ -1550,17 +1662,29 @@ pub fn build_return_block(fcx: &FunctionContext, ret_cx: &Block, retty: ty::t) {
retptr.erase_from_parent();
}

if ty::type_is_bool(retty) {
let retval = if ty::type_is_bool(retty) {
Trunc(ret_cx, retval, Type::i1(fcx.ccx))
} else {
retval
};

if fcx.caller_expects_out_pointer {
store_ty(ret_cx, retval, get_param(fcx.llfn, 0), retty);
return RetVoid(ret_cx);
} else {
return Ret(ret_cx, retval);
}
}
// Otherwise, load the return value from the ret slot
None => load_ty(ret_cx, fcx.llretptr.get().unwrap(), retty)
};

Ret(ret_cx, retval);
// Otherwise, copy the return value to the ret slot
None => {
if fcx.caller_expects_out_pointer {
memcpy_ty(ret_cx, get_param(fcx.llfn, 0), retslot, retty);
return RetVoid(ret_cx);
} else {
return Ret(ret_cx, load_ty(ret_cx, retslot, retty));
}
}
}
}

#[deriving(Clone, Eq, PartialEq)]
Expand Down Expand Up @@ -1658,10 +1782,10 @@ pub fn trans_closure(ccx: &CrateContext,
// emitting should be enabled.
debuginfo::start_emitting_source_locations(&fcx);

let dest = match fcx.llretptr.get() {
Some(e) => {expr::SaveIn(e)}
let dest = match fcx.llretslotptr.get() {
Some(_) => expr::SaveIn(fcx.get_ret_slot(bcx, block_ty, "iret_slot")),
None => {
assert!(type_is_zero_size(bcx.ccx(), block_ty))
assert!(type_is_zero_size(bcx.ccx(), block_ty));
expr::Ignore
}
};
Expand All @@ -1672,6 +1796,13 @@ pub fn trans_closure(ccx: &CrateContext,
// (trans_block, trans_expr, et cetera).
bcx = controlflow::trans_block(bcx, body, dest);

match dest {
expr::SaveIn(slot) if fcx.needs_ret_allocas => {
Store(bcx, slot, fcx.llretslotptr.get().unwrap());
}
_ => {}
}

match fcx.llreturn.get() {
Some(_) => {
Br(bcx, fcx.return_exit_block());
Expand Down Expand Up @@ -1836,21 +1967,24 @@ fn trans_enum_variant_or_tuple_like_struct(ccx: &CrateContext,
param_substs, None, &arena, TranslateItems);
let bcx = init_function(&fcx, false, result_ty);

assert!(!fcx.needs_ret_allocas);

let arg_tys = ty::ty_fn_args(ctor_ty);

let arg_datums = create_datums_for_fn_args(&fcx, arg_tys.as_slice());

if !type_is_zero_size(fcx.ccx, result_ty) {
let dest = fcx.get_ret_slot(bcx, result_ty, "eret_slot");
let repr = adt::represent_type(ccx, result_ty);
for (i, arg_datum) in arg_datums.move_iter().enumerate() {
let lldestptr = adt::trans_field_ptr(bcx,
&*repr,
fcx.llretptr.get().unwrap(),
dest,
disr,
i);
arg_datum.store_to(bcx, lldestptr);
}
adt::trans_set_discr(bcx, &*repr, fcx.llretptr.get().unwrap(), disr);
adt::trans_set_discr(bcx, &*repr, dest, disr);
}

finish_fn(&fcx, bcx, result_ty);
Expand Down
16 changes: 10 additions & 6 deletions src/librustc/middle/trans/callee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,7 @@ pub fn trans_unboxing_shim(bcx: &Block,
let return_type = ty::ty_fn_ret(boxed_function_type);
let fcx = new_fn_ctxt(ccx,
llfn,
-1,
ast::DUMMY_NODE_ID,
false,
return_type,
&empty_param_substs,
Expand Down Expand Up @@ -389,6 +389,11 @@ pub fn trans_unboxing_shim(bcx: &Block,
for i in range(1, arg_types.len()) {
llshimmedargs.push(get_param(fcx.llfn, fcx.arg_pos(i) as u32));
}
assert!(!fcx.needs_ret_allocas);
let dest = match fcx.llretslotptr.get() {
Some(_) => Some(expr::SaveIn(fcx.get_ret_slot(bcx, return_type, "ret_slot"))),
None => None
};
bcx = trans_call_inner(bcx,
None,
function_type,
Expand All @@ -399,10 +404,7 @@ pub fn trans_unboxing_shim(bcx: &Block,
}
},
ArgVals(llshimmedargs.as_slice()),
match fcx.llretptr.get() {
None => None,
Some(llretptr) => Some(expr::SaveIn(llretptr)),
}).bcx;
dest).bcx;

bcx = fcx.pop_and_trans_custom_cleanup_scope(bcx, arg_scope);
finish_fn(&fcx, bcx, return_type);
Expand Down Expand Up @@ -758,9 +760,11 @@ pub fn trans_call_inner<'a>(
assert!(abi == synabi::RustIntrinsic);
assert!(dest.is_some());

let call_info = call_info.expect("no call info for intrinsic call?");
return intrinsic::trans_intrinsic_call(bcx, node, callee_ty,
arg_cleanup_scope, args,
dest.unwrap(), substs);
dest.unwrap(), substs,
call_info);
}
NamedTupleConstructor(substs, disr) => {
assert!(dest.is_some());
Expand Down
7 changes: 4 additions & 3 deletions src/librustc/middle/trans/closure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -574,24 +574,25 @@ pub fn get_wrapper_for_bare_fn(ccx: &CrateContext,

let arena = TypedArena::new();
let empty_param_substs = param_substs::empty();
let fcx = new_fn_ctxt(ccx, llfn, -1, true, f.sig.output,
let fcx = new_fn_ctxt(ccx, llfn, ast::DUMMY_NODE_ID, true, f.sig.output,
&empty_param_substs, None, &arena, TranslateItems);
let bcx = init_function(&fcx, true, f.sig.output);

let args = create_datums_for_fn_args(&fcx,
ty::ty_fn_args(closure_ty)
.as_slice());
let mut llargs = Vec::new();
match fcx.llretptr.get() {
match fcx.llretslotptr.get() {
Some(llretptr) => {
assert!(!fcx.needs_ret_allocas);
llargs.push(llretptr);
}
None => {}
}
llargs.extend(args.iter().map(|arg| arg.val));

let retval = Call(bcx, fn_ptr, llargs.as_slice(), None);
if type_is_zero_size(ccx, f.sig.output) || fcx.llretptr.get().is_some() {
if type_is_zero_size(ccx, f.sig.output) || fcx.llretslotptr.get().is_some() {
RetVoid(bcx);
} else {
Ret(bcx, retval);
Expand Down
Loading

0 comments on commit e2273d9

Please sign in to comment.