diff --git a/src/comp/back/upcall.rs b/src/comp/back/upcall.rs index e3a124640e4..7e86191caf6 100644 --- a/src/comp/back/upcall.rs +++ b/src/comp/back/upcall.rs @@ -28,6 +28,7 @@ type upcalls = dynastack_free: ValueRef, alloc_c_stack: ValueRef, call_shim_on_c_stack: ValueRef, + call_shim_on_rust_stack: ValueRef, rust_personality: ValueRef, reset_stack_limit: ValueRef}; @@ -106,6 +107,9 @@ fn declare_upcalls(targ_cfg: @session::config, // arguments: void *args, void *fn_ptr [T_ptr(T_i8()), T_ptr(T_i8())], int_t), + call_shim_on_rust_stack: + d("call_shim_on_rust_stack", + [T_ptr(T_i8()), T_ptr(T_i8())], int_t), rust_personality: d("rust_personality", [], T_i32()), reset_stack_limit: diff --git a/src/comp/middle/trans/base.rs b/src/comp/middle/trans/base.rs index a1656d50e8e..f5f924b5691 100644 --- a/src/comp/middle/trans/base.rs +++ b/src/comp/middle/trans/base.rs @@ -4193,14 +4193,17 @@ fn copy_args_to_allocas(fcx: @fn_ctxt, bcx: @block_ctxt, args: [ast::arg], // Ties up the llstaticallocas -> llloadenv -> llderivedtydescs -> // lldynamicallocas -> lltop edges, and builds the return block. fn finish_fn(fcx: @fn_ctxt, lltop: BasicBlockRef) { + tie_up_header_blocks(fcx, lltop); + let ret_cx = new_raw_block_ctxt(fcx, fcx.llreturn); + trans_fn_cleanups(fcx, ret_cx); + RetVoid(ret_cx); +} + +fn tie_up_header_blocks(fcx: @fn_ctxt, lltop: BasicBlockRef) { Br(new_raw_block_ctxt(fcx, fcx.llstaticallocas), fcx.llloadenv); Br(new_raw_block_ctxt(fcx, fcx.llloadenv), fcx.llderivedtydescs_first); Br(new_raw_block_ctxt(fcx, fcx.llderivedtydescs), fcx.lldynamicallocas); Br(new_raw_block_ctxt(fcx, fcx.lldynamicallocas), lltop); - - let ret_cx = new_raw_block_ctxt(fcx, fcx.llreturn); - trans_fn_cleanups(fcx, ret_cx); - RetVoid(ret_cx); } enum self_arg { impl_self(ty::t), no_self, } @@ -4552,13 +4555,20 @@ fn param_bounds(ccx: @crate_ctxt, tp: ast::ty_param) -> ty::param_bounds { ccx.tcx.ty_param_bounds.get(tp.id) } -fn register_fn_full(ccx: @crate_ctxt, sp: span, path: path, _flav: str, +fn register_fn_full(ccx: @crate_ctxt, sp: span, path: path, flav: str, tps: [ast::ty_param], node_id: ast::node_id, node_type: ty::t) { let llfty = type_of_fn_from_ty(ccx, node_type, vec::map(tps, {|p| param_bounds(ccx, p)})); + register_fn_fuller(ccx, sp, path, flav, node_id, node_type, + lib::llvm::CCallConv, llfty); +} + +fn register_fn_fuller(ccx: @crate_ctxt, sp: span, path: path, _flav: str, + node_id: ast::node_id, node_type: ty::t, + cc: lib::llvm::CallConv, llfty: TypeRef) { let ps: str = mangle_exported_name(ccx, path, node_type); - let llfn: ValueRef = decl_cdecl_fn(ccx.llmod, ps, llfty); + let llfn: ValueRef = decl_fn(ccx.llmod, ps, cc, llfty); ccx.item_ids.insert(node_id, llfn); ccx.item_symbols.insert(node_id, ps); @@ -4747,9 +4757,13 @@ fn collect_item(ccx: @crate_ctxt, abi: @mutable option, either::right(abi_) { *abi = option::some(abi_); } } } - ast::item_fn(_, tps, _) { - register_fn(ccx, i.span, my_path, "fn", tps, - i.id); + ast::item_fn(decl, tps, _) { + if decl.purity != ast::crust_fn { + register_fn(ccx, i.span, my_path, "fn", tps, + i.id); + } else { + native::register_crust_fn(ccx, i.span, my_path, i.id); + } } ast::item_impl(tps, _, _, methods) { let path = my_path + [path_name(int::str(i.id))]; diff --git a/src/comp/middle/trans/native.rs b/src/comp/middle/trans/native.rs index 93f753455bf..3830c363879 100644 --- a/src/comp/middle/trans/native.rs +++ b/src/comp/middle/trans/native.rs @@ -1,13 +1,15 @@ import driver::session::session; +import syntax::codemap::span; import ctypes::c_uint; import front::attr; import lib::llvm::{ llvm, TypeRef, ValueRef }; import syntax::ast; +import back::link; import common::*; import build::*; import base::*; -export link_name, trans_native_mod, trans_crust_fn; +export link_name, trans_native_mod, register_crust_fn, trans_crust_fn; fn link_name(i: @ast::native_item) -> str { alt attr::get_meta_item_value_str_by_name(i.attrs, "link_name") { @@ -20,34 +22,110 @@ type c_stack_tys = { arg_tys: [TypeRef], ret_ty: TypeRef, ret_def: bool, - base_fn_ty: TypeRef, bundle_ty: TypeRef, shim_fn_ty: TypeRef }; -fn c_stack_tys(ccx: @crate_ctxt, - id: ast::node_id) -> @c_stack_tys { +fn c_arg_and_ret_lltys(ccx: @crate_ctxt, + id: ast::node_id) -> ([TypeRef], TypeRef, ty::t) { alt ty::get(ty::node_id_to_type(ccx.tcx, id)).struct { ty::ty_fn({inputs: arg_tys, output: ret_ty, _}) { let llargtys = type_of_explicit_args(ccx, arg_tys); let llretty = type_of(ccx, ret_ty); - let bundle_ty = T_struct(llargtys + [T_ptr(llretty)]); - ret @{ - arg_tys: llargtys, - ret_ty: llretty, - ret_def: !ty::type_is_bot(ret_ty) && !ty::type_is_nil(ret_ty), - base_fn_ty: T_fn(llargtys, llretty), - bundle_ty: bundle_ty, - shim_fn_ty: T_fn([T_ptr(bundle_ty)], T_void()) - }; + (llargtys, llretty, ret_ty) } _ { // Precondition? - ccx.tcx.sess.bug("c_stack_tys called on non-function type"); + ccx.tcx.sess.bug("c_arg_and_ret_lltys called on non-function type"); } } } +fn c_stack_tys(ccx: @crate_ctxt, + id: ast::node_id) -> @c_stack_tys { + let (llargtys, llretty, ret_ty) = c_arg_and_ret_lltys(ccx, id); + let bundle_ty = T_struct(llargtys + [T_ptr(llretty)]); + ret @{ + arg_tys: llargtys, + ret_ty: llretty, + ret_def: !ty::type_is_bot(ret_ty) && !ty::type_is_nil(ret_ty), + bundle_ty: bundle_ty, + shim_fn_ty: T_fn([T_ptr(bundle_ty)], T_void()) + }; +} + +type shim_arg_builder = fn(bcx: @block_ctxt, tys: @c_stack_tys, + llargbundle: ValueRef) -> [ValueRef]; + +type shim_ret_builder = fn(bcx: @block_ctxt, tys: @c_stack_tys, + llargbundle: ValueRef, llretval: ValueRef); + +fn build_shim_fn_(ccx: @crate_ctxt, + shim_name: str, + llbasefn: ValueRef, + tys: @c_stack_tys, + cc: lib::llvm::CallConv, + arg_builder: shim_arg_builder, + ret_builder: shim_ret_builder) -> ValueRef { + + let llshimfn = decl_internal_cdecl_fn( + ccx.llmod, shim_name, tys.shim_fn_ty); + + // Declare the body of the shim function: + let fcx = new_fn_ctxt(ccx, [], llshimfn, none); + let bcx = new_top_block_ctxt(fcx, none); + let lltop = bcx.llbb; + let llargbundle = llvm::LLVMGetParam(llshimfn, 0 as c_uint); + let llargvals = arg_builder(bcx, tys, llargbundle); + + // Create the call itself and store the return value: + let llretval = CallWithConv(bcx, llbasefn, + llargvals, cc); // r + + ret_builder(bcx, tys, llargbundle, llretval); + + build_return(bcx); + finish_fn(fcx, lltop); + + ret llshimfn; +} + +type wrap_arg_builder = fn(bcx: @block_ctxt, tys: @c_stack_tys, + llwrapfn: ValueRef, + llargbundle: ValueRef); + +type wrap_ret_builder = fn(bcx: @block_ctxt, tys: @c_stack_tys, + llargbundle: ValueRef); + +fn build_wrap_fn_(ccx: @crate_ctxt, + tys: @c_stack_tys, + llshimfn: ValueRef, + llwrapfn: ValueRef, + shim_upcall: ValueRef, + arg_builder: wrap_arg_builder, + ret_builder: wrap_ret_builder) { + + let fcx = new_fn_ctxt(ccx, [], llwrapfn, none); + let bcx = new_top_block_ctxt(fcx, none); + let lltop = bcx.llbb; + + // Allocate the struct and write the arguments into it. + let llargbundle = alloca(bcx, tys.bundle_ty); + arg_builder(bcx, tys, llwrapfn, llargbundle); + + // Create call itself. + let llshimfnptr = PointerCast(bcx, llshimfn, T_ptr(T_i8())); + let llrawargbundle = PointerCast(bcx, llargbundle, T_ptr(T_i8())); + Call(bcx, shim_upcall, [llrawargbundle, llshimfnptr]); + ret_builder(bcx, tys, llargbundle); + + tie_up_header_blocks(fcx, lltop); + + // Make sure our standard return block (that we didn't use) is terminated + let ret_cx = new_raw_block_ctxt(fcx, fcx.llreturn); + Unreachable(ret_cx); +} + // For each native function F, we generate a wrapper function W and a shim // function S that all work together. The wrapper function W is the function // that other rust code actually invokes. Its job is to marshall the @@ -89,46 +167,41 @@ fn trans_native_mod(ccx: @crate_ctxt, native_item: @ast::native_item, tys: @c_stack_tys, cc: lib::llvm::CallConv) -> ValueRef { + + fn build_args(bcx: @block_ctxt, tys: @c_stack_tys, + llargbundle: ValueRef) -> [ValueRef] { + let llargvals = []; + let i = 0u; + let n = vec::len(tys.arg_tys); + while i < n { + let llargval = load_inbounds(bcx, llargbundle, [0, i as int]); + llargvals += [llargval]; + i += 1u; + } + ret llargvals; + } + + fn build_ret(bcx: @block_ctxt, tys: @c_stack_tys, + llargbundle: ValueRef, llretval: ValueRef) { + if tys.ret_def { + let n = vec::len(tys.arg_tys); + // R** llretptr = &args->r; + let llretptr = GEPi(bcx, llargbundle, [0, n as int]); + // R* llretloc = *llretptr; /* (args->r) */ + let llretloc = Load(bcx, llretptr); + // *args->r = r; + Store(bcx, llretval, llretloc); + } + } + let lname = link_name(native_item); - // Declare the "prototype" for the base function F: - let llbasefn = decl_fn(ccx.llmod, lname, cc, tys.base_fn_ty); - - // Create the shim function: + let llbasefnty = T_fn(tys.arg_tys, tys.ret_ty); + let llbasefn = decl_fn(ccx.llmod, lname, cc, llbasefnty); + // Name the shim function let shim_name = lname + "__c_stack_shim"; - let llshimfn = decl_internal_cdecl_fn( - ccx.llmod, shim_name, tys.shim_fn_ty); - - // Declare the body of the shim function: - let fcx = new_fn_ctxt(ccx, [], llshimfn, none); - let bcx = new_top_block_ctxt(fcx, none); - let lltop = bcx.llbb; - let llargbundle = llvm::LLVMGetParam(llshimfn, 0 as c_uint); - let i = 0u, n = vec::len(tys.arg_tys); - let llargvals = []; - while i < n { - let llargval = load_inbounds(bcx, llargbundle, [0, i as int]); - llargvals += [llargval]; - i += 1u; - } - - // Create the call itself and store the return value: - let llretval = CallWithConv(bcx, llbasefn, - llargvals, cc); // r - if tys.ret_def { - // R** llretptr = &args->r; - let llretptr = GEPi(bcx, llargbundle, [0, n as int]); - // R* llretloc = *llretptr; /* (args->r) */ - let llretloc = Load(bcx, llretptr); - // *args->r = r; - Store(bcx, llretval, llretloc); - } - - // Finish up: - build_return(bcx); - finish_fn(fcx, lltop); - - ret llshimfn; + ret build_shim_fn_(ccx, shim_name, llbasefn, tys, cc, + build_args, build_ret); } fn build_wrap_fn(ccx: @crate_ctxt, @@ -136,30 +209,32 @@ fn trans_native_mod(ccx: @crate_ctxt, num_tps: uint, llshimfn: ValueRef, llwrapfn: ValueRef) { - let fcx = new_fn_ctxt(ccx, [], llwrapfn, none); - let bcx = new_top_block_ctxt(fcx, none); - let lltop = bcx.llbb; - // Allocate the struct and write the arguments into it. - let llargbundle = alloca(bcx, tys.bundle_ty); - let i = 0u, n = vec::len(tys.arg_tys); - let implicit_args = 2u + num_tps; // ret + env - while i < n { - let llargval = llvm::LLVMGetParam(llwrapfn, - (i + implicit_args) as c_uint); - store_inbounds(bcx, llargval, llargbundle, [0, i as int]); - i += 1u; + fn build_args(bcx: @block_ctxt, tys: @c_stack_tys, + llwrapfn: ValueRef, llargbundle: ValueRef, + num_tps: uint) { + let i = 0u, n = vec::len(tys.arg_tys); + let implicit_args = 2u + num_tps; // ret + env + while i < n { + let llargval = llvm::LLVMGetParam( + llwrapfn, + (i + implicit_args) as c_uint); + store_inbounds(bcx, llargval, llargbundle, [0, i as int]); + i += 1u; + } + let llretptr = llvm::LLVMGetParam(llwrapfn, 0 as c_uint); + store_inbounds(bcx, llretptr, llargbundle, [0, n as int]); } - let llretptr = llvm::LLVMGetParam(llwrapfn, 0 as c_uint); - store_inbounds(bcx, llretptr, llargbundle, [0, n as int]); - // Create call itself. - let call_shim_on_c_stack = ccx.upcalls.call_shim_on_c_stack; - let llshimfnptr = PointerCast(bcx, llshimfn, T_ptr(T_i8())); - let llrawargbundle = PointerCast(bcx, llargbundle, T_ptr(T_i8())); - Call(bcx, call_shim_on_c_stack, [llrawargbundle, llshimfnptr]); - build_return(bcx); - finish_fn(fcx, lltop); + fn build_ret(bcx: @block_ctxt, _tys: @c_stack_tys, + _llargbundle: ValueRef) { + RetVoid(bcx); + } + + build_wrap_fn_(ccx, tys, llshimfn, llwrapfn, + ccx.upcalls.call_shim_on_c_stack, + bind build_args(_, _ ,_ , _, num_tps), + build_ret); } let cc = lib::llvm::CCallConv; @@ -180,7 +255,7 @@ fn trans_native_mod(ccx: @crate_ctxt, build_wrap_fn(ccx, tys, vec::len(tps), llshimfn, llwrapfn); } none { - ccx.sess.span_fatal( + ccx.sess.span_bug( native_item.span, "unbound function item in trans_native_mod"); } @@ -191,6 +266,96 @@ fn trans_native_mod(ccx: @crate_ctxt, } fn trans_crust_fn(ccx: @crate_ctxt, path: ast_map::path, decl: ast::fn_decl, - body: ast::blk, llfndecl: ValueRef, id: ast::node_id) { - trans_fn(ccx, path, decl, body, llfndecl, no_self, [], none, id) + body: ast::blk, llwrapfn: ValueRef, id: ast::node_id) { + + fn build_rust_fn(ccx: @crate_ctxt, path: ast_map::path, + decl: ast::fn_decl, body: ast::blk, + id: ast::node_id) -> ValueRef { + let t = ty::node_id_to_type(ccx.tcx, id); + let ps = link::mangle_internal_name_by_path( + ccx, path + [ast_map::path_name("__rust_abi")]); + let llty = type_of_fn_from_ty(ccx, t, []); + let llfndecl = decl_internal_cdecl_fn(ccx.llmod, ps, llty); + trans_fn(ccx, path, decl, body, llfndecl, no_self, [], none, id); + ret llfndecl; + } + + fn build_shim_fn(ccx: @crate_ctxt, path: ast_map::path, + llrustfn: ValueRef, tys: @c_stack_tys) -> ValueRef { + + fn build_args(bcx: @block_ctxt, tys: @c_stack_tys, + llargbundle: ValueRef) -> [ValueRef] { + let llargvals = []; + let i = 0u; + let n = vec::len(tys.arg_tys); + let llretptr = load_inbounds(bcx, llargbundle, [0, n as int]); + llargvals += [llretptr]; + let llenvptr = C_null(T_opaque_box_ptr(bcx_ccx(bcx))); + llargvals += [llenvptr]; + while i < n { + let llargval = load_inbounds(bcx, llargbundle, [0, i as int]); + llargvals += [llargval]; + i += 1u; + } + ret llargvals; + } + + fn build_ret(_bcx: @block_ctxt, _tys: @c_stack_tys, + _llargbundle: ValueRef, _llretval: ValueRef) { + // Nop. The return pointer in the Rust ABI function + // is wired directly into the return slot in the shim struct + } + + let shim_name = link::mangle_internal_name_by_path( + ccx, path + [ast_map::path_name("__rust_stack_shim")]); + ret build_shim_fn_(ccx, shim_name, llrustfn, tys, + lib::llvm::CCallConv, + build_args, build_ret); + } + + fn build_wrap_fn(ccx: @crate_ctxt, llshimfn: ValueRef, + llwrapfn: ValueRef, tys: @c_stack_tys) { + + fn build_args(bcx: @block_ctxt, tys: @c_stack_tys, + llwrapfn: ValueRef, llargbundle: ValueRef) { + let llretptr = alloca(bcx, tys.ret_ty); + let i = 0u, n = vec::len(tys.arg_tys); + while i < n { + let llargval = llvm::LLVMGetParam( + llwrapfn, i as c_uint); + store_inbounds(bcx, llargval, llargbundle, [0, i as int]); + i += 1u; + } + store_inbounds(bcx, llretptr, llargbundle, [0, n as int]); + } + + fn build_ret(bcx: @block_ctxt, tys: @c_stack_tys, + llargbundle: ValueRef) { + let n = vec::len(tys.arg_tys); + let llretval = load_inbounds(bcx, llargbundle, [0, n as int]); + let llretval = Load(bcx, llretval); + Ret(bcx, llretval); + } + + build_wrap_fn_(ccx, tys, llshimfn, llwrapfn, + ccx.upcalls.call_shim_on_rust_stack, + build_args, build_ret); + } + + let tys = c_stack_tys(ccx, id); + // The internal Rust ABI function - runs on the Rust stack + let llrustfn = build_rust_fn(ccx, path, decl, body, id); + // The internal shim function - runs on the Rust stack + let llshimfn = build_shim_fn(ccx, path, llrustfn, tys); + // The external C function - runs on the C stack + build_wrap_fn(ccx, llshimfn, llwrapfn, tys) +} + +fn register_crust_fn(ccx: @crate_ctxt, sp: span, + path: ast_map::path, node_id: ast::node_id) { + let t = ty::node_id_to_type(ccx.tcx, node_id); + let (llargtys, llretty, _) = c_arg_and_ret_lltys(ccx, node_id); + let llfty = T_fn(llargtys, llretty); + register_fn_fuller(ccx, sp, path, "crust fn", node_id, + t, lib::llvm::CCallConv, llfty); } \ No newline at end of file diff --git a/src/rt/rust_builtin.cpp b/src/rt/rust_builtin.cpp index 0b4ce047997..918630b18f6 100644 --- a/src/rt/rust_builtin.cpp +++ b/src/rt/rust_builtin.cpp @@ -657,8 +657,8 @@ rust_dbg_lock_signal(lock_and_signal *lock) { typedef void *(*dbg_callback)(void*); extern "C" CDECL void * -rust_dbg_call(dbg_callback *cb, void *data) { - return (*cb)(data); +rust_dbg_call(dbg_callback cb, void *data) { + return cb(data); } // diff --git a/src/test/run-pass/crust-call-deep.rs b/src/test/run-pass/crust-call-deep.rs new file mode 100644 index 00000000000..230117ee9fc --- /dev/null +++ b/src/test/run-pass/crust-call-deep.rs @@ -0,0 +1,23 @@ +native mod rustrt { + fn rust_dbg_call(cb: *u8, + data: ctypes::uintptr_t) -> ctypes::uintptr_t; +} + +crust fn cb(data: ctypes::uintptr_t) -> ctypes::uintptr_t { + if data == 1u { + data + } else { + count(data - 1u) + 1u + } +} + +fn count(n: uint) -> uint { + #debug("n = %?", n); + rustrt::rust_dbg_call(cb, n) +} + +fn main() { + let result = count(1000u); + #debug("result = %?", result); + assert result == 1000u; +} \ No newline at end of file diff --git a/src/test/run-pass/crust-call.rs b/src/test/run-pass/crust-call.rs new file mode 100644 index 00000000000..16ec9f3953c --- /dev/null +++ b/src/test/run-pass/crust-call.rs @@ -0,0 +1,23 @@ +native mod rustrt { + fn rust_dbg_call(cb: *u8, + data: ctypes::uintptr_t) -> ctypes::uintptr_t; +} + +crust fn cb(data: ctypes::uintptr_t) -> ctypes::uintptr_t { + if data == 1u { + data + } else { + fact(data - 1u) * data + } +} + +fn fact(n: uint) -> uint { + #debug("n = %?", n); + rustrt::rust_dbg_call(cb, n) +} + +fn main() { + let result = fact(10u); + #debug("result = %?", result); + assert result == 3628800u; +} \ No newline at end of file