about summary refs log tree commit diff
path: root/src/comp/middle
diff options
context:
space:
mode:
authorNiko Matsakis <niko@alum.mit.edu>2011-11-17 18:45:18 -0800
committerNiko Matsakis <niko@alum.mit.edu>2011-11-18 16:32:19 -0800
commit6072ddad33e034a63bddd1ef577492b2842e8136 (patch)
tree76ba850c486969d08d66efe0fe3478b1058e6573 /src/comp/middle
parentd6ab8ebb07d0f6360d2aa32dec71c24a32c21ea6 (diff)
downloadrust-6072ddad33e034a63bddd1ef577492b2842e8136.tar.gz
rust-6072ddad33e034a63bddd1ef577492b2842e8136.zip
sketch out design
Diffstat (limited to 'src/comp/middle')
-rw-r--r--src/comp/middle/trans.rs152
1 files changed, 98 insertions, 54 deletions
diff --git a/src/comp/middle/trans.rs b/src/comp/middle/trans.rs
index 78d2577f4ae..58252ff9330 100644
--- a/src/comp/middle/trans.rs
+++ b/src/comp/middle/trans.rs
@@ -5366,7 +5366,7 @@ fn c_stack_tys(ccx: @crate_ctxt,
       ty::ty_native_fn(_, arg_tys, ret_ty) {
         let llargtys = type_of_explicit_args(ccx, sp, arg_tys);
         check non_ty_var(ccx, ret_ty); // NDM does this truly hold?
-        let llretty = type_of_inner(ccx, sp, ret_ty);
+        let llretty = T_ptr(type_of_inner(ccx, sp, ret_ty));
         let bundle_ty = T_struct(llargtys + [llretty]);
         ret @{
             arg_tys: llargtys,
@@ -5385,31 +5385,58 @@ fn c_stack_tys(ccx: @crate_ctxt,
     }
 }
 
-// For c-stack ABIs, we must generate shim functions for making
-// the call.  These shim functions will unpack arguments out of
-// a struct and then invoke the base function.
+// 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 shim function S that is something like:
+// we generate a wrapper function W that looks like:
 //
-//     void S(struct F_Args { X x; Y y; Z *z; } *args) {
+//    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(lcx: @local_ctxt, native_mod: ast::native_mod) {
     fn build_shim_fn(lcx: @local_ctxt,
                      native_item: @ast::native_item,
-                     llshimfn: ValueRef,
-                     cc: uint) {
+                     tys: @c_stack_tys,
+                     cc: uint) -> ValueRef {
         let lname = link_name(native_item);
         let ccx = lcx_ccx(lcx);
         let span = native_item.span;
-        let id = native_item.id;
-        let tys = c_stack_tys(ccx, span, id);
 
         // 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(lcx, span, llshimfn);
         let bcx = new_top_block_ctxt(fcx);
@@ -5423,11 +5450,39 @@ fn trans_native_mod(lcx: @local_ctxt, native_mod: ast::native_mod) {
             i += 1u;
         }
 
-        // Create the call itself:
+        // Create the call itself and store the return value:
         let llretval = CallWithConv(bcx, llbasefn, llargvals, cc);
-        store_inbounds(bcx, llretval, llargbundle, [0, n as int]);
+        store_inbounds(bcx, llretval, llargbundle, [0, n as int, 0]);
+
+        // Finish up:
+        build_return(bcx);
+        finish_fn(fcx, lltop);
+
+        ret llshimfn;
+    }
+
+    fn build_wrap_fn(lcx: @local_ctxt,
+                     native_item: @ast::native_item,
+                     tys: @c_stack_tys,
+                     llshimfn: ValueRef,
+                     llwrapfn: ValueRef) {
+        let fcx = new_fn_ctxt(lcx, span, llshimfn);
+        let bcx = new_top_block_ctxt(fcx);
+        let lltop = bcx.llbb;
+
+        // Allocate the struct and write the arguments into it.
+        let llargbundle = alloca(bcx, tys.bundle_ty);
+        let imp = 2u, i = 0u, n = vec::len(tys.arg_tys);
+        while i < n {
+            let llargval = llvm::LLVMGetParam(llwrapfn, i + imp);
+            store_inbounds(bcx, llargval, llargbundle, [0, i as int]);
+            i += 1u;
+        }
+        let llretptr = llvm::LLVMGetParam(llwrapfn, 0);
+        store_inbounds(bcx, llretptr, llargbundle, [0, n as int]);
+
+        // Create call itself:
 
-        // Finish up.
         build_return(bcx);
         finish_fn(fcx, lltop);
     }
@@ -5445,9 +5500,11 @@ fn trans_native_mod(lcx: @local_ctxt, native_mod: ast::native_mod) {
         ast::native_item_ty. {}
         ast::native_item_fn(fn_decl, _) {
           let id = native_item.id;
+          let tys = c_stack_tys(ccx, span, id);
           alt ccx.item_ids.find(id) {
-            some(llshimfn) {
-              build_shim_fn(lcx, native_item, llshimfn, cc);
+            some(llwrapfn) {
+              let llshimfn = build_shim_fn(lcx, native_item, cc, tys);
+              build_wrap_fn(lcx, native_item, tys, llshimfn, llwrapfn);
             }
 
             none. {
@@ -5548,14 +5605,6 @@ fn register_fn_full(ccx: @crate_ctxt, sp: span, path: [str], _flav: str,
     let path = path;
     let llfty =
         type_of_fn_from_ty(ccx, sp, node_type, std::vec::len(ty_params));
-    alt ty::struct(ccx.tcx, node_type) {
-      ty::ty_fn(_, inputs, output, rs, _) {
-        check non_ty_var(ccx, output);
-        llfty = type_of_fn(ccx, sp, false, ast_util::ret_by_ref(rs), inputs,
-                           output, vec::len(ty_params));
-      }
-      _ { ccx.sess.bug("register_fn(): fn item doesn't have fn type!"); }
-    }
     let ps: str = mangle_exported_name(ccx, path, node_type);
     let llfn: ValueRef = decl_cdecl_fn(ccx.llmod, ps, llfty);
     ccx.item_ids.insert(node_id, llfn);
@@ -5715,33 +5764,6 @@ fn raw_native_fn_type(ccx: @crate_ctxt, sp: span, args: [ty::arg],
     ret T_fn(type_of_explicit_args(ccx, sp, args), type_of(ccx, sp, ret_ty));
 }
 
-fn register_native_fn(ccx: @crate_ctxt, sp: span, _path: [str], name: str,
-                      id: ast::node_id) {
-    let fn_type = node_id_type(ccx, id); // NB: has no type params
-    let abi = ty::ty_fn_abi(ccx.tcx, fn_type);
-
-    alt abi {
-      ast::native_abi_rust_intrinsic. {
-        let num_ty_param = native_fn_ty_param_count(ccx, id);
-        let fn_type = native_fn_wrapper_type(ccx, sp, num_ty_param, fn_type);
-        let ri_name = "rust_intrinsic_" + name;
-        let llnativefn = get_extern_fn(ccx.externs, ccx.llmod, ri_name,
-                                       lib::llvm::LLVMCCallConv, fn_type);
-        ccx.item_ids.insert(id, llnativefn);
-        ccx.item_symbols.insert(id, ri_name);
-      }
-
-      ast::native_abi_cdecl. | ast::native_abi_stdcall. {
-        let tys = c_stack_tys(ccx, sp, id);
-        let shim_name = name + "__c_stack_shim";
-        let llshimfn = decl_internal_cdecl_fn(
-            ccx.llmod, shim_name, tys.shim_fn_ty);
-        ccx.item_ids.insert(id, llshimfn);
-        ccx.item_symbols.insert(id, shim_name);
-      }
-    }
-}
-
 fn item_path(item: @ast::item) -> [str] { ret [item.ident]; }
 
 fn link_name(i: @ast::native_item) -> str {
@@ -5751,14 +5773,36 @@ fn link_name(i: @ast::native_item) -> str {
     }
 }
 
-
 fn collect_native_item(ccx: @crate_ctxt, i: @ast::native_item, &&pt: [str],
                        _v: vt<[str]>) {
     alt i.node {
-      ast::native_item_fn(_, _) {
+      ast::native_item_fn(_, tps) {
         if !ccx.obj_methods.contains_key(i.id) {
-            let name = link_name(i);
-            register_native_fn(ccx, i.span, pt, name, i.id);
+            // FIXME NDM abi should come from attr
+            let abi = ty::ty_fn_abi(ccx.tcx, fn_type);
+
+            alt abi {
+              ast::native_abi_rust_intrinsic. {
+                // For intrinsics: link the function directly to the intrinsic
+                // function itself.
+                let num_ty_param = vec::len(tps);
+                let node_type = node_id_type(ccx, id);
+                let fn_type = type_of_fn_from_ty(ccx, sp, node_type, num_ty_param);
+                let ri_name = "rust_intrinsic_" + name;
+                let llnativefn = get_extern_fn(ccx.externs, ccx.llmod, ri_name,
+                                               lib::llvm::LLVMCCallConv, fn_type);
+                ccx.item_ids.insert(id, llnativefn);
+                ccx.item_symbols.insert(id, ri_name);
+              }
+
+              ast::native_abi_cdecl. | ast::native_abi_stdcall. {
+                // For true external functions: create a rust wrapper
+                // and link to that.  The rust wrapper will handle
+                // switching to the C stack.
+                let new_pt = pt + [i.ident];
+                register_fn(ccx, i.span, new_pt, "native fn", tps, i.id);
+              }
+            }
         }
       }
       _ { }