diff options
| author | Brian Anderson <banderson@mozilla.com> | 2012-02-13 14:59:05 -0800 |
|---|---|---|
| committer | Brian Anderson <banderson@mozilla.com> | 2012-02-14 11:24:09 -0800 |
| commit | d7c8cacfde6c2dc08bb526a244cabf83af94eaa8 (patch) | |
| tree | bf49e4a84e00cb66f003c4a0036bbaa503f7f648 /src/comp | |
| parent | 4cc1e31f74e2714c10ca06daa7a446a32fda6b2a (diff) | |
| download | rust-d7c8cacfde6c2dc08bb526a244cabf83af94eaa8.tar.gz rust-d7c8cacfde6c2dc08bb526a244cabf83af94eaa8.zip | |
rustc: Extract native function generation into trans::native
Diffstat (limited to 'src/comp')
| -rw-r--r-- | src/comp/middle/trans/base.rs | 185 | ||||
| -rw-r--r-- | src/comp/middle/trans/native.rs | 191 | ||||
| -rw-r--r-- | src/comp/rustc.rc | 1 |
3 files changed, 194 insertions, 183 deletions
diff --git a/src/comp/middle/trans/base.rs b/src/comp/middle/trans/base.rs index 8769efcb4b4..2750a345ae0 100644 --- a/src/comp/middle/trans/base.rs +++ b/src/comp/middle/trans/base.rs @@ -4458,180 +4458,6 @@ fn trans_const(cx: @crate_ctxt, e: @ast::expr, id: ast::node_id) { } } -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 { - 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()) - }; - } - _ { - // Precondition? - ccx.tcx.sess.bug("c_stack_tys called on non-function type"); - } - } -} - -// 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 -// arguments into a struct. It then uses a small bit of assembly to switch -// over to the C stack and invoke the shim function. The shim function S then -// unpacks the arguments from the struct and invokes the actual function F -// according to its specified calling convention. -// -// Example: Given a native c-stack function F(x: X, y: Y) -> Z, -// we generate a wrapper function W that looks like: -// -// void W(Z* dest, void *env, X x, Y y) { -// struct { X x; Y y; Z *z; } args = { x, y, z }; -// call_on_c_stack_shim(S, &args); -// } -// -// The shim function S then looks something like: -// -// void S(struct { X x; Y y; Z *z; } *args) { -// *args->z = F(args->x, args->y); -// } -// -// However, if the return type of F is dynamically sized or of aggregate type, -// the shim function looks like: -// -// void S(struct { X x; Y y; Z *z; } *args) { -// F(args->z, args->x, args->y); -// } -// -// Note: on i386, the layout of the args struct is generally the same as the -// desired layout of the arguments on the C stack. Therefore, we could use -// upcall_alloc_c_stack() to allocate the `args` structure and switch the -// stack pointer appropriately to avoid a round of copies. (In fact, the shim -// function itself is unnecessary). We used to do this, in fact, and will -// perhaps do so in the future. -fn trans_native_mod(ccx: @crate_ctxt, - native_mod: ast::native_mod, abi: ast::native_abi) { - fn build_shim_fn(ccx: @crate_ctxt, - native_item: @ast::native_item, - tys: @c_stack_tys, - cc: lib::llvm::CallConv) -> ValueRef { - 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 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 = tys.arg_tys.len(); - 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; - } - - fn build_wrap_fn(ccx: @crate_ctxt, - tys: @c_stack_tys, - 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 = tys.arg_tys.len(); - 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]); - - // 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); - } - - let cc = lib::llvm::CCallConv; - alt abi { - ast::native_abi_rust_intrinsic { ret; } - ast::native_abi_cdecl { cc = lib::llvm::CCallConv; } - ast::native_abi_stdcall { cc = lib::llvm::X86StdcallCallConv; } - } - - for native_item in native_mod.items { - alt native_item.node { - ast::native_item_fn(fn_decl, tps) { - let id = native_item.id; - let tys = c_stack_tys(ccx, id); - alt ccx.item_ids.find(id) { - some(llwrapfn) { - let llshimfn = build_shim_fn(ccx, native_item, tys, cc); - build_wrap_fn(ccx, tys, tps.len(), llshimfn, llwrapfn); - } - none { - ccx.sess.span_fatal( - native_item.span, - "unbound function item in trans_native_mod"); - } - } - } - } - } -} - fn trans_item(ccx: @crate_ctxt, item: ast::item) { let path = alt ccx.tcx.items.get(item.id) { ast_map::node_item(_, p) { p } @@ -4690,7 +4516,7 @@ fn trans_item(ccx: @crate_ctxt, item: ast::item) { either::right(abi_) { abi_ } either::left(msg) { ccx.sess.span_fatal(item.span, msg) } }; - trans_native_mod(ccx, native_mod, abi); + native::trans_native_mod(ccx, native_mod, abi); } _ {/* fall through */ } } @@ -4837,13 +4663,6 @@ fn fill_fn_pair(bcx: @block_ctxt, pair: ValueRef, llfn: ValueRef, Store(bcx, llenvblobptr, env_cell); } -fn link_name(i: @ast::native_item) -> str { - alt attr::get_meta_item_value_str_by_name(i.attrs, "link_name") { - none { ret i.ident; } - option::some(ln) { ret ln; } - } -} - fn collect_native_item(ccx: @crate_ctxt, abi: @mutable option<ast::native_abi>, i: @ast::native_item) { @@ -4872,7 +4691,7 @@ fn collect_native_item(ccx: @crate_ctxt, let fn_type = type_of_fn_from_ty( ccx, node_type, vec::map(tps, {|p| param_bounds(ccx, p)})); - let ri_name = "rust_intrinsic_" + link_name(i); + let ri_name = "rust_intrinsic_" + native::link_name(i); let llnativefn = get_extern_fn( ccx.externs, ccx.llmod, ri_name, lib::llvm::CCallConv, fn_type); diff --git a/src/comp/middle/trans/native.rs b/src/comp/middle/trans/native.rs new file mode 100644 index 00000000000..67d320fa668 --- /dev/null +++ b/src/comp/middle/trans/native.rs @@ -0,0 +1,191 @@ +import driver::session::session; +import ctypes::c_uint; +import front::attr; +import lib::llvm::{ llvm, TypeRef, ValueRef }; +import syntax::ast; +import common::*; +import build::*; +import base::*; + +export link_name, trans_native_mod; + +fn link_name(i: @ast::native_item) -> str { + alt attr::get_meta_item_value_str_by_name(i.attrs, "link_name") { + none { ret i.ident; } + option::some(ln) { ret ln; } + } +} + +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 { + 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()) + }; + } + _ { + // Precondition? + ccx.tcx.sess.bug("c_stack_tys called on non-function type"); + } + } +} + +// 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 +// arguments into a struct. It then uses a small bit of assembly to switch +// over to the C stack and invoke the shim function. The shim function S then +// unpacks the arguments from the struct and invokes the actual function F +// according to its specified calling convention. +// +// Example: Given a native c-stack function F(x: X, y: Y) -> Z, +// we generate a wrapper function W that looks like: +// +// void W(Z* dest, void *env, X x, Y y) { +// struct { X x; Y y; Z *z; } args = { x, y, z }; +// call_on_c_stack_shim(S, &args); +// } +// +// The shim function S then looks something like: +// +// void S(struct { X x; Y y; Z *z; } *args) { +// *args->z = F(args->x, args->y); +// } +// +// However, if the return type of F is dynamically sized or of aggregate type, +// the shim function looks like: +// +// void S(struct { X x; Y y; Z *z; } *args) { +// F(args->z, args->x, args->y); +// } +// +// Note: on i386, the layout of the args struct is generally the same as the +// desired layout of the arguments on the C stack. Therefore, we could use +// upcall_alloc_c_stack() to allocate the `args` structure and switch the +// stack pointer appropriately to avoid a round of copies. (In fact, the shim +// function itself is unnecessary). We used to do this, in fact, and will +// perhaps do so in the future. +fn trans_native_mod(ccx: @crate_ctxt, + native_mod: ast::native_mod, abi: ast::native_abi) { + fn build_shim_fn(ccx: @crate_ctxt, + native_item: @ast::native_item, + tys: @c_stack_tys, + cc: lib::llvm::CallConv) -> ValueRef { + 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 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; + } + + fn build_wrap_fn(ccx: @crate_ctxt, + tys: @c_stack_tys, + 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; + } + 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); + } + + let cc = lib::llvm::CCallConv; + alt abi { + ast::native_abi_rust_intrinsic { ret; } + ast::native_abi_cdecl { cc = lib::llvm::CCallConv; } + ast::native_abi_stdcall { cc = lib::llvm::X86StdcallCallConv; } + } + + for native_item in native_mod.items { + alt native_item.node { + ast::native_item_fn(fn_decl, tps) { + let id = native_item.id; + let tys = c_stack_tys(ccx, id); + alt ccx.item_ids.find(id) { + some(llwrapfn) { + let llshimfn = build_shim_fn(ccx, native_item, tys, cc); + build_wrap_fn(ccx, tys, vec::len(tps), llshimfn, llwrapfn); + } + none { + ccx.sess.span_fatal( + native_item.span, + "unbound function item in trans_native_mod"); + } + } + } + } + } +} diff --git a/src/comp/rustc.rc b/src/comp/rustc.rc index bfb26382a84..ea65b970e3b 100644 --- a/src/comp/rustc.rc +++ b/src/comp/rustc.rc @@ -23,6 +23,7 @@ mod middle { mod closure; mod tvec; mod impl; + mod native; } mod ty; mod ast_map; |
