aboutsummaryrefslogtreecommitdiff
path: root/src/boot/llvm
diff options
context:
space:
mode:
authorGraydon Hoare <[email protected]>2010-06-23 21:03:09 -0700
committerGraydon Hoare <[email protected]>2010-06-23 21:03:09 -0700
commitd6b7c96c3eb29b9244ece0c046d3f372ff432d04 (patch)
treeb425187e232966063ffc2f0d14c04a55d8f004ef /src/boot/llvm
parentInitial git commit. (diff)
downloadrust-d6b7c96c3eb29b9244ece0c046d3f372ff432d04.tar.xz
rust-d6b7c96c3eb29b9244ece0c046d3f372ff432d04.zip
Populate tree.
Diffstat (limited to 'src/boot/llvm')
-rw-r--r--src/boot/llvm/llabi.ml69
-rw-r--r--src/boot/llvm/llasm.ml192
-rw-r--r--src/boot/llvm/llemit.ml36
-rw-r--r--src/boot/llvm/llfinal.ml96
-rw-r--r--src/boot/llvm/lltrans.ml938
5 files changed, 1331 insertions, 0 deletions
diff --git a/src/boot/llvm/llabi.ml b/src/boot/llvm/llabi.ml
new file mode 100644
index 00000000..fd5d9277
--- /dev/null
+++ b/src/boot/llvm/llabi.ml
@@ -0,0 +1,69 @@
+(*
+ * LLVM integration with the Rust runtime.
+ *)
+
+type abi = {
+ crate_ty: Llvm.lltype;
+ task_ty: Llvm.lltype;
+ word_ty: Llvm.lltype;
+ rust_start: Llvm.llvalue;
+};;
+
+let declare_abi (llctx:Llvm.llcontext) (llmod:Llvm.llmodule) : abi =
+ let i32 = Llvm.i32_type llctx in
+
+ let crate_ty =
+ (* TODO: other architectures besides x86 *)
+ let crate_opaque_ty = Llvm.opaque_type llctx in
+ let crate_tyhandle = Llvm.handle_to_type (Llvm.struct_type llctx [|
+ i32; (* ptrdiff_t image_base_off *)
+ Llvm.pointer_type crate_opaque_ty;(* uintptr_t self_addr *)
+ i32; (* ptrdiff_t debug_abbrev_off *)
+ i32; (* size_t debug_abbrev_sz *)
+ i32; (* ptrdiff_t debug_info_off *)
+ i32; (* size_t debug_info_sz *)
+ i32; (* size_t activate_glue_off *)
+ i32; (* size_t main_exit_task_glue_off *)
+ i32; (* size_t unwind_glue_off *)
+ i32; (* size_t yield_glue_off *)
+ i32; (* int n_rust_syms *)
+ i32; (* int n_c_syms *)
+ i32 (* int n_libs *)
+ |])
+ in
+ Llvm.refine_type crate_opaque_ty (Llvm.type_of_handle crate_tyhandle);
+ Llvm.type_of_handle crate_tyhandle
+ in
+ ignore (Llvm.define_type_name "rust_crate" crate_ty llmod);
+
+ let task_ty =
+ (* TODO: other architectures besides x86 *)
+ Llvm.struct_type llctx [|
+ i32; (* size_t refcnt *)
+ Llvm.pointer_type i32; (* stk_seg *stk *)
+ Llvm.pointer_type i32; (* uintptr_t runtime_sp *)
+ Llvm.pointer_type i32; (* uintptr_t rust_sp *)
+ Llvm.pointer_type i32; (* rust_rt *rt *)
+ Llvm.pointer_type i32 (* rust_crate_cache *cache *)
+ |]
+ in
+ ignore (Llvm.define_type_name "rust_task" task_ty llmod);
+
+ let rust_start_ty =
+ let task_ptr_ty = Llvm.pointer_type task_ty in
+ let llnilty = Llvm.array_type (Llvm.i1_type llctx) 0 in
+ let main_ty = Llvm.function_type (Llvm.void_type llctx)
+ [| Llvm.pointer_type llnilty; task_ptr_ty; |]
+ in
+ let args_ty = Array.map Llvm.pointer_type [| main_ty; crate_ty; |] in
+ let args_ty = Array.append args_ty [| i32; i32 |] in
+ Llvm.function_type i32 args_ty
+ in
+ {
+ crate_ty = crate_ty;
+ task_ty = task_ty;
+ word_ty = i32;
+ rust_start = Llvm.declare_function "rust_start" rust_start_ty llmod
+ }
+;;
+
diff --git a/src/boot/llvm/llasm.ml b/src/boot/llvm/llasm.ml
new file mode 100644
index 00000000..56448b07
--- /dev/null
+++ b/src/boot/llvm/llasm.ml
@@ -0,0 +1,192 @@
+(*
+ * machine-specific assembler routines.
+ *)
+
+open Common;;
+
+type asm_glue =
+ {
+ asm_activate_glue : Llvm.llvalue;
+ asm_yield_glue : Llvm.llvalue;
+ asm_upcall_glues : Llvm.llvalue array;
+ }
+;;
+
+let n_upcall_glues = 7
+;;
+
+(* x86-specific asm. *)
+
+let x86_glue
+ (llctx:Llvm.llcontext)
+ (llmod:Llvm.llmodule)
+ (abi:Llabi.abi)
+ (sess:Session.sess)
+ : asm_glue =
+ let (prefix,align) =
+ match sess.Session.sess_targ with
+ Linux_x86_elf
+ | Win32_x86_pe -> ("",4)
+ | MacOS_x86_macho -> ("_", 16)
+ in
+ let save_callee_saves =
+ ["pushl %ebp";
+ "pushl %edi";
+ "pushl %esi";
+ "pushl %ebx";]
+ in
+ let restore_callee_saves =
+ ["popl %ebx";
+ "popl %esi";
+ "popl %edi";
+ "popl %ebp";]
+ in
+ let load_esp_from_rust_sp = ["movl 12(%edx), %esp"] in
+ let load_esp_from_runtime_sp = ["movl 8(%edx), %esp"] in
+ let store_esp_to_rust_sp = ["movl %esp, 12(%edx)"] in
+ let store_esp_to_runtime_sp = ["movl %esp, 8(%edx)"] in
+ let list_init i f = (Array.to_list (Array.init i f)) in
+ let list_init_concat i f = List.concat (list_init i f) in
+
+ let glue =
+ [
+ ("rust_activate_glue",
+ String.concat "\n\t"
+ (["movl 4(%esp), %edx # edx = rust_task"]
+ @ save_callee_saves
+ @ store_esp_to_runtime_sp
+ @ load_esp_from_rust_sp
+ (*
+ * This 'add' instruction is a bit surprising.
+ * See lengthy comment in boot/be/x86.ml activate_glue.
+ *)
+ @ ["addl $20, 12(%edx)"]
+ @ restore_callee_saves
+ @ ["ret"]));
+
+ ("rust_yield_glue",
+ String.concat "\n\t"
+
+ (["movl 0(%esp), %edx # edx = rust_task"]
+ @ load_esp_from_rust_sp
+ @ save_callee_saves
+ @ store_esp_to_rust_sp
+ @ load_esp_from_runtime_sp
+ @ restore_callee_saves
+ @ ["ret"]))
+ ]
+ @ list_init n_upcall_glues
+ begin
+ fun i ->
+ (*
+ * 0, 4, 8, 12 are callee-saves
+ * 16 is retpc
+ * 20 is taskptr
+ * 24 is callee
+ * 28 .. (7+i) * 4 are args
+ *)
+
+ ((Printf.sprintf "rust_upcall_%d" i),
+ String.concat "\n\t"
+ (save_callee_saves
+ @ ["movl %esp, %ebp # ebp = rust_sp";
+ "movl 20(%esp), %edx # edx = rust_task"]
+ @ store_esp_to_rust_sp
+ @ load_esp_from_runtime_sp
+ @ [Printf.sprintf
+ "subl $%d, %%esp # esp -= args" ((i+1)*4);
+ "andl $~0xf, %esp # align esp down";
+ "movl %edx, (%esp) # arg[0] = rust_task "]
+
+ @ (list_init_concat i
+ begin
+ fun j ->
+ [ Printf.sprintf "movl %d(%%ebp),%%edx" ((j+7)*4);
+ Printf.sprintf "movl %%edx,%d(%%esp)" ((j+1)*4) ]
+ end)
+
+ @ ["movl 24(%ebp), %edx # edx = callee";
+ "call *%edx # call *%edx";
+ "movl 20(%ebp), %edx # edx = rust_task"]
+ @ load_esp_from_rust_sp
+ @ restore_callee_saves
+ @ ["ret"]))
+ end
+ in
+
+ let _ =
+ Llvm.set_module_inline_asm llmod
+ begin
+ String.concat "\n"
+ begin
+ List.map
+ begin
+ fun (sym,asm) ->
+ Printf.sprintf
+ "\t.globl %s%s\n\t.balign %d\n%s%s:\n\t%s"
+ prefix sym align prefix sym asm
+ end
+ glue
+ end
+ end
+ in
+
+ let decl_cdecl_fn name out_ty arg_tys =
+ let ty = Llvm.function_type out_ty arg_tys in
+ let fn = Llvm.declare_function name ty llmod in
+ Llvm.set_function_call_conv Llvm.CallConv.c fn;
+ fn
+ in
+
+ let decl_glue s =
+ let task_ptr_ty = Llvm.pointer_type abi.Llabi.task_ty in
+ let void_ty = Llvm.void_type llctx in
+ decl_cdecl_fn s void_ty [| task_ptr_ty |]
+ in
+
+ let decl_upcall n =
+ let task_ptr_ty = Llvm.pointer_type abi.Llabi.task_ty in
+ let word_ty = abi.Llabi.word_ty in
+ let callee_ty = word_ty in
+ let args_ty =
+ Array.append
+ [| task_ptr_ty; callee_ty |]
+ (Array.init n (fun _ -> word_ty))
+ in
+ let name = Printf.sprintf "rust_upcall_%d" n in
+ decl_cdecl_fn name word_ty args_ty
+ in
+ {
+ asm_activate_glue = decl_glue "rust_activate_glue";
+ asm_yield_glue = decl_glue "rust_yield_glue";
+ asm_upcall_glues = Array.init n_upcall_glues decl_upcall;
+ }
+;;
+
+(* x64-specific asm. *)
+(* arm-specific asm. *)
+(* ... *)
+
+
+let get_glue
+ (llctx:Llvm.llcontext)
+ (llmod:Llvm.llmodule)
+ (abi:Llabi.abi)
+ (sess:Session.sess)
+ : asm_glue =
+ match sess.Session.sess_targ with
+ Linux_x86_elf
+ | Win32_x86_pe
+ | MacOS_x86_macho ->
+ x86_glue llctx llmod abi sess
+;;
+
+
+(*
+ * Local Variables:
+ * fill-column: 78;
+ * indent-tabs-mode: nil
+ * buffer-file-coding-system: utf-8-unix
+ * compile-command: "make -k -C ../.. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
+ * End:
+ *)
diff --git a/src/boot/llvm/llemit.ml b/src/boot/llvm/llemit.ml
new file mode 100644
index 00000000..2b229fde
--- /dev/null
+++ b/src/boot/llvm/llemit.ml
@@ -0,0 +1,36 @@
+(*
+ * LLVM emitter.
+ *)
+
+(* The top-level interface to the LLVM translation subsystem. *)
+let trans_and_process_crate
+ (sess:Session.sess)
+ (sem_cx:Semant.ctxt)
+ (crate:Ast.crate)
+ : unit =
+ let llcontext = Llvm.create_context () in
+ let emit_file (llmod:Llvm.llmodule) : unit =
+ let filename = Session.filename_of sess.Session.sess_out in
+ if not (Llvm_bitwriter.write_bitcode_file llmod filename)
+ then raise (Failure ("failed to write the LLVM bitcode '" ^ filename
+ ^ "'"))
+ in
+ let llmod = Lltrans.trans_crate sem_cx llcontext sess crate in
+ begin
+ try
+ emit_file llmod
+ with e -> Llvm.dispose_module llmod; raise e
+ end;
+ Llvm.dispose_module llmod;
+ Llvm.dispose_context llcontext
+;;
+
+(*
+ * Local Variables:
+ * fill-column: 78;
+ * indent-tabs-mode: nil
+ * buffer-file-coding-system: utf-8-unix
+ * compile-command: "make -k -C ../.. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
+ * End:
+ *)
+
diff --git a/src/boot/llvm/llfinal.ml b/src/boot/llvm/llfinal.ml
new file mode 100644
index 00000000..64ea3d37
--- /dev/null
+++ b/src/boot/llvm/llfinal.ml
@@ -0,0 +1,96 @@
+(*
+ * LLVM ABI-level stuff that needs to happen after modules have been
+ * translated.
+ *)
+
+let finalize_module
+ (llctx:Llvm.llcontext)
+ (llmod:Llvm.llmodule)
+ (abi:Llabi.abi)
+ (asm_glue:Llasm.asm_glue)
+ (exit_task_glue:Llvm.llvalue)
+ (crate_ptr:Llvm.llvalue)
+ : unit =
+ let i32 = Llvm.i32_type llctx in
+
+ (*
+ * Count the number of Rust functions and the number of C functions by
+ * simply (and crudely) testing whether each function in the module begins
+ * with "_rust_".
+ *)
+
+ let (rust_fn_count, c_fn_count) =
+ let count (rust_fn_count, c_fn_count) fn =
+ let begins_with prefix str =
+ let (str_len, prefix_len) =
+ (String.length str, String.length prefix)
+ in
+ prefix_len <= str_len && (String.sub str 0 prefix_len) = prefix
+ in
+ if begins_with "_rust_" (Llvm.value_name fn) then
+ (rust_fn_count + 1, c_fn_count)
+ else
+ (rust_fn_count, c_fn_count + 1)
+ in
+ Llvm.fold_left_functions count (0, 0) llmod
+ in
+
+ let crate_val =
+ let crate_addr = Llvm.const_ptrtoint crate_ptr i32 in
+ let glue_off glue =
+ let addr = Llvm.const_ptrtoint glue i32 in
+ Llvm.const_sub addr crate_addr
+ in
+ let activate_glue_off = glue_off asm_glue.Llasm.asm_activate_glue in
+ let yield_glue_off = glue_off asm_glue.Llasm.asm_yield_glue in
+ let exit_task_glue_off = glue_off exit_task_glue in
+
+ Llvm.const_struct llctx [|
+ Llvm.const_int i32 0; (* ptrdiff_t image_base_off *)
+ crate_ptr; (* uintptr_t self_addr *)
+ Llvm.const_int i32 0; (* ptrdiff_t debug_abbrev_off *)
+ Llvm.const_int i32 0; (* size_t debug_abbrev_sz *)
+ Llvm.const_int i32 0; (* ptrdiff_t debug_info_off *)
+ Llvm.const_int i32 0; (* size_t debug_info_sz *)
+ activate_glue_off; (* size_t activate_glue_off *)
+ exit_task_glue_off; (* size_t main_exit_task_glue_off *)
+ Llvm.const_int i32 0; (* size_t unwind_glue_off *)
+ yield_glue_off; (* size_t yield_glue_off *)
+ Llvm.const_int i32 rust_fn_count; (* int n_rust_syms *)
+ Llvm.const_int i32 c_fn_count; (* int n_c_syms *)
+ Llvm.const_int i32 0 (* int n_libs *)
+ |]
+ in
+
+ Llvm.set_initializer crate_val crate_ptr;
+
+ (* Define the main function for crt0 to call. *)
+ let main_fn =
+ let main_ty = Llvm.function_type i32 [| i32; i32 |] in
+ Llvm.define_function "main" main_ty llmod
+ in
+ let argc = Llvm.param main_fn 0 in
+ let argv = Llvm.param main_fn 1 in
+ let main_builder = Llvm.builder_at_end llctx (Llvm.entry_block main_fn) in
+ let rust_main_fn =
+ match Llvm.lookup_function "_rust_main" llmod with
+ None -> raise (Failure "no main function found")
+ | Some fn -> fn
+ in
+ let rust_start = abi.Llabi.rust_start in
+ let rust_start_args = [| rust_main_fn; crate_ptr; argc; argv |] in
+ ignore (Llvm.build_call
+ rust_start rust_start_args "start_rust" main_builder);
+ ignore (Llvm.build_ret (Llvm.const_int i32 0) main_builder)
+;;
+
+
+(*
+ * Local Variables:
+ * fill-column: 78;
+ * indent-tabs-mode: nil
+ * buffer-file-coding-system: utf-8-unix
+ * compile-command: "make -k -C ../.. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
+ * End:
+ *)
+
diff --git a/src/boot/llvm/lltrans.ml b/src/boot/llvm/lltrans.ml
new file mode 100644
index 00000000..7f985d25
--- /dev/null
+++ b/src/boot/llvm/lltrans.ml
@@ -0,0 +1,938 @@
+(*
+ * LLVM translator.
+ *)
+
+open Common;;
+open Transutil;;
+
+let log cx = Session.log "trans"
+ cx.Semant.ctxt_sess.Session.sess_log_trans
+ cx.Semant.ctxt_sess.Session.sess_log_out
+;;
+
+let trans_crate
+ (sem_cx:Semant.ctxt)
+ (llctx:Llvm.llcontext)
+ (sess:Session.sess)
+ (crate:Ast.crate)
+ : Llvm.llmodule =
+
+ let iflog thunk =
+ if sess.Session.sess_log_trans
+ then thunk ()
+ else ()
+ in
+
+ (* Helpers for adding metadata. *)
+ let (dbg_mdkind:int) = Llvm.mdkind_id llctx "dbg" in
+ let set_dbg_metadata (inst:Llvm.llvalue) (md:Llvm.llvalue) : unit =
+ Llvm.set_metadata inst dbg_mdkind md
+ in
+ let md_str (s:string) : Llvm.llvalue = Llvm.mdstring llctx s in
+ let md_node (vals:Llvm.llvalue array) : Llvm.llvalue =
+ Llvm.mdnode llctx vals
+ in
+ let const_i32 (i:int) : Llvm.llvalue =
+ Llvm.const_int (Llvm.i32_type llctx) i
+ in
+ let const_i1 (i:int) : Llvm.llvalue =
+ Llvm.const_int (Llvm.i1_type llctx) i
+ in
+ let llvm_debug_version : int = 0x8 lsl 16 in
+ let const_dw_tag (tag:Dwarf.dw_tag) : Llvm.llvalue =
+ const_i32 (llvm_debug_version lor (Dwarf.dw_tag_to_int tag))
+ in
+
+ (* Translation of our node_ids into LLVM identifiers, which are strings. *)
+ let next_anon_llid = ref 0 in
+ let num_llid num klass = Printf.sprintf "%s%d" klass num in
+ let anon_llid klass =
+ let llid = num_llid !next_anon_llid klass in
+ next_anon_llid := !next_anon_llid + 1;
+ llid
+ in
+ let node_llid (node_id_opt:node_id option) : (string -> string) =
+ match node_id_opt with
+ None -> anon_llid
+ | Some (Node num) -> num_llid num
+ in
+
+ (*
+ * Returns a bogus value for use in stub code that hasn't been implemented
+ * yet.
+ *
+ * TODO: On some joyous day, remove me.
+ *)
+ let bogus = Llvm.const_null (Llvm.i32_type llctx) in
+ let bogus_ptr = Llvm.const_null (Llvm.pointer_type (Llvm.i32_type llctx)) in
+
+ let llnilty = Llvm.array_type (Llvm.i1_type llctx) 0 in
+ let llnil = Llvm.const_array (Llvm.i1_type llctx) [| |] in
+
+ let ty_of_item = Hashtbl.find sem_cx.Semant.ctxt_all_item_types in
+ let ty_of_slot n = Semant.slot_ty (Semant.get_slot sem_cx n) in
+
+ let filename = Session.filename_of sess.Session.sess_in in
+ let llmod = Llvm.create_module llctx filename in
+
+ let (abi:Llabi.abi) = Llabi.declare_abi llctx llmod in
+ let (crate_ptr:Llvm.llvalue) =
+ Llvm.declare_global abi.Llabi.crate_ty "rust_crate" llmod
+ in
+
+ let (void_ty:Llvm.lltype) = Llvm.void_type llctx in
+ let (word_ty:Llvm.lltype) = abi.Llabi.word_ty in
+ let (wordptr_ty:Llvm.lltype) = Llvm.pointer_type word_ty in
+ let (task_ty:Llvm.lltype) = abi.Llabi.task_ty in
+ let (task_ptr_ty:Llvm.lltype) = Llvm.pointer_type task_ty in
+ let fn_ty (out:Llvm.lltype) (args:Llvm.lltype array) : Llvm.lltype =
+ Llvm.function_type out args
+ in
+
+ let imm (i:int64) : Llvm.llvalue =
+ Llvm.const_int word_ty (Int64.to_int i)
+ in
+
+ let asm_glue = Llasm.get_glue llctx llmod abi sess in
+
+ let llty_str llty =
+ Llvm.string_of_lltype llty
+ in
+
+ let llval_str llv =
+ let ts = llty_str (Llvm.type_of llv) in
+ match Llvm.value_name llv with
+ "" ->
+ Printf.sprintf "<anon=%s>" ts
+ | s -> Printf.sprintf "<%s=%s>" s ts
+ in
+
+ let llvals_str llvals =
+ (String.concat ", "
+ (Array.to_list
+ (Array.map llval_str llvals)))
+ in
+
+ let build_call callee args rvid builder =
+ iflog
+ begin
+ fun _ ->
+ let name = Llvm.value_name callee in
+ log sem_cx "build_call: %s(%s)" name (llvals_str args);
+ log sem_cx "build_call: typeof(%s) = %s"
+ name (llty_str (Llvm.type_of callee))
+ end;
+ Llvm.build_call callee args rvid builder
+ in
+
+ (* Upcall translation *)
+
+ let extern_upcalls = Hashtbl.create 0 in
+ let trans_upcall
+ (llbuilder:Llvm.llbuilder)
+ (lltask:Llvm.llvalue)
+ (name:string)
+ (lldest:Llvm.llvalue option)
+ (llargs:Llvm.llvalue array) =
+ let n = Array.length llargs in
+ let llglue = asm_glue.Llasm.asm_upcall_glues.(n) in
+ let llupcall = htab_search_or_add extern_upcalls name
+ begin
+ fun _ ->
+ let args_ty =
+ Array.append
+ [| task_ptr_ty |]
+ (Array.init n (fun i -> Llvm.type_of llargs.(i)))
+ in
+ let out_ty = match lldest with
+ None -> void_ty
+ | Some v -> Llvm.type_of v
+ in
+ let fty = fn_ty out_ty args_ty in
+ (*
+ * NB: At this point it actually doesn't matter what type
+ * we gave the upcall function, as we're just going to
+ * pointercast it to a word and pass it to the upcall-glue
+ * for now. But possibly in the future it might matter if
+ * we develop a proper upcall calling convention.
+ *)
+ Llvm.declare_function name fty llmod
+ end
+ in
+ (* Cast everything to plain words so we can hand off to the glue. *)
+ let llupcall = Llvm.const_pointercast llupcall word_ty in
+ let llargs =
+ Array.map
+ (fun arg ->
+ Llvm.build_pointercast arg word_ty
+ (anon_llid "arg") llbuilder)
+ llargs
+ in
+ let llallargs = Array.append [| lltask; llupcall |] llargs in
+ let llid = anon_llid "rv" in
+ let llrv = build_call llglue llallargs llid llbuilder in
+ Llvm.set_instruction_call_conv Llvm.CallConv.c llrv;
+ match lldest with
+ None -> ()
+ | Some lldest ->
+ let lldest =
+ Llvm.build_pointercast lldest wordptr_ty "" llbuilder
+ in
+ ignore (Llvm.build_store llrv lldest llbuilder);
+ in
+
+ let upcall
+ (llbuilder:Llvm.llbuilder)
+ (lltask:Llvm.llvalue)
+ (name:string)
+ (lldest:Llvm.llvalue option)
+ (llargs:Llvm.llvalue array)
+ : unit =
+ trans_upcall llbuilder lltask name lldest llargs
+ in
+
+ let trans_free
+ (llbuilder:Llvm.llbuilder)
+ (lltask:Llvm.llvalue)
+ (src:Llvm.llvalue)
+ : unit =
+ upcall llbuilder lltask "upcall_free" None [| src |]
+ in
+
+ (*
+ * let trans_malloc (llbuilder:Llvm.llbuilder)
+ * (dst:Llvm.llvalue) (nbytes:int64) : unit =
+ * upcall llbuilder "upcall_malloc" (Some dst) [| imm nbytes |]
+ * in
+ *)
+
+ (* Type translation *)
+
+ let lltys = Hashtbl.create 0 in
+
+ let trans_mach_ty (mty:ty_mach) : Llvm.lltype =
+ let tycon =
+ match mty with
+ TY_u8 | TY_i8 -> Llvm.i8_type
+ | TY_u16 | TY_i16 -> Llvm.i16_type
+ | TY_u32 | TY_i32 -> Llvm.i32_type
+ | TY_u64 | TY_i64 -> Llvm.i64_type
+ | TY_f32 -> Llvm.float_type
+ | TY_f64 -> Llvm.double_type
+ in
+ tycon llctx
+ in
+
+
+ let rec trans_ty_full (ty:Ast.ty) : Llvm.lltype =
+ let p t = Llvm.pointer_type t in
+ let s ts = Llvm.struct_type llctx ts in
+ let opaque _ = Llvm.opaque_type llctx in
+ let vec_body_ty _ =
+ s [| word_ty; word_ty; word_ty; (opaque()) |]
+ in
+ let rc_opaque_ty =
+ s [| word_ty; (opaque()) |]
+ in
+ match ty with
+ Ast.TY_any -> opaque ()
+ | Ast.TY_nil -> llnilty
+ | Ast.TY_bool -> Llvm.i1_type llctx
+ | Ast.TY_mach mty -> trans_mach_ty mty
+ | Ast.TY_int -> word_ty
+ | Ast.TY_uint -> word_ty
+ | Ast.TY_char -> Llvm.i32_type llctx
+ | Ast.TY_vec _
+ | Ast.TY_str -> p (vec_body_ty())
+
+ | Ast.TY_fn tfn ->
+ let (tsig, _) = tfn in
+ let lloutptr = p (trans_slot None tsig.Ast.sig_output_slot) in
+ let lltaskty = p abi.Llabi.task_ty in
+ let llins = Array.map (trans_slot None) tsig.Ast.sig_input_slots in
+ fn_ty void_ty (Array.append [| lloutptr; lltaskty |] llins)
+
+ | Ast.TY_tup slots ->
+ s (Array.map (trans_slot None) slots)
+
+ | Ast.TY_rec entries ->
+ s (Array.map (fun e -> trans_slot None (snd e)) entries)
+
+ | Ast.TY_constrained (ty', _) -> trans_ty ty'
+
+ | Ast.TY_chan _ | Ast.TY_port _ | Ast.TY_task ->
+ p rc_opaque_ty
+
+ | Ast.TY_native _ ->
+ word_ty
+
+ | Ast.TY_tag _ | Ast.TY_iso _ | Ast.TY_idx _
+ | Ast.TY_obj _ | Ast.TY_type -> (opaque()) (* TODO *)
+
+ | Ast.TY_param _ | Ast.TY_named _ ->
+ bug () "unresolved type in lltrans"
+
+ and trans_ty t =
+ htab_search_or_add lltys t (fun _ -> trans_ty_full t)
+
+ (* Translates the type of a slot into the corresponding LLVM type. If the
+ * id_opt parameter is specified, then the type will be fetched from the
+ * context. *)
+ and trans_slot (id_opt:node_id option) (slot:Ast.slot) : Llvm.lltype =
+ let ty =
+ match id_opt with
+ Some id -> ty_of_slot id
+ | None -> Semant.slot_ty slot
+ in
+ let base_llty = trans_ty ty in
+ match slot.Ast.slot_mode with
+ Ast.MODE_exterior _
+ | Ast.MODE_alias _ ->
+ Llvm.pointer_type base_llty
+ | Ast.MODE_interior _ -> base_llty
+ in
+
+ let get_element_ptr
+ (llbuilder:Llvm.llbuilder)
+ (ptr:Llvm.llvalue)
+ (i:int)
+ : Llvm.llvalue =
+ (*
+ * GEP takes a first-index of zero. Because it must! And this is
+ * sufficiently surprising that the GEP FAQ exists. And you must
+ * read it.
+ *)
+ let deref_ptr = Llvm.const_int (Llvm.i32_type llctx) 0 in
+ let idx = Llvm.const_int (Llvm.i32_type llctx) i in
+ Llvm.build_gep ptr [| deref_ptr; idx |] (anon_llid "gep") llbuilder
+ in
+
+ let free_ty
+ (llbuilder:Llvm.llbuilder)
+ (lltask:Llvm.llvalue)
+ (ty:Ast.ty)
+ (ptr:Llvm.llvalue)
+ : unit =
+ match ty with
+ Ast.TY_port _
+ | Ast.TY_chan _
+ | Ast.TY_task -> bug () "unimplemented ty in Lltrans.free_ty"
+ | _ -> trans_free llbuilder lltask ptr
+ in
+
+ let rec iter_ty_slots_full
+ (llbuilder:Llvm.llbuilder ref)
+ (ty:Ast.ty)
+ (dst_ptr:Llvm.llvalue)
+ (src_ptr:Llvm.llvalue)
+ (f:(Llvm.llvalue
+ -> Llvm.llvalue
+ -> Ast.slot
+ -> (Ast.ty_iso option)
+ -> unit))
+ (curr_iso:Ast.ty_iso option)
+ : unit =
+
+ (* NB: must deref llbuilder at call-time; don't curry this. *)
+ let gep p i = get_element_ptr (!llbuilder) p i in
+
+ match ty with
+ Ast.TY_rec entries ->
+ iter_rec_slots gep dst_ptr src_ptr entries f curr_iso
+
+ | Ast.TY_tup slots ->
+ iter_tup_slots gep dst_ptr src_ptr slots f curr_iso
+
+ | Ast.TY_tag _
+ | Ast.TY_iso _
+ | Ast.TY_fn _
+ | Ast.TY_obj _ ->
+ bug () "unimplemented ty in Lltrans.iter_ty_slots_full"
+
+ | _ -> ()
+
+ and iter_ty_slots
+ (llbuilder:Llvm.llbuilder ref)
+ (ty:Ast.ty)
+ (ptr:Llvm.llvalue)
+ (f:Llvm.llvalue -> Ast.slot -> (Ast.ty_iso option) -> unit)
+ (curr_iso:Ast.ty_iso option)
+ : unit =
+ iter_ty_slots_full llbuilder ty ptr ptr
+ (fun _ src_ptr slot curr_iso -> f src_ptr slot curr_iso)
+ curr_iso
+
+ and drop_ty
+ (llbuilder:Llvm.llbuilder ref)
+ (lltask:Llvm.llvalue)
+ (ty:Ast.ty)
+ (ptr:Llvm.llvalue)
+ (curr_iso:Ast.ty_iso option)
+ : unit =
+ iter_ty_slots llbuilder ty ptr (drop_slot llbuilder lltask) curr_iso
+
+ and drop_slot
+ (llbuilder:Llvm.llbuilder ref)
+ (lltask:Llvm.llvalue)
+ (slot_ptr:Llvm.llvalue)
+ (slot:Ast.slot)
+ (curr_iso:Ast.ty_iso option)
+ : unit =
+
+ let llfn = Llvm.block_parent (Llvm.insertion_block (!llbuilder)) in
+ let llty = trans_slot None slot in
+ let ty = Semant.slot_ty slot in
+
+ let new_block klass =
+ let llblock = Llvm.append_block llctx (anon_llid klass) llfn in
+ let llbuilder = Llvm.builder_at_end llctx llblock in
+ (llblock, llbuilder)
+ in
+
+ let if_ptr_in_slot_not_null
+ (inner:Llvm.llvalue -> Llvm.llbuilder -> Llvm.llbuilder)
+ (llbuilder:Llvm.llbuilder)
+ : Llvm.llbuilder =
+ let ptr = Llvm.build_load slot_ptr (anon_llid "tmp") llbuilder in
+ let null = Llvm.const_pointer_null llty in
+ let test =
+ Llvm.build_icmp Llvm.Icmp.Ne null ptr (anon_llid "nullp") llbuilder
+ in
+ let (llthen, llthen_builder) = new_block "then" in
+ let (llnext, llnext_builder) = new_block "next" in
+ ignore (Llvm.build_cond_br test llthen llnext llbuilder);
+ let llthen_builder = inner ptr llthen_builder in
+ ignore (Llvm.build_br llnext llthen_builder);
+ llnext_builder
+ in
+
+ let decr_refcnt_and_if_zero
+ (rc_elt:int)
+ (inner:Llvm.llvalue -> Llvm.llbuilder -> Llvm.llbuilder)
+ (ptr:Llvm.llvalue)
+ (llbuilder:Llvm.llbuilder)
+ : Llvm.llbuilder =
+ let rc_ptr = get_element_ptr llbuilder ptr rc_elt in
+ let rc = Llvm.build_load rc_ptr (anon_llid "rc") llbuilder in
+ let rc = Llvm.build_sub rc (imm 1L) (anon_llid "tmp") llbuilder in
+ let _ = Llvm.build_store rc rc_ptr llbuilder in
+ log sem_cx "rc type: %s" (llval_str rc);
+ let test =
+ Llvm.build_icmp Llvm.Icmp.Eq
+ rc (imm 0L) (anon_llid "zerop") llbuilder
+ in
+ let (llthen, llthen_builder) = new_block "then" in
+ let (llnext, llnext_builder) = new_block "next" in
+ ignore (Llvm.build_cond_br test llthen llnext llbuilder);
+ let llthen_builder = inner ptr llthen_builder in
+ ignore (Llvm.build_br llnext llthen_builder);
+ llnext_builder
+ in
+
+ let free_and_null_out_slot
+ (ptr:Llvm.llvalue)
+ (llbuilder:Llvm.llbuilder)
+ : Llvm.llbuilder =
+ free_ty llbuilder lltask ty ptr;
+ let null = Llvm.const_pointer_null llty in
+ ignore (Llvm.build_store null slot_ptr llbuilder);
+ llbuilder
+ in
+
+ begin
+ match slot_mem_ctrl slot with
+ MEM_rc_struct
+ | MEM_gc ->
+ llbuilder :=
+ if_ptr_in_slot_not_null
+ (decr_refcnt_and_if_zero
+ Abi.exterior_rc_slot_field_refcnt
+ free_and_null_out_slot)
+ (!llbuilder)
+
+ | MEM_rc_opaque ->
+ llbuilder :=
+ if_ptr_in_slot_not_null
+ (decr_refcnt_and_if_zero
+ Abi.exterior_rc_slot_field_refcnt
+ free_and_null_out_slot)
+ (!llbuilder)
+
+ | MEM_interior when Semant.type_is_structured ty ->
+ (* FIXME: to handle recursive types, need to call drop
+ glue here, not inline. *)
+ drop_ty llbuilder lltask ty slot_ptr curr_iso
+
+ | _ -> ()
+ end
+ in
+
+ let (llitems:(node_id, Llvm.llvalue) Hashtbl.t) = Hashtbl.create 0 in
+ let declare_mod_item
+ (name:Ast.ident)
+ { node = { Ast.decl_item = (item:Ast.mod_item') }; id = id }
+ : unit =
+ let full_name = Semant.item_str sem_cx id in
+ let line_num =
+ match Session.get_span sess id with
+ None -> 0
+ | Some span ->
+ let (_, line, _) = span.lo in
+ line
+ in
+ match item with
+ Ast.MOD_ITEM_fn _ ->
+ let llty = trans_ty (ty_of_item id) in
+ let llfn = Llvm.declare_function ("_rust_" ^ name) llty llmod in
+ let meta =
+ md_node
+ [|
+ const_dw_tag Dwarf.DW_TAG_subprogram;
+ const_i32 0; (* unused *)
+ const_i32 0; (* context metadata llvalue *)
+ md_str name;
+ md_str full_name;
+ md_str full_name;
+ const_i32 0; (* file metadata llvalue *)
+ const_i32 line_num;
+ const_i32 0; (* type descriptor metadata llvalue *)
+ const_i1 1; (* flag: local to compile unit? *)
+ const_i1 1; (* flag: defined in compile unit? *)
+ |]
+ in
+ Llvm.set_function_call_conv Llvm.CallConv.c llfn;
+ Hashtbl.add llitems id llfn;
+
+ (* FIXME: Adding metadata does not work yet. . *)
+ let _ = fun _ -> set_dbg_metadata llfn meta in
+ ()
+
+ | _ -> () (* TODO *)
+ in
+
+ let trans_fn
+ ({
+ Ast.fn_input_slots = (header_slots:Ast.header_slots);
+ Ast.fn_body = (body:Ast.block)
+ }:Ast.fn)
+ (fn_id:node_id)
+ : unit =
+ let llfn = Hashtbl.find llitems fn_id in
+ let lloutptr = Llvm.param llfn 0 in
+ let lltask = Llvm.param llfn 1 in
+
+ (* LLVM requires that functions be grouped into basic blocks terminated by
+ * terminator instructions, while our AST is less strict. So we have to do
+ * a little trickery here to wrangle the statement sequence into LLVM's
+ * format. *)
+
+ let new_block id_opt klass =
+ let llblock = Llvm.append_block llctx (node_llid id_opt klass) llfn in
+ let llbuilder = Llvm.builder_at_end llctx llblock in
+ (llblock, llbuilder)
+ in
+
+ (* Build up the slot-to-llvalue mapping, allocating space along the
+ * way. *)
+ let slot_to_llvalue = Hashtbl.create 0 in
+ let (_, llinitbuilder) = new_block None "init" in
+
+ (* Allocate space for arguments (needed because arguments are lvalues in
+ * Rust), and store them in the slot-to-llvalue mapping. *)
+ let n_implicit_args = 2 in
+ let build_arg idx llargval =
+ if idx >= n_implicit_args
+ then
+ let ({ id = id }, ident) = header_slots.(idx - 2) in
+ Llvm.set_value_name ident llargval;
+ let llarg =
+ let llty = Llvm.type_of llargval in
+ Llvm.build_alloca llty (ident ^ "_ptr") llinitbuilder
+ in
+ ignore (Llvm.build_store llargval llarg llinitbuilder);
+ Hashtbl.add slot_to_llvalue id llarg
+ in
+ Array.iteri build_arg (Llvm.params llfn);
+
+ (* Allocate space for all the blocks' slots.
+ * and zero the exteriors. *)
+ let init_block (block_id:node_id) : unit =
+ let init_slot
+ (key:Ast.slot_key)
+ (slot_id:node_id)
+ (slot:Ast.slot)
+ : unit =
+ let name = Ast.sprintf_slot_key () key in
+ let llty = trans_slot (Some slot_id) slot in
+ let llptr = Llvm.build_alloca llty name llinitbuilder in
+ begin
+ match slot_mem_ctrl slot with
+ MEM_rc_struct
+ | MEM_rc_opaque
+ | MEM_gc ->
+ ignore (Llvm.build_store
+ (Llvm.const_pointer_null llty)
+ llptr llinitbuilder);
+ | _ -> ()
+ end;
+ Hashtbl.add slot_to_llvalue slot_id llptr
+ in
+ iter_block_slots sem_cx block_id init_slot
+ in
+
+ let exit_block
+ (llbuilder:Llvm.llbuilder)
+ (block_id:node_id)
+ : Llvm.llbuilder =
+ let r = ref llbuilder in
+ iter_block_slots sem_cx block_id
+ begin
+ fun _ slot_id slot ->
+ if (not (Semant.slot_is_obj_state sem_cx slot_id))
+ then
+ let ptr = Hashtbl.find slot_to_llvalue slot_id in
+ drop_slot r lltask ptr slot None
+ end;
+ !r
+ in
+
+ List.iter init_block (Hashtbl.find sem_cx.Semant.ctxt_frame_blocks fn_id);
+
+ let static_str (s:string) : Llvm.llvalue =
+ Llvm.define_global (anon_llid "str") (Llvm.const_stringz llctx s) llmod
+ in
+
+
+ (* Translates a list of AST statements to a sequence of LLVM instructions.
+ * The supplied "terminate" function appends the appropriate terminator
+ * instruction to the instruction stream. It may or may not be called,
+ * depending on whether the AST contains a terminating instruction
+ * explicitly. *)
+ let rec trans_stmts
+ (block_id:node_id)
+ (llbuilder:Llvm.llbuilder)
+ (stmts:Ast.stmt list)
+ (terminate:(Llvm.llbuilder -> node_id -> unit))
+ : unit =
+ let trans_literal
+ (lit:Ast.lit)
+ : Llvm.llvalue =
+ match lit with
+ Ast.LIT_nil -> llnil
+ | Ast.LIT_bool value ->
+ Llvm.const_int (Llvm.i1_type llctx) (if value then 1 else 0)
+ | Ast.LIT_mach (mty, value, _) ->
+ let llty = trans_mach_ty mty in
+ Llvm.const_of_int64 llty value (mach_is_signed mty)
+ | Ast.LIT_int (value, _) ->
+ Llvm.const_of_int64 (Llvm.i32_type llctx) value true
+ | Ast.LIT_uint (value, _) ->
+ Llvm.const_of_int64 (Llvm.i32_type llctx) value false
+ | Ast.LIT_char ch ->
+ Llvm.const_int (Llvm.i32_type llctx) ch
+ in
+
+ (* Translates an lval by reference into the appropriate pointer
+ * value. *)
+ let trans_lval (lval:Ast.lval) : Llvm.llvalue =
+ iflog (fun _ -> log sem_cx "trans_lval: %a" Ast.sprintf_lval lval);
+ match lval with
+ Ast.LVAL_base { id = base_id } ->
+ let id =
+ Hashtbl.find sem_cx.Semant.ctxt_lval_to_referent base_id
+ in
+ let referent = Hashtbl.find sem_cx.Semant.ctxt_all_defns id in
+ begin
+ match referent with
+ Semant.DEFN_slot _ -> Hashtbl.find slot_to_llvalue id
+ | Semant.DEFN_item _ -> Hashtbl.find llitems id
+ | _ -> bogus_ptr (* TODO *)
+ end
+ | Ast.LVAL_ext _ -> bogus_ptr (* TODO *)
+ in
+
+ let trans_atom (atom:Ast.atom) : Llvm.llvalue =
+ iflog (fun _ -> log sem_cx "trans_atom: %a" Ast.sprintf_atom atom);
+ match atom with
+ Ast.ATOM_literal { node = lit } -> trans_literal lit
+ | Ast.ATOM_lval lval ->
+ Llvm.build_load (trans_lval lval) (anon_llid "tmp") llbuilder
+ in
+
+ let trans_binary_expr
+ ((op:Ast.binop), (lhs:Ast.atom), (rhs:Ast.atom))
+ : Llvm.llvalue =
+ (* Evaluate the operands in the proper order. *)
+ let (lllhs, llrhs) =
+ match op with
+ Ast.BINOP_or | Ast.BINOP_and | Ast.BINOP_eq | Ast.BINOP_ne
+ | Ast.BINOP_lt | Ast.BINOP_le | Ast.BINOP_ge | Ast.BINOP_gt
+ | Ast.BINOP_lsl | Ast.BINOP_lsr | Ast.BINOP_asr
+ | Ast.BINOP_add | Ast.BINOP_sub | Ast.BINOP_mul
+ | Ast.BINOP_div | Ast.BINOP_mod | Ast.BINOP_xor ->
+ (trans_atom lhs, trans_atom rhs)
+ | Ast.BINOP_send ->
+ let llrhs = trans_atom rhs in
+ let lllhs = trans_atom lhs in
+ (lllhs, llrhs)
+ in
+ let llid = anon_llid "expr" in
+ match op with
+ Ast.BINOP_eq ->
+ (* TODO: equality works on more than just integers *)
+ Llvm.build_icmp Llvm.Icmp.Eq lllhs llrhs llid llbuilder
+
+ (* TODO: signed/unsigned distinction, floating point *)
+ | Ast.BINOP_add -> Llvm.build_add lllhs llrhs llid llbuilder
+ | Ast.BINOP_sub -> Llvm.build_sub lllhs llrhs llid llbuilder
+ | Ast.BINOP_mul -> Llvm.build_mul lllhs llrhs llid llbuilder
+ | Ast.BINOP_div -> Llvm.build_sdiv lllhs llrhs llid llbuilder
+ | Ast.BINOP_mod -> Llvm.build_srem lllhs llrhs llid llbuilder
+
+ | _ -> bogus (* TODO *)
+ in
+
+ let trans_unary_expr _ = bogus in (* TODO *)
+
+ let trans_expr (expr:Ast.expr) : Llvm.llvalue =
+ iflog (fun _ -> log sem_cx "trans_expr: %a" Ast.sprintf_expr expr);
+ match expr with
+ Ast.EXPR_binary binexp -> trans_binary_expr binexp
+ | Ast.EXPR_unary unexp -> trans_unary_expr unexp
+ | Ast.EXPR_atom atom -> trans_atom atom
+ in
+
+ let trans_log_str (atom:Ast.atom) : unit =
+ upcall llbuilder lltask "upcall_log_str" None [| trans_atom atom |]
+ in
+
+ let trans_log_int (atom:Ast.atom) : unit =
+ upcall llbuilder lltask "upcall_log_int" None [| trans_atom atom |]
+ in
+
+ let trans_fail
+ (llbuilder:Llvm.llbuilder)
+ (lltask:Llvm.llvalue)
+ (reason:string)
+ (stmt_id:node_id)
+ : unit =
+ let (file, line, _) =
+ match Session.get_span sem_cx.Semant.ctxt_sess stmt_id with
+ None -> ("<none>", 0, 0)
+ | Some sp -> sp.lo
+ in
+ upcall llbuilder lltask "upcall_fail" None [|
+ static_str reason;
+ static_str file;
+ Llvm.const_int (Llvm.i32_type llctx) line
+ |];
+ ignore (Llvm.build_unreachable llbuilder)
+ in
+
+ (* FIXME: this may be irrelevant; possibly LLVM will wind up
+ * using GOT and such wherever it needs to to achieve PIC
+ * data.
+ *)
+ (*
+ let crate_rel (v:Llvm.llvalue) : Llvm.llvalue =
+ let v_int = Llvm.const_pointercast v word_ty in
+ let c_int = Llvm.const_pointercast crate_ptr word_ty in
+ Llvm.const_sub v_int c_int
+ in
+ *)
+
+ match stmts with
+ [] -> terminate llbuilder block_id
+ | head::tail ->
+
+ iflog (fun _ ->
+ log sem_cx "trans_stmt: %a" Ast.sprintf_stmt head);
+
+ let trans_tail_with_builder llbuilder' : unit =
+ trans_stmts block_id llbuilder' tail terminate
+ in
+ let trans_tail () = trans_tail_with_builder llbuilder in
+
+ match head.node with
+ Ast.STMT_init_tup (dest, atoms) ->
+ let zero = const_i32 0 in
+ let lldest = trans_lval dest in
+ let trans_tup_atom idx (_, _, atom) =
+ let indices = [| zero; const_i32 idx |] in
+ let gep_id = anon_llid "init_tup_gep" in
+ let ptr =
+ Llvm.build_gep lldest indices gep_id llbuilder
+ in
+ ignore (Llvm.build_store (trans_atom atom) ptr llbuilder)
+ in
+ Array.iteri trans_tup_atom atoms;
+ trans_tail ()
+
+ | Ast.STMT_copy (dest, src) ->
+ let llsrc = trans_expr src in
+ let lldest = trans_lval dest in
+ ignore (Llvm.build_store llsrc lldest llbuilder);
+ trans_tail ()
+
+ | Ast.STMT_call (dest, fn, args) ->
+ let llargs = Array.map trans_atom args in
+ let lldest = trans_lval dest in
+ let llfn = trans_lval fn in
+ let llallargs = Array.append [| lldest; lltask |] llargs in
+ let llrv = build_call llfn llallargs "" llbuilder in
+ Llvm.set_instruction_call_conv Llvm.CallConv.c llrv;
+ trans_tail ()
+
+ | Ast.STMT_if sif ->
+ let llexpr = trans_expr sif.Ast.if_test in
+ let (llnext, llnextbuilder) = new_block None "next" in
+ let branch_to_next llbuilder' _ =
+ ignore (Llvm.build_br llnext llbuilder')
+ in
+ let llthen = trans_block sif.Ast.if_then branch_to_next in
+ let llelse =
+ match sif.Ast.if_else with
+ None -> llnext
+ | Some if_else -> trans_block if_else branch_to_next
+ in
+ ignore (Llvm.build_cond_br llexpr llthen llelse llbuilder);
+ trans_tail_with_builder llnextbuilder
+
+ | Ast.STMT_ret atom_opt ->
+ begin
+ match atom_opt with
+ None -> ()
+ | Some atom ->
+ ignore (Llvm.build_store (trans_atom atom)
+ lloutptr llbuilder)
+ end;
+ let llbuilder = exit_block llbuilder block_id in
+ ignore (Llvm.build_ret_void llbuilder)
+
+ | Ast.STMT_fail ->
+ trans_fail llbuilder lltask "explicit failure" head.id
+
+ | Ast.STMT_log a ->
+ begin
+ match Semant.atom_type sem_cx a with
+ (* NB: If you extend this, be sure to update the
+ * typechecking code in type.ml as well. *)
+ Ast.TY_str -> trans_log_str a
+ | Ast.TY_int | Ast.TY_uint | Ast.TY_bool | Ast.TY_char
+ | Ast.TY_mach (TY_u8) | Ast.TY_mach (TY_u16)
+ | Ast.TY_mach (TY_u32) | Ast.TY_mach (TY_i8)
+ | Ast.TY_mach (TY_i16) | Ast.TY_mach (TY_i32) ->
+ trans_log_int a
+ | _ -> Semant.bugi sem_cx head.id
+ "unimplemented logging type"
+ end;
+ trans_tail ()
+
+ | Ast.STMT_check_expr expr ->
+ let llexpr = trans_expr expr in
+ let (llfail, llfailbuilder) = new_block None "fail" in
+ let reason = Ast.fmt_to_str Ast.fmt_expr expr in
+ trans_fail llfailbuilder lltask reason head.id;
+ let (llok, llokbuilder) = new_block None "ok" in
+ ignore (Llvm.build_cond_br llexpr llok llfail llbuilder);
+ trans_tail_with_builder llokbuilder
+
+ | Ast.STMT_init_str (dst, str) ->
+ let d = trans_lval dst in
+ let s = static_str str in
+ let len =
+ Llvm.const_int word_ty ((String.length str) + 1)
+ in
+ upcall llbuilder lltask "upcall_new_str"
+ (Some d) [| s; len |];
+ trans_tail ()
+
+ | _ -> trans_stmts block_id llbuilder tail terminate
+
+ (*
+ * Translates an AST block to one or more LLVM basic blocks and returns
+ * the first basic block. The supplied callback is expected to add a
+ * terminator instruction.
+ *)
+
+ and trans_block
+ ({ node = (stmts:Ast.stmt array); id = id }:Ast.block)
+ (terminate:Llvm.llbuilder -> node_id -> unit)
+ : Llvm.llbasicblock =
+ let (llblock, llbuilder) = new_block (Some id) "bb" in
+ trans_stmts id llbuilder (Array.to_list stmts) terminate;
+ llblock
+ in
+
+ (* "Falling off the end" of a function needs to turn into an explicit
+ * return instruction. *)
+ let default_terminate llbuilder block_id =
+ let llbuilder = exit_block llbuilder block_id in
+ ignore (Llvm.build_ret_void llbuilder)
+ in
+
+ (* Build up the first body block, and link it to the end of the
+ * initialization block. *)
+ let llbodyblock = (trans_block body default_terminate) in
+ ignore (Llvm.build_br llbodyblock llinitbuilder)
+ in
+
+ let trans_mod_item
+ (_:Ast.ident)
+ { node = { Ast.decl_item = (item:Ast.mod_item') }; id = id }
+ : unit =
+ match item with
+ Ast.MOD_ITEM_fn fn -> trans_fn fn id
+ | _ -> ()
+ in
+
+ let exit_task_glue =
+ (* The exit-task glue does not get called.
+ *
+ * Rather, control arrives at it by *returning* to the first
+ * instruction of it, when control falls off the end of the task's
+ * root function.
+ *
+ * There is a "fake" frame set up by the runtime, underneath us,
+ * that we find ourselves in. This frame has the shape of a frame
+ * entered with 2 standard arguments (outptr + taskptr), then a
+ * retpc and N callee-saves sitting on the stack; all this is under
+ * ebp. Then there are 2 *outgoing* args at sp[0] and sp[1].
+ *
+ * All these are fake except the taskptr, which is the one bit we
+ * want. So we construct an equally fake cdecl llvm signature here
+ * to crudely *get* the taskptr that's sitting 2 words up from sp,
+ * and pass it to upcall_exit.
+ *
+ * The latter never returns.
+ *)
+ let llty = fn_ty void_ty [| task_ptr_ty |] in
+ let llfn = Llvm.declare_function "rust_exit_task_glue" llty llmod in
+ let lltask = Llvm.param llfn 0 in
+ let llblock = Llvm.append_block llctx "body" llfn in
+ let llbuilder = Llvm.builder_at_end llctx llblock in
+ trans_upcall llbuilder lltask "upcall_exit" None [||];
+ ignore (Llvm.build_ret_void llbuilder);
+ llfn
+ in
+
+ try
+ let crate' = crate.node in
+ let items = snd (crate'.Ast.crate_items) in
+ Hashtbl.iter declare_mod_item items;
+ Hashtbl.iter trans_mod_item items;
+ Llfinal.finalize_module
+ llctx llmod abi asm_glue exit_task_glue crate_ptr;
+ llmod
+ with e -> Llvm.dispose_module llmod; raise e
+;;
+
+(*
+ * Local Variables:
+ * fill-column: 78;
+ * indent-tabs-mode: nil
+ * buffer-file-coding-system: utf-8-unix
+ * compile-command: "make -k -C ../.. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
+ * End:
+ *)
+