From a5b422fe6ba916b8761a70f3043982ce0af77956 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Thu, 29 Sep 2011 17:33:41 -0700 Subject: [PATCH] rustc: Translate calls on the C stack --- src/comp/middle/trans.rs | 124 +++++++++++++++++++++++++++++++-------- 1 file changed, 98 insertions(+), 26 deletions(-) diff --git a/src/comp/middle/trans.rs b/src/comp/middle/trans.rs index 765e5815983..35ecd147082 100644 --- a/src/comp/middle/trans.rs +++ b/src/comp/middle/trans.rs @@ -3897,6 +3897,11 @@ fn trans_call(in_cx: @block_ctxt, f: @ast::expr, // with trans_call. let tcx = bcx_tcx(in_cx); let fn_expr_ty = ty::expr_ty(tcx, f); + + if check type_is_native_fn_on_c_stack(tcx, fn_expr_ty) { + ret trans_c_stack_native_call(in_cx, f, args); + } + let by_ref = ast_util::ret_by_ref(ty::ty_fn_ret_style(tcx, fn_expr_ty)); let cx = new_scope_block_ctxt(in_cx, "call"); let f_res = trans_callee(cx, f); @@ -3977,6 +3982,63 @@ fn trans_call(in_cx: @block_ctxt, f: @ast::expr, ret {res: rslt(bcx, retval), by_ref: by_ref}; } +// Translates a native call on the C stack. Calls into the runtime to perform +// the stack switching operation. +fn trans_c_stack_native_call(bcx: @block_ctxt, f: @ast::expr, + args: [@ast::expr]) + -> {res: result, by_ref: bool} { + let ccx = bcx_ccx(bcx); + let f_res = trans_callee(bcx, f); + let llfn = f_res.val; bcx = f_res.bcx; + + // Translate the callee. + let fn_ty = ty::expr_ty(bcx_tcx(bcx), f); + let fn_arg_tys = ty::ty_fn_args(bcx_tcx(bcx), fn_ty); + + // Translate arguments. + let (to_zero, to_revoke) = ([], []); + let llargs = vec::map2({ |ty_arg, arg| + let arg_ty = ty_arg.ty; + check type_has_static_size(ccx, arg_ty); + let llargty = type_of(ccx, f.span, arg_ty); + let r = trans_arg_expr(bcx, ty_arg, llargty, to_zero, to_revoke, arg); + let llargval = r.val; bcx = r.bcx; + { llval: llargval, llty: llargty } + }, fn_arg_tys, args); + + // Allocate the argument bundle. + let llargbundlety = T_struct(vec::map({ |r| r.llty }, llargs)); + let llargbundlesz = llsize_of(llargbundlety); + let llrawargbundle = Call(bcx, ccx.upcalls.alloc_c_stack, + [llargbundlesz]); + let llargbundle = PointerCast(bcx, llrawargbundle, T_ptr(llargbundlety)); + + // Copy in arguments. + log_err ("bundle type", val_str(ccx.tn, llargbundle)); + vec::eachi({ |llarg, i| + // FIXME: This load is unfortunate. + let llargval = Load(bcx, llarg.llval); + log_err ("llarg type", val_str(ccx.tn, llargval), i); + store_inbounds(bcx, llargval, llargbundle, [C_int(0), C_uint(i)]); + }, llargs); + + // Call. + // TODO: Invoke instead. + let llrawretval = Call(bcx, ccx.upcalls.call_c_stack, + [llfn, llrawargbundle]); + + // Cast return type. + let ret_ty = ty::ty_fn_ret(bcx_tcx(bcx), fn_ty); + check type_has_static_size(ccx, ret_ty); + let llretty = type_of(ccx, f.span, ret_ty); + let llretval = TruncOrBitCast(bcx, llrawretval, llretty); + + // Forget about anything we moved out. + bcx = zero_and_revoke(bcx, to_zero, to_revoke); + + ret {res: rslt(bcx, llretval), by_ref: false}; +} + fn zero_and_revoke(bcx: @block_ctxt, to_zero: [{v: ValueRef, t: ty::t}], to_revoke: [{v: ValueRef, t: ty::t}]) -> @block_ctxt { @@ -5662,7 +5724,7 @@ fn native_fn_ty_param_count(cx: @crate_ctxt, id: ast::node_id) -> uint { ret count; } -fn native_abi_requires_pair(abi: ast::native_abi) -> bool { +pure fn native_abi_requires_pair(abi: ast::native_abi) -> bool { alt abi { ast::native_abi_rust. | ast::native_abi_cdecl. | ast::native_abi_llvm. | ast::native_abi_rust_intrinsic. | @@ -5671,6 +5733,13 @@ fn native_abi_requires_pair(abi: ast::native_abi) -> bool { } } +pure fn type_is_native_fn_on_c_stack(tcx: ty::ctxt, t: ty::t) -> bool { + alt ty::struct(tcx, t) { + ty::ty_native_fn(abi, _, _) { ret !native_abi_requires_pair(abi); } + _ { ret false; } + } +} + fn native_fn_wrapper_type(cx: @crate_ctxt, sp: span, ty_param_count: uint, x: ty::t) -> TypeRef { alt ty::struct(cx.tcx, x) { @@ -5690,32 +5759,8 @@ fn raw_native_fn_type(ccx: @crate_ctxt, sp: span, args: [ty::arg], fn register_native_fn(ccx: @crate_ctxt, sp: span, path: [str], name: str, id: ast::node_id) { - let path = path; - let num_ty_param = native_fn_ty_param_count(ccx, id); - // Declare the wrapper. - - let t = node_id_type(ccx, id); - let wrapper_type = native_fn_wrapper_type(ccx, sp, num_ty_param, t); - let ps: str = mangle_exported_name(ccx, path, node_id_type(ccx, id)); - let wrapper_fn = decl_cdecl_fn(ccx.llmod, ps, wrapper_type); - ccx.item_ids.insert(id, wrapper_fn); - ccx.item_symbols.insert(id, ps); - - // Build the wrapper. - let fcx = new_fn_ctxt(new_local_ctxt(ccx), sp, wrapper_fn); - let bcx = new_top_block_ctxt(fcx); - let lltop = bcx.llbb; - - // Declare the function itself. let fn_type = node_id_type(ccx, id); // NB: has no type params - let abi = ty::ty_fn_abi(ccx.tcx, fn_type); - // FIXME: If the returned type is not nil, then we assume it's 32 bits - // wide. This is obviously wildly unsafe. We should have a better FFI - // that allows types of different sizes to be returned. - - let rty = ty::ty_fn_ret(ccx.tcx, fn_type); - let rty_is_nil = ty::type_is_nil(ccx.tcx, rty); let pass_task; let uses_retptr; @@ -5747,10 +5792,37 @@ fn register_native_fn(ccx: @crate_ctxt, sp: span, path: [str], name: str, cast_to_i32 = true; } ast::native_abi_c_stack_cdecl. { - fail "C stack cdecl ABI shouldn't have a wrapper"; + let llfn = decl_cdecl_fn(ccx.llmod, name, T_fn([], T_void())); + ccx.item_ids.insert(id, llfn); + ccx.item_symbols.insert(id, name); + ret; } } + let path = path; + let num_ty_param = native_fn_ty_param_count(ccx, id); + // Declare the wrapper. + + let t = node_id_type(ccx, id); + let wrapper_type = native_fn_wrapper_type(ccx, sp, num_ty_param, t); + let ps: str = mangle_exported_name(ccx, path, node_id_type(ccx, id)); + let wrapper_fn = decl_cdecl_fn(ccx.llmod, ps, wrapper_type); + ccx.item_ids.insert(id, wrapper_fn); + ccx.item_symbols.insert(id, ps); + + // Build the wrapper. + let fcx = new_fn_ctxt(new_local_ctxt(ccx), sp, wrapper_fn); + let bcx = new_top_block_ctxt(fcx); + let lltop = bcx.llbb; + + // Declare the function itself. + // FIXME: If the returned type is not nil, then we assume it's 32 bits + // wide. This is obviously wildly unsafe. We should have a better FFI + // that allows types of different sizes to be returned. + + let rty = ty::ty_fn_ret(ccx.tcx, fn_type); + let rty_is_nil = ty::type_is_nil(ccx.tcx, rty); + let lltaskptr; if cast_to_i32 { lltaskptr = vp2i(bcx, fcx.lltaskptr);