diff options
Diffstat (limited to 'src')
61 files changed, 1253 insertions, 488 deletions
diff --git a/src/Makefile b/src/Makefile index 7236e02f..496457bb 100644 --- a/src/Makefile +++ b/src/Makefile @@ -245,7 +245,8 @@ BOOT_CMXS := $(BOOT_MLS:.ml=.cmx) BOOT_OBJS := $(BOOT_MLS:.ml=.o) BOOT_CMIS := $(BOOT_MLS:.ml=.cmi) -RUNTIME_CS := rt/sync/spin_lock.cpp \ +RUNTIME_CS := rt/sync/sync.cpp \ + rt/sync/spin_lock.cpp \ rt/sync/lock_free_queue.cpp \ rt/sync/condition_variable.cpp \ rt/rust.cpp \ @@ -279,7 +280,8 @@ RUNTIME_HDR := rt/globals.h \ rt/rust_message.h \ rt/circular_buffer.h \ rt/util/array_list.h \ - rt/util/hash_map.h + rt/util/hash_map.h \ + rt/sync/sync.h RUNTIME_INCS := -Irt/isaac -Irt/uthash RUNTIME_OBJS := $(RUNTIME_CS:.cpp=$(CFG_OBJ_SUFFIX)) @@ -383,17 +385,22 @@ TASK_XFAILS := test/run-pass/acyclic-unwind.rs \ test/run-pass/threads.rs \ test/run-pass/yield.rs +TEST_XFAILS := test/run-pass/arith-0.rs + TEST_XFAILS_X86 := $(TASK_XFAILS) \ + $(TEST_XFAILS) \ test/run-pass/arithmetic-interference.rs \ test/run-pass/bind-obj-ctor.rs \ test/run-pass/child-outlives-parent.rs \ test/run-pass/clone-with-exterior.rs \ test/run-pass/constrained-type.rs \ + test/run-pass/destructor-ordering.rs \ test/run-pass/obj-as.rs \ test/run-pass/vec-slice.rs \ test/run-pass/fn-lval.rs \ test/run-pass/generic-fn-infer.rs \ test/run-pass/generic-recursive-tag.rs \ + test/run-pass/int-lib.rs \ test/run-pass/iter-ret.rs \ test/run-pass/lib-deque.rs \ test/run-pass/lib-map.rs \ @@ -412,16 +419,19 @@ TEST_XFAILS_X86 := $(TASK_XFAILS) \ test/compile-fail/writing-through-read-alias.rs TEST_XFAILS_LLVM := $(TASK_XFAILS) \ + $(TEST_XFAILS) \ $(addprefix test/run-pass/, \ + arith-1.rs \ acyclic-unwind.rs \ alt-pattern-simple.rs \ alt-tag.rs \ arithmetic-interference.rs \ argv.rs \ - auto-deref.rs \ autoderef-full-lval.rs \ autoderef-objfn.rs \ basic.rs \ + basic-1.rs \ + basic-2.rs \ bind-obj-ctor.rs \ bind-thunk.rs \ bind-trivial.rs \ @@ -452,7 +462,6 @@ TEST_XFAILS_LLVM := $(TASK_XFAILS) \ generic-drop-glue.rs \ generic-exterior-box.rs \ generic-fn-infer.rs \ - generic-fn-twice.rs \ generic-fn.rs \ generic-obj-with-derived-type.rs \ generic-obj.rs \ @@ -463,7 +472,8 @@ TEST_XFAILS_LLVM := $(TASK_XFAILS) \ i32-sub.rs \ i8-incr.rs \ import.rs \ - inner-module.rs \ + integral-indexing.rs \ + int-lib.rs \ iter-range.rs \ iter-ret.rs \ large-records.rs \ @@ -481,8 +491,6 @@ TEST_XFAILS_LLVM := $(TASK_XFAILS) \ mutable-alias-vec.rs \ mutable-vec-drop.rs \ mutual-recursion-group.rs \ - native-mod.rc \ - native.rc \ obj-as.rs \ obj-drop.rs \ obj-dtor.rs \ @@ -506,6 +514,7 @@ TEST_XFAILS_LLVM := $(TASK_XFAILS) \ str-append.rs \ str-concat.rs \ str-idx.rs \ + str-lib.rs \ tag.rs \ tail-cps.rs \ tail-direct.rs \ diff --git a/src/boot/be/abi.ml b/src/boot/be/abi.ml index 347d49fc..5bdf21fa 100644 --- a/src/boot/be/abi.ml +++ b/src/boot/be/abi.ml @@ -41,7 +41,7 @@ let box_gc_header_size = 4;; let box_gc_malloc_return_adjustment = 3;; -let stk_field_valgrind_id = 0 + 1;; +let stk_field_valgrind_id = 0;; let stk_field_limit = stk_field_valgrind_id + 1;; let stk_field_data = stk_field_limit + 1;; @@ -121,7 +121,8 @@ type abi = -> Common.size (* callsz *) -> Common.nabi -> Common.fixup (* grow_task *) - -> unit); + -> bool (* is_obj_fn *) + -> unit); abi_emit_fn_epilogue: (Il.emitter -> unit); diff --git a/src/boot/be/il.ml b/src/boot/be/il.ml index 792e83e2..2a5b643a 100644 --- a/src/boot/be/il.ml +++ b/src/boot/be/il.ml @@ -695,7 +695,7 @@ type emitter = { mutable emit_pc: int; emit_target_specific: (emitter -> quad -> unit); mutable emit_quads: quads; emit_annotations: (int,string) Hashtbl.t; - emit_size_cache: ((size,operand) Hashtbl.t) Stack.t; + emit_size_cache: (size,operand) Hashtbl.t; emit_node: node_id option; } @@ -722,7 +722,7 @@ let new_emitter emit_target_specific = emit_target_specific; emit_quads = Array.create 4 badq; emit_annotations = Hashtbl.create 0; - emit_size_cache = Stack.create (); + emit_size_cache = Hashtbl.create 0; emit_node = node; } ;; @@ -901,6 +901,13 @@ let get_element_ptr (string_of_cell fmt mem_cell) ;; +let ptr_cast (cell:cell) (rty:referent_ty) : cell = + match cell with + Mem (mem, _) -> Mem (mem, rty) + | Reg (reg, AddrTy _) -> Reg (reg, AddrTy rty) + | _ -> bug () "expected address cell in Il.ptr_cast" +;; + (* * Local Variables: * fill-column: 78; diff --git a/src/boot/be/x86.ml b/src/boot/be/x86.ml index 217149c7..826127a0 100644 --- a/src/boot/be/x86.ml +++ b/src/boot/be/x86.ml @@ -302,11 +302,41 @@ let emit_target_specific | Il.IMOD | Il.UMOD -> let dst_eax = hr_like_cell eax dst in let lhs_eax = hr_like_op eax lhs in - let rhs_ecx = hr_like_op ecx lhs in - if lhs <> (Il.Cell lhs_eax) - then mov lhs_eax lhs; - if rhs <> (Il.Cell rhs_ecx) - then mov rhs_ecx rhs; + let rhs_ecx = hr_like_op ecx rhs in + (* Horrible: we bounce complex mul inputs off spill slots + * to ensure non-interference between the temporaries used + * during mem-base-reg reloads and the registers we're + * preparing. *) + let next_spill_like op = + Il.Mem (Il.next_spill_slot e + (Il.ScalarTy (Il.operand_scalar_ty op))) + in + let is_mem op = + match op with + Il.Cell (Il.Mem _) -> true + | _ -> false + in + let bounce_lhs = is_mem lhs in + let bounce_rhs = is_mem rhs in + let lhs_spill = next_spill_like lhs in + let rhs_spill = next_spill_like rhs in + + if bounce_lhs + then mov lhs_spill lhs; + + if bounce_rhs + then mov rhs_spill rhs; + + mov lhs_eax + (if bounce_lhs + then (Il.Cell lhs_spill) + else lhs); + + mov rhs_ecx + (if bounce_rhs + then (Il.Cell rhs_spill) + else rhs); + put (Il.Binary { b with Il.binary_lhs = (Il.Cell lhs_eax); @@ -314,7 +344,7 @@ let emit_target_specific Il.binary_dst = dst_eax; }); if dst <> dst_eax then mov dst (Il.Cell dst_eax); - + | _ when (Il.Cell dst) <> lhs -> mov dst lhs; put (Il.Binary @@ -563,6 +593,7 @@ let restore_frame_base (e:Il.emitter) (base:Il.reg) (retpc:Il.reg) : unit = * * *ebp+20+(4*N) = [argN ] * ... + * *ebp+28 = [arg2 ] = obj/closure ptr * *ebp+24 = [arg1 ] = task ptr * *ebp+20 = [arg0 ] = out ptr * *ebp+16 = [retpc ] @@ -1003,7 +1034,7 @@ let unwind_glue (* Puts result in eax; clobbers ecx, edx in the process. *) -let rec calculate_sz (e:Il.emitter) (size:size) : unit = +let rec calculate_sz (e:Il.emitter) (size:size) (in_obj:bool) : unit = let emit = Il.emit e in let mov dst src = emit (Il.umov dst src) in let push x = emit (Il.Push x) in @@ -1015,11 +1046,48 @@ let rec calculate_sz (e:Il.emitter) (size:size) : unit = let mul x y = emit (Il.binary Il.UMUL (rc x) (ro x) (ro y)) in let subi x y = emit (Il.binary Il.SUB (rc x) (ro x) (immi y)) in let eax_gets_a_and_ecx_gets_b a b = - calculate_sz e b; + calculate_sz e b in_obj; push (ro eax); - calculate_sz e a; + calculate_sz e a in_obj; pop (rc ecx); in + + let ty_param_n_in_obj_fn i = + (* + * Here we are trying to immitate the obj-fn branch of + * Trans.get_ty_params_of_current_frame while using + * eax as our only register. + *) + + (* Bind all the referent types we'll need... *) + + let obj_body_rty = Semant.obj_closure_rty word_bits in + let tydesc_rty = Semant.tydesc_rty word_bits in + (* Note that we cheat here and pretend only to have i+1 tydescs (because + we GEP to the i'th while still in this function, so no one outside + finds out about the lie. *) + let tydesc_tys = Array.init (i + 1) (fun _ -> Ast.TY_type) in + let ty_params_ty = Ast.TY_tup tydesc_tys in + let ty_params_rty = Semant.referent_type word_bits ty_params_ty in + + (* ... and fetch! *) + + mov (rc eax) (Il.Cell closure_ptr); + let obj_body = word_n (h eax) Abi.box_rc_field_body in + let obj_body = Il.ptr_cast obj_body obj_body_rty in + let tydesc_ptr = get_element_ptr obj_body Abi.obj_body_elt_tydesc in + + mov (rc eax) (Il.Cell tydesc_ptr); + let tydesc = Il.ptr_cast (word_at (h eax)) tydesc_rty in + let ty_params_ptr = + get_element_ptr tydesc Abi.tydesc_field_first_param + in + + mov (rc eax) (Il.Cell ty_params_ptr); + let ty_params = Il.ptr_cast (word_at (h eax)) ty_params_rty in + get_element_ptr ty_params i + in + match size with SIZE_fixed i -> mov (rc eax) (immi i) @@ -1031,15 +1099,23 @@ let rec calculate_sz (e:Il.emitter) (size:size) : unit = mov (rc eax) (imm (Asm.M_POS f)) | SIZE_param_size i -> - mov (rc eax) (Il.Cell (ty_param_n i)); + if in_obj + then + mov (rc eax) (Il.Cell (ty_param_n_in_obj_fn i)) + else + mov (rc eax) (Il.Cell (ty_param_n i)); mov (rc eax) (Il.Cell (word_n (h eax) Abi.tydesc_field_size)) | SIZE_param_align i -> - mov (rc eax) (Il.Cell (ty_param_n i)); + if in_obj + then + mov (rc eax) (Il.Cell (ty_param_n_in_obj_fn i)) + else + mov (rc eax) (Il.Cell (ty_param_n i)); mov (rc eax) (Il.Cell (word_n (h eax) Abi.tydesc_field_align)) | SIZE_rt_neg a -> - calculate_sz e a; + calculate_sz e a in_obj; neg eax | SIZE_rt_add (a, b) -> @@ -1155,6 +1231,7 @@ let fn_prologue (callsz:size) (nabi:nabi) (grow_task_fixup:fixup) + (is_obj_fn:bool) : unit = let esi_n = word_n (h esi) in @@ -1284,7 +1361,7 @@ let fn_prologue emit (Il.jmp Il.JA Il.CodeNone); (* Calculate dynamic frame size. *) - calculate_sz e call_and_frame_sz; + calculate_sz e call_and_frame_sz is_obj_fn; ((ro eax), Some primordial_underflow_jmp_pc) end | Some e -> ((imm e), None) @@ -1936,15 +2013,20 @@ let zero (dst:Il.cell) (count:Il.operand) : Asm.frag = ;; let mov (signed:bool) (dst:Il.cell) (src:Il.operand) : Asm.frag = - if is_ty8 (Il.cell_scalar_ty dst) || is_ty8 (Il.operand_scalar_ty src) + if is_ty8 (Il.cell_scalar_ty dst) + then + begin + match dst with + Il.Reg (Il.Hreg r, _) -> assert (is_ok_r8 r) + | _ -> () + end; + + if is_ty8 (Il.operand_scalar_ty src) then begin - (match dst with - Il.Reg (Il.Hreg r, _) - -> assert (is_ok_r8 r) | _ -> ()); - (match src with - Il.Cell (Il.Reg (Il.Hreg r, _)) - -> assert (is_ok_r8 r) | _ -> ()); + match src with + Il.Cell (Il.Reg (Il.Hreg r, _)) -> assert (is_ok_r8 r) + | _ -> () end; match (signed, dst, src) with diff --git a/src/boot/fe/ast.ml b/src/boot/fe/ast.ml index 6cd1114a..357bf1e6 100644 --- a/src/boot/fe/ast.ml +++ b/src/boot/fe/ast.ml @@ -199,7 +199,7 @@ and tup_input = (mutability * atom) and stmt' = (* lval-assigning stmts. *) - STMT_spawn of (lval * domain * lval * (atom array)) + STMT_spawn of (lval * domain * string * lval * (atom array)) | STMT_new_rec of (lval * (rec_input array) * lval option) | STMT_new_tup of (lval * (tup_input array)) | STMT_new_vec of (lval * mutability * atom array) @@ -259,7 +259,7 @@ and stmt_alt_type = and stmt_alt_port = { - (* else lval is a timeout value. *) + (* else atom is a timeout value. *) alt_port_arms: port_arm array; alt_port_else: (atom * block) option; } @@ -328,7 +328,7 @@ and type_arm = type_arm' identified and port_arm' = port_case * block and port_arm = port_arm' identified -and port_case = +and port_case = PORT_CASE_send of (lval * lval) | PORT_CASE_recv of (lval * lval) @@ -664,7 +664,7 @@ and fmt_constrained ff (ty, constrs) : unit = fmt_constrs ff constrs; fmt ff "@]"; fmt ff "@]"; - + and fmt_ty (ff:Format.formatter) (t:ty) : unit = match t with @@ -707,7 +707,7 @@ and fmt_ty (ff:Format.formatter) (t:ty) : unit = | TY_tag ttag -> fmt_tag ff ttag | TY_iso tiso -> fmt_iso ff tiso | TY_idx idx -> fmt ff "<idx#%d>" idx - | TY_constrained ctrd -> fmt_constrained ff ctrd + | TY_constrained ctrd -> fmt_constrained ff ctrd | TY_obj (effect, fns) -> fmt_obox ff; @@ -942,10 +942,11 @@ and fmt_stmt_body (ff:Format.formatter) (s:stmt) : unit = fmt ff ";" end - | STMT_spawn (dst, domain, fn, args) -> + | STMT_spawn (dst, domain, name, fn, args) -> fmt_lval ff dst; fmt ff " = spawn "; fmt_domain ff domain; + fmt_str ff ("\"" ^ name ^ "\""); fmt_lval ff fn; fmt_atoms ff args; fmt ff ";"; @@ -1233,7 +1234,7 @@ and fmt_stmt_body (ff:Format.formatter) (s:stmt) : unit = Array.iter (fmt_tag_arm ff) at.alt_tag_arms; fmt_cbb ff; - | STMT_alt_type at -> + | STMT_alt_type at -> fmt_obox ff; fmt ff "alt type ("; fmt_lval ff at.alt_type_lval; @@ -1241,7 +1242,7 @@ and fmt_stmt_body (ff:Format.formatter) (s:stmt) : unit = fmt_obr ff; Array.iter (fmt_type_arm ff) at.alt_type_arms; begin - match at.alt_type_else with + match at.alt_type_else with None -> () | Some block -> fmt ff "@\n"; @@ -1252,7 +1253,8 @@ and fmt_stmt_body (ff:Format.formatter) (s:stmt) : unit = fmt_cbb ff; end; fmt_cbb ff; - | STMT_alt_port at -> + + | STMT_alt_port at -> fmt_obox ff; fmt ff "alt "; fmt_obr ff; @@ -1271,13 +1273,14 @@ and fmt_stmt_body (ff:Format.formatter) (s:stmt) : unit = fmt_cbb ff; end; fmt_cbb ff; - | STMT_note at -> + + | STMT_note at -> begin fmt ff "note "; fmt_atom ff at; fmt ff ";" end - | STMT_slice (dst, src, slice) -> + | STMT_slice (dst, src, slice) -> fmt_lval ff dst; fmt ff " = "; fmt_lval ff src; @@ -1285,11 +1288,11 @@ and fmt_stmt_body (ff:Format.formatter) (s:stmt) : unit = fmt_slice ff slice; fmt ff ";"; end - -and fmt_arm - (ff:Format.formatter) + +and fmt_arm + (ff:Format.formatter) (fmt_arm_case_expr : Format.formatter -> unit) - (block : block) + (block : block) : unit = fmt ff "@\n"; fmt_obox ff; @@ -1299,15 +1302,16 @@ and fmt_arm fmt_obr ff; fmt_stmts ff block.node; fmt_cbb ff; - + and fmt_tag_arm (ff:Format.formatter) (tag_arm:tag_arm) : unit = let (pat, block) = tag_arm.node in fmt_arm ff (fun ff -> fmt_pat ff pat) block; - + and fmt_type_arm (ff:Format.formatter) (type_arm:type_arm) : unit = let (_, slot, block) = type_arm.node in fmt_arm ff (fun ff -> fmt_slot ff slot) block; - + + and fmt_port_arm (ff:Format.formatter) (port_arm:port_arm) : unit = let (port_case, block) = port_arm.node in fmt_arm ff (fun ff -> fmt_port_case ff port_case) block; @@ -1317,7 +1321,6 @@ and fmt_port_case (ff:Format.formatter) (port_case:port_case) : unit = PORT_CASE_send params -> STMT_send params | PORT_CASE_recv params -> STMT_recv params in fmt_stmt ff {node = stmt'; id = Node 0}; - and fmt_pat (ff:Format.formatter) (pat:pat) : unit = match pat with @@ -1348,9 +1351,9 @@ and fmt_slice (ff:Format.formatter) (slice:slice) : unit = fmt ff "@]"; end; fmt ff "@])"; - - + + and fmt_decl_param (ff:Format.formatter) (param:ty_param) : unit = let (ident, (i, e)) = param in diff --git a/src/boot/fe/item.ml b/src/boot/fe/item.ml index 69fe5fc2..82ec2faf 100644 --- a/src/boot/fe/item.ml +++ b/src/boot/fe/item.ml @@ -253,7 +253,10 @@ and parse_stmts (ps:pstate) : Ast.stmt array = let lv = name_to_lval apos bpos name in Ast.PAT_tag (lv, paren_comma_list parse_pat ps) - | LIT_INT _ | LIT_CHAR _ | LIT_BOOL _ -> + | LIT_INT _ + | LIT_UINT _ + | LIT_CHAR _ + | LIT_BOOL _ -> Ast.PAT_lit (Pexp.parse_lit ps) | UNDERSCORE -> bump ps; Ast.PAT_wild diff --git a/src/boot/fe/pexp.ml b/src/boot/fe/pexp.ml index fb2d91a0..75983c7f 100644 --- a/src/boot/fe/pexp.ml +++ b/src/boot/fe/pexp.ml @@ -18,7 +18,7 @@ open Parser;; type pexp' = PEXP_call of (pexp * pexp array) - | PEXP_spawn of (Ast.domain * pexp) + | PEXP_spawn of (Ast.domain * string * pexp) | PEXP_bind of (pexp * pexp option array) | PEXP_rec of ((Ast.ident * Ast.mutability * pexp) array * pexp option) | PEXP_tup of ((Ast.mutability * pexp) array) @@ -558,9 +558,27 @@ and parse_bottom_pexp (ps:pstate) : pexp = THREAD -> bump ps; Ast.DOMAIN_thread | _ -> Ast.DOMAIN_local in - let pexp = ctxt "spawn [domain] pexp: init call" parse_pexp ps in + (* Spawns either have an explicit literal string for the spawned + task's name, or the task is named as the entry call + expression. *) + let explicit_name = + match peek ps with + LIT_STR s -> bump ps; Some s + | _ -> None + in + let pexp = + ctxt "spawn [domain] [name] pexp: init call" parse_pexp ps + in let bpos = lexpos ps in - span ps apos bpos (PEXP_spawn (domain, pexp)) + let name = + match explicit_name with + Some s -> s + (* FIXME: string_of_span returns a string like + "./driver.rs:10:16 - 11:52", not the actual text at those + characters *) + | None -> Session.string_of_span { lo = apos; hi = bpos } + in + span ps apos bpos (PEXP_spawn (domain, name, pexp)) | BIND -> let apos = lexpos ps in @@ -817,11 +835,33 @@ and parse_or_pexp (ps:pstate) : pexp = step lhs +and parse_as_pexp (ps:pstate) : pexp = + let apos = lexpos ps in + let pexp = ctxt "as pexp" parse_or_pexp ps in + let rec step accum = + match peek ps with + AS -> + bump ps; + let tapos = lexpos ps in + let t = parse_ty ps in + let bpos = lexpos ps in + let t = span ps tapos bpos t in + let node = + span ps apos bpos + (PEXP_unop ((Ast.UNOP_cast t), accum)) + in + step node + + | _ -> accum + in + step pexp + + and parse_relational_pexp (ps:pstate) : pexp = let name = "relational pexp" in let apos = lexpos ps in - let lhs = ctxt (name ^ " lhs") parse_or_pexp ps in - let build = binop_build ps name apos parse_or_pexp in + let lhs = ctxt (name ^ " lhs") parse_as_pexp ps in + let build = binop_build ps name apos parse_as_pexp in let rec step accum = match peek ps with LT -> build accum step Ast.BINOP_lt @@ -883,30 +923,8 @@ and parse_oror_pexp (ps:pstate) : pexp = step lhs -and parse_as_pexp (ps:pstate) : pexp = - let apos = lexpos ps in - let pexp = ctxt "as pexp" parse_oror_pexp ps in - let rec step accum = - match peek ps with - AS -> - bump ps; - let tapos = lexpos ps in - let t = parse_ty ps in - let bpos = lexpos ps in - let t = span ps tapos bpos t in - let node = - span ps apos bpos - (PEXP_unop ((Ast.UNOP_cast t), accum)) - in - step node - - | _ -> accum - in - step pexp - - and parse_pexp (ps:pstate) : pexp = - parse_as_pexp ps + parse_oror_pexp ps and parse_mutable_and_pexp (ps:pstate) : (Ast.mutability * pexp) = let mutability = parse_mutability ps in @@ -1183,7 +1201,7 @@ and desugar_expr_init let bind_stmt = ss (Ast.STMT_bind (dst_lval, fn_lval, arg_atoms)) in ac [ fn_stmts; arg_stmts; [| bind_stmt |] ] - | PEXP_spawn (domain, sub) -> + | PEXP_spawn (domain, name, sub) -> begin match sub.node with PEXP_call (fn, args) -> @@ -1191,7 +1209,8 @@ and desugar_expr_init let (arg_stmts, arg_atoms) = desugar_expr_atoms ps args in let fn_lval = atom_lval ps fn_atom in let spawn_stmt = - ss (Ast.STMT_spawn (dst_lval, domain, fn_lval, arg_atoms)) + ss (Ast.STMT_spawn + (dst_lval, domain, name, fn_lval, arg_atoms)) in ac [ fn_stmts; arg_stmts; [| spawn_stmt |] ] | _ -> raise (err "non-call spawn" ps) diff --git a/src/boot/llvm/lltrans.ml b/src/boot/llvm/lltrans.ml index ee192725..c1ef49af 100644 --- a/src/boot/llvm/lltrans.ml +++ b/src/boot/llvm/lltrans.ml @@ -588,7 +588,7 @@ let trans_crate (* Maps a fn's or block's id to an LLVM metadata node (subprogram or lexical block) representing it. *) let (dbg_llscopes:(node_id, Llvm.llvalue) Hashtbl.t) = Hashtbl.create 0 in - let declare_mod_item + let rec declare_mod_item (name:Ast.ident) mod_item : unit = @@ -616,9 +616,8 @@ let trans_crate | Ast.MOD_ITEM_type _ -> () (* Types get translated with their terms. *) - | Ast.MOD_ITEM_mod _ -> - () (* Modules simply contain other items that are translated - on their own. *) + | Ast.MOD_ITEM_mod (_, items) -> + Hashtbl.iter declare_mod_item items | _ -> Common.unimpl (Some id) @@ -807,6 +806,17 @@ let trans_crate Ast.sprintf_lval lval) in + let trans_callee (fn:Ast.lval) : (Llvm.llvalue * Ast.ty) = + let fty = Hashtbl.find sem_cx.ctxt_all_lval_types (lval_base_id fn) in + if lval_base_is_item sem_cx fn then + let fn_item = lval_item sem_cx fn in + let llfn = Hashtbl.find llitems (fn_item.id) in + (llfn, fty) + else + (* indirect call to computed slot *) + trans_lval fn + in + let trans_atom (atom:Ast.atom) : Llvm.llvalue = iflog (fun _ -> log sem_cx "trans_atom: %a" Ast.sprintf_atom atom); match atom with @@ -959,7 +969,7 @@ let trans_crate | 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 (llfn, _) = trans_callee 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; @@ -1072,13 +1082,22 @@ let trans_crate ignore (Llvm.build_br llbodyblock llinitbuilder) in - let trans_mod_item - (_:Ast.ident) - { node = { Ast.decl_item = (item:Ast.mod_item') }; id = id } + let rec trans_mod_item + (name:Ast.ident) + mod_item : unit = + let { node = { Ast.decl_item = (item:Ast.mod_item') }; id = id } = + mod_item in match item with - Ast.MOD_ITEM_fn fn -> trans_fn fn id - | _ -> () + Ast.MOD_ITEM_type _ -> + () (* Types get translated with their terms. *) + | Ast.MOD_ITEM_mod (_, items) -> + Hashtbl.iter trans_mod_item items + | Ast.MOD_ITEM_fn fn -> trans_fn fn id + | _ -> Common.unimpl (Some id) + "LLVM module declaration for: %a" + Ast.sprintf_mod_item (name, mod_item) + in let exit_task_glue = diff --git a/src/boot/me/alias.ml b/src/boot/me/alias.ml index 94d34fb2..27575324 100644 --- a/src/boot/me/alias.ml +++ b/src/boot/me/alias.ml @@ -59,7 +59,7 @@ let alias_analysis_visitor * survive 'into' a sub-block (those formed during iteration) * need to be handled in this module. *) Ast.STMT_call (dst, callee, args) - | Ast.STMT_spawn (dst, _, callee, args) + | Ast.STMT_spawn (dst, _, _, callee, args) -> alias_call_args dst callee args | Ast.STMT_send (_, src) -> alias src diff --git a/src/boot/me/dwarf.ml b/src/boot/me/dwarf.ml index d3fb81de..0ec7af78 100644 --- a/src/boot/me/dwarf.ml +++ b/src/boot/me/dwarf.ml @@ -1677,7 +1677,7 @@ let dwarf_visitor in let record trec = - let rty = referent_type abi (Ast.TY_rec trec) in + let rty = referent_type word_bits (Ast.TY_rec trec) in let rty_sz = Il.referent_ty_size abi.Abi.abi_word_bits in let fix = new_fixup "record type DIE" in let die = DEF (fix, SEQ [| @@ -1926,7 +1926,7 @@ let dwarf_visitor * I'm a bit surprised by that! *) - let rty = referent_type abi (Ast.TY_tag ttag) in + let rty = referent_type word_bits (Ast.TY_tag ttag) in let rty_sz = Il.referent_ty_size abi.Abi.abi_word_bits in let rtys = match rty with diff --git a/src/boot/me/effect.ml b/src/boot/me/effect.ml index 79868def..73797409 100644 --- a/src/boot/me/effect.ml +++ b/src/boot/me/effect.ml @@ -62,7 +62,7 @@ let mutability_checking_visitor match s.node with Ast.STMT_copy (lv_dst, _) | Ast.STMT_call (lv_dst, _, _) - | Ast.STMT_spawn (lv_dst, _, _, _) + | Ast.STMT_spawn (lv_dst, _, _, _, _) | Ast.STMT_recv (lv_dst, _) | Ast.STMT_bind (lv_dst, _, _) | Ast.STMT_new_rec (lv_dst, _, _) diff --git a/src/boot/me/layout.ml b/src/boot/me/layout.ml index a9358795..cfd087ff 100644 --- a/src/boot/me/layout.ml +++ b/src/boot/me/layout.ml @@ -113,7 +113,7 @@ let layout_visitor | Il.CodeTy -> true | Il.NilTy -> false in - rt_in_mem (slot_referent_type cx.ctxt_abi slot) + rt_in_mem (slot_referent_type cx.ctxt_abi.Abi.abi_word_bits slot) in let rty_sz rty = Il.referent_ty_size cx.ctxt_abi.Abi.abi_word_bits rty in @@ -142,7 +142,7 @@ let layout_visitor : unit = let accum (off,align) id : (size * size) = let slot = get_slot cx id in - let rt = slot_referent_type cx.ctxt_abi slot in + let rt = slot_referent_type cx.ctxt_abi.Abi.abi_word_bits slot in let (elt_size, elt_align) = rty_layout rt in if vregs_ok && (is_subword_size elt_size) @@ -170,7 +170,9 @@ let layout_visitor then elt_off else neg_sz (add_sz elt_off elt_size) in - Stack.push (slot_referent_type cx.ctxt_abi slot) slot_accum; + Stack.push + (slot_referent_type cx.ctxt_abi.Abi.abi_word_bits slot) + slot_accum; iflog begin fun _ -> @@ -400,7 +402,7 @@ let layout_visitor let callees = match s.node with Ast.STMT_call (_, lv, _) - | Ast.STMT_spawn (_, _, lv, _) -> [| lv |] + | Ast.STMT_spawn (_, _, _, lv, _) -> [| lv |] | Ast.STMT_check (_, calls) -> Array.map (fun (lv, _) -> lv) calls | _ -> [| |] in diff --git a/src/boot/me/semant.ml b/src/boot/me/semant.ml index bcaec2b4..a3a8abdf 100644 --- a/src/boot/me/semant.ml +++ b/src/boot/me/semant.ml @@ -1822,24 +1822,24 @@ let run_passes (* Rust type -> IL type conversion. *) -let word_sty (abi:Abi.abi) : Il.scalar_ty = - Il.ValTy abi.Abi.abi_word_bits +let word_sty (word_bits:Il.bits) : Il.scalar_ty = + Il.ValTy word_bits ;; -let word_rty (abi:Abi.abi) : Il.referent_ty = - Il.ScalarTy (word_sty abi) +let word_rty (word_bits:Il.bits) : Il.referent_ty = + Il.ScalarTy (word_sty word_bits) ;; -let tydesc_rty (abi:Abi.abi) : Il.referent_ty = +let tydesc_rty (word_bits:Il.bits) : Il.referent_ty = (* * NB: must match corresponding tydesc structure * in trans and offsets in ABI exactly. *) Il.StructTy [| - word_rty abi; (* Abi.tydesc_field_first_param *) - word_rty abi; (* Abi.tydesc_field_size *) - word_rty abi; (* Abi.tydesc_field_align *) + word_rty word_bits; (* Abi.tydesc_field_first_param *) + word_rty word_bits; (* Abi.tydesc_field_size *) + word_rty word_bits; (* Abi.tydesc_field_align *) Il.ScalarTy (Il.AddrTy Il.CodeTy); (* Abi.tydesc_field_copy_glue *) Il.ScalarTy (Il.AddrTy Il.CodeTy); (* Abi.tydesc_field_drop_glue *) Il.ScalarTy (Il.AddrTy Il.CodeTy); (* Abi.tydesc_field_free_glue *) @@ -1849,29 +1849,29 @@ let tydesc_rty (abi:Abi.abi) : Il.referent_ty = |] ;; -let obj_closure_rty (abi:Abi.abi) : Il.referent_ty = +let obj_closure_rty (word_bits:Il.bits) : Il.referent_ty = Il.StructTy [| - word_rty abi; + word_rty word_bits; Il.StructTy [| - Il.ScalarTy (Il.AddrTy (tydesc_rty abi)); - word_rty abi (* A lie: it's opaque, but this permits - * GEP'ing to it. *) + Il.ScalarTy (Il.AddrTy (tydesc_rty word_bits)); + word_rty word_bits (* A lie: it's opaque, but this permits + * GEP'ing to it. *) |] |] ;; -let rec referent_type (abi:Abi.abi) (t:Ast.ty) : Il.referent_ty = +let rec referent_type (word_bits:Il.bits) (t:Ast.ty) : Il.referent_ty = let s t = Il.ScalarTy t in let v b = Il.ValTy b in let p t = Il.AddrTy t in let sv b = s (v b) in let sp t = s (p t) in - let word = word_rty abi in + let word = word_rty word_bits in let ptr = sp Il.OpaqueTy in let rc_ptr = sp (Il.StructTy [| word; Il.OpaqueTy |]) in let codeptr = sp Il.CodeTy in - let tup ttup = Il.StructTy (Array.map (referent_type abi) ttup) in + let tup ttup = Il.StructTy (Array.map (referent_type word_bits) ttup) in let tag ttag = let union = Il.UnionTy @@ -1916,7 +1916,7 @@ let rec referent_type (abi:Abi.abi) (t:Ast.ty) : Il.referent_ty = Il.StructTy [| codeptr; fn_closure_ptr |] | Ast.TY_obj _ -> - let obj_closure_ptr = sp (obj_closure_rty abi) in + let obj_closure_ptr = sp (obj_closure_rty word_bits) in Il.StructTy [| ptr; obj_closure_ptr |] | Ast.TY_tag ttag -> tag ttag @@ -1928,26 +1928,26 @@ let rec referent_type (abi:Abi.abi) (t:Ast.ty) : Il.referent_ty = | Ast.TY_port _ | Ast.TY_task -> rc_ptr - | Ast.TY_type -> sp (tydesc_rty abi) + | Ast.TY_type -> sp (tydesc_rty word_bits) | Ast.TY_native _ -> ptr | Ast.TY_box t -> - sp (Il.StructTy [| word; referent_type abi t |]) + sp (Il.StructTy [| word; referent_type word_bits t |]) - | Ast.TY_mutable t -> referent_type abi t + | Ast.TY_mutable t -> referent_type word_bits t | Ast.TY_param (i, _) -> Il.ParamTy i | Ast.TY_named _ -> bug () "named type in referent_type" - | Ast.TY_constrained (t, _) -> referent_type abi t + | Ast.TY_constrained (t, _) -> referent_type word_bits t -and slot_referent_type (abi:Abi.abi) (sl:Ast.slot) : Il.referent_ty = +and slot_referent_type (word_bits:Il.bits) (sl:Ast.slot) : Il.referent_ty = let s t = Il.ScalarTy t in let p t = Il.AddrTy t in let sp t = s (p t) in - let rty = referent_type abi (slot_ty sl) in + let rty = referent_type word_bits (slot_ty sl) in match sl.Ast.slot_mode with | Ast.MODE_local -> rty | Ast.MODE_alias -> sp rty @@ -1958,7 +1958,7 @@ let task_rty (abi:Abi.abi) : Il.referent_ty = begin Array.init Abi.n_visible_task_fields - (fun _ -> word_rty abi) + (fun _ -> word_rty abi.Abi.abi_word_bits) end ;; @@ -1970,14 +1970,17 @@ let call_args_referent_type_full (iterator_arg_rtys:Il.referent_ty array) (indirect_arg_rtys:Il.referent_ty array) : Il.referent_ty = - let out_slot_rty = slot_referent_type abi out_slot in + let out_slot_rty = slot_referent_type abi.Abi.abi_word_bits out_slot in let out_ptr_rty = Il.ScalarTy (Il.AddrTy out_slot_rty) in let task_ptr_rty = Il.ScalarTy (Il.AddrTy (task_rty abi)) in let ty_param_rtys = - let td = Il.ScalarTy (Il.AddrTy (tydesc_rty abi)) in + let td = Il.ScalarTy (Il.AddrTy (tydesc_rty abi.Abi.abi_word_bits)) in Il.StructTy (Array.init n_ty_params (fun _ -> td)) in - let arg_rtys = Il.StructTy (Array.map (slot_referent_type abi) in_slots) in + let arg_rtys = + Il.StructTy + (Array.map (slot_referent_type abi.Abi.abi_word_bits) in_slots) + in (* * NB: must match corresponding calltup structure in trans and * member indices in ABI exactly. @@ -2003,7 +2006,7 @@ let call_args_referent_type (* Abi.indirect_args_elt_closure *) match closure with None -> - [| word_rty cx.ctxt_abi |] + [| word_rty cx.ctxt_abi.Abi.abi_word_bits |] | Some c -> [| Il.ScalarTy (Il.AddrTy c) |] in @@ -2057,16 +2060,18 @@ let direct_call_args_referent_type ;; let ty_sz (abi:Abi.abi) (t:Ast.ty) : int64 = - force_sz (Il.referent_ty_size abi.Abi.abi_word_bits (referent_type abi t)) + let wb = abi.Abi.abi_word_bits in + force_sz (Il.referent_ty_size wb (referent_type wb t)) ;; let ty_align (abi:Abi.abi) (t:Ast.ty) : int64 = - force_sz (Il.referent_ty_align abi.Abi.abi_word_bits (referent_type abi t)) + let wb = abi.Abi.abi_word_bits in + force_sz (Il.referent_ty_align wb (referent_type wb t)) ;; let slot_sz (abi:Abi.abi) (s:Ast.slot) : int64 = - force_sz (Il.referent_ty_size abi.Abi.abi_word_bits - (slot_referent_type abi s)) + let wb = abi.Abi.abi_word_bits in + force_sz (Il.referent_ty_size wb (slot_referent_type wb s)) ;; let word_slot (abi:Abi.abi) : Ast.slot = diff --git a/src/boot/me/trans.ml b/src/boot/me/trans.ml index f2bb2287..620b27e7 100644 --- a/src/boot/me/trans.ml +++ b/src/boot/me/trans.ml @@ -163,7 +163,6 @@ let trans_visitor abi.Abi.abi_emit_target_specific vregs_ok fnid in - Stack.push (Hashtbl.create 0) e.Il.emit_size_cache; Stack.push e emitters; in @@ -172,16 +171,20 @@ let trans_visitor let pop_emitter _ = ignore (Stack.pop emitters) in let emitter _ = Stack.top emitters in - let emitter_size_cache _ = Stack.top (emitter()).Il.emit_size_cache in - let push_emitter_size_cache _ = - Stack.push - (Hashtbl.copy (emitter_size_cache())) - (emitter()).Il.emit_size_cache + let emitter_size_cache _ = (emitter()).Il.emit_size_cache in + let flush_emitter_size_cache _ = + Hashtbl.clear (emitter_size_cache()) in - let pop_emitter_size_cache _ = - ignore (Stack.pop (emitter()).Il.emit_size_cache) + + let emit q = + begin + match q with + Il.Jmp _ -> flush_emitter_size_cache(); + | _ -> () + end; + Il.emit (emitter()) q in - let emit q = Il.emit (emitter()) q in + let next_vreg _ = Il.next_vreg (emitter()) in let next_vreg_cell t = Il.next_vreg_cell (emitter()) t in let next_spill_cell t = @@ -190,12 +193,17 @@ let trans_visitor let spill_ta = (spill_mem, Il.ScalarTy t) in Il.Mem spill_ta in - let mark _ : quad_idx = (emitter()).Il.emit_pc in + let mark _ : quad_idx = + flush_emitter_size_cache (); + (emitter()).Il.emit_pc + in let patch_existing (jmp:quad_idx) (targ:quad_idx) : unit = - Il.patch_jump (emitter()) jmp targ + Il.patch_jump (emitter()) jmp targ; + flush_emitter_size_cache (); in let patch (i:quad_idx) : unit = Il.patch_jump (emitter()) i (mark()); + flush_emitter_size_cache (); (* Insert a dead quad to ensure there's an otherwise-unused * jump-target here. *) @@ -284,7 +292,7 @@ let trans_visitor in let ptr_at (mem:Il.mem) (pointee_ty:Ast.ty) : Il.cell = - rty_ptr_at mem (referent_type abi pointee_ty) + rty_ptr_at mem (referent_type word_bits pointee_ty) in let need_scalar_ty (rty:Il.referent_ty) : Il.scalar_ty = @@ -322,11 +330,7 @@ let trans_visitor (cell_str mem_cell) in - let rec ptr_cast (cell:Il.cell) (rty:Il.referent_ty) : Il.cell = - match cell with - Il.Mem (mem, _) -> Il.Mem (mem, rty) - | Il.Reg (reg, Il.AddrTy _) -> Il.Reg (reg, Il.AddrTy rty) - | _ -> bug () "expected address cell in Trans.ptr_cast" + let rec ptr_cast = Il.ptr_cast and curr_crate_ptr _ : Il.cell = word_at (fp_imm frame_crate_ptr) @@ -445,13 +449,22 @@ let trans_visitor in let slot_id_referent_type (slot_id:node_id) : Il.referent_ty = - slot_referent_type abi (get_slot cx slot_id) + slot_referent_type word_bits (get_slot cx slot_id) in let caller_args_cell (args_rty:Il.referent_ty) : Il.cell = Il.Mem (fp_imm out_mem_disp, args_rty) in + let get_obj_box_from_calltup (args_cell:Il.cell) = + let indirect_args = + get_element_ptr args_cell Abi.calltup_elt_indirect_args + in + deref (ptr_cast + (get_element_ptr indirect_args Abi.indirect_args_elt_closure) + (Il.ScalarTy (Il.AddrTy (obj_closure_rty word_bits)))) + in + let fp_to_args (fp:Il.cell) (args_rty:Il.referent_ty): Il.cell = let (reg, _) = force_to_reg (Il.Cell fp) in Il.Mem(based_imm reg out_mem_disp, args_rty) @@ -461,11 +474,43 @@ let trans_visitor get_element_ptr ty_params param_idx in - let get_ty_params_of_frame (fp:Il.reg) (n_params:int) : Il.cell = - let fn_ty = mk_simple_ty_fn [| |] in - let fn_rty = call_args_referent_type cx n_params fn_ty None in - let args_cell = Il.Mem (based_imm fp out_mem_disp, fn_rty) in - get_element_ptr args_cell Abi.calltup_elt_ty_params + let get_ty_params_of_frame + (fnid:node_id) + (fp:Il.reg) + (n_ty_params:int) + : Il.cell = + + let fn_ty = mk_simple_ty_fn [| |] in + let fn_rty = + call_args_referent_type cx n_ty_params fn_ty (Some Il.OpaqueTy) + in + let args_cell = Il.Mem (based_imm fp out_mem_disp, fn_rty) in + + if defn_id_is_obj_fn_or_drop cx fnid + then + (* + * To get the typarams in an obj fn, we must go to the + * implicit obj's captured type descriptor. + *) + let obj_box = + get_obj_box_from_calltup args_cell + in + let obj = get_element_ptr obj_box Abi.box_rc_field_body in + let tydesc = get_element_ptr obj Abi.obj_body_elt_tydesc in + let ty_params_ty = Ast.TY_tup (make_tydesc_tys n_ty_params) in + let ty_params_rty = referent_type word_bits ty_params_ty in + let ty_params = + get_element_ptr (deref tydesc) Abi.tydesc_field_first_param + in + let ty_params = + ptr_cast ty_params (Il.ScalarTy (Il.AddrTy ty_params_rty)) + in + deref ty_params + else + (* + * Regular function --- typarams are right in the frame calltup. + *) + get_element_ptr args_cell Abi.calltup_elt_ty_params in let get_args_for_current_frame _ = @@ -512,34 +557,10 @@ let trans_visitor Abi.iterator_args_elt_outer_frame_ptr in - let get_obj_for_current_frame _ = - deref (ptr_cast - (get_closure_for_current_frame ()) - (Il.ScalarTy (Il.AddrTy (obj_closure_rty abi)))) - in - let get_ty_params_of_current_frame _ : Il.cell = - let id = current_fn() in - let n_ty_params = n_item_ty_params cx id in - if defn_id_is_obj_fn_or_drop cx id - then - begin - let obj_box = get_obj_for_current_frame() in - let obj = get_element_ptr obj_box Abi.box_rc_field_body in - let tydesc = get_element_ptr obj Abi.obj_body_elt_tydesc in - let ty_params_ty = Ast.TY_tup (make_tydesc_tys n_ty_params) in - let ty_params_rty = referent_type abi ty_params_ty in - let ty_params = - get_element_ptr (deref tydesc) Abi.tydesc_field_first_param - in - let ty_params = - ptr_cast ty_params (Il.ScalarTy (Il.AddrTy ty_params_rty)) - in - deref ty_params - end - - else - get_ty_params_of_frame abi.Abi.abi_fp_reg n_ty_params + let fnid = current_fn() in + let n_ty_params = n_item_ty_params cx fnid in + get_ty_params_of_frame fnid abi.Abi.abi_fp_reg n_ty_params in let get_ty_param_in_current_frame (param_idx:int) : Il.cell = @@ -583,7 +604,13 @@ let trans_visitor (string_of_size size))); let sub_sz = calculate_sz ty_params in match htab_search (emitter_size_cache()) size with - Some op -> op + Some op -> + iflog (fun _ -> annotate + (Printf.sprintf "cached size %s is %s" + (string_of_size size) + (oper_str op))); + op + | _ -> let res = match size with @@ -707,7 +734,7 @@ let trans_visitor in let ty_sz_in_current_frame (ty:Ast.ty) : Il.operand = - let rty = referent_type abi ty in + let rty = referent_type word_bits ty in let sz = Il.referent_ty_size word_bits rty in calculate_sz_in_current_frame sz in @@ -716,7 +743,7 @@ let trans_visitor (ty_params:Il.cell) (ty:Ast.ty) : Il.operand = - let rty = referent_type abi ty in + let rty = referent_type word_bits ty in let sz = Il.referent_ty_size word_bits rty in calculate_sz ty_params sz in @@ -914,9 +941,10 @@ let trans_visitor let atop = trans_atom at in let unit_sz = ty_sz_in_current_frame ty in let idx = next_vreg_cell word_sty in - emit (Il.binary Il.UMUL idx atop unit_sz); + mov idx atop; + emit (Il.binary Il.UMUL idx (Il.Cell idx) unit_sz); let elt_mem = trans_bounds_check (deref cell) (Il.Cell idx) in - (Il.Mem (elt_mem, referent_type abi ty), ty) + (Il.Mem (elt_mem, referent_type word_bits ty), ty) in (* * All lval components aside from explicit-deref just auto-deref @@ -1105,7 +1133,7 @@ let trans_visitor and trans_static_string (s:string) : Il.operand = Il.Cell (crate_rel_to_ptr (trans_crate_rel_static_string_operand s) - (referent_type abi Ast.TY_str)) + (referent_type word_bits Ast.TY_str)) and get_static_tydesc (idopt:node_id option) @@ -1211,7 +1239,7 @@ let trans_visitor let fty = Hashtbl.find (snd caller) ident in let self_args_rty = call_args_referent_type cx 0 - (Ast.TY_fn fty) (Some (obj_closure_rty abi)) + (Ast.TY_fn fty) (Some (obj_closure_rty word_bits)) in let callsz = Il.referent_ty_size word_bits self_args_rty in let spill = new_fixup "forwarding fn spill" in @@ -1379,7 +1407,7 @@ let trans_visitor push_new_emitter_with_vregs None; iflog (fun _ -> annotate "prologue"); abi.Abi.abi_emit_fn_prologue (emitter()) - framesz callsz nabi_rust (upcall_fixup "upcall_grow_task"); + framesz callsz nabi_rust (upcall_fixup "upcall_grow_task") false; write_frame_info_ptrs None; (* FIXME: not clear why, but checking interrupt in glue context * causes many.rs to crash when run on a sufficiently large number @@ -1458,8 +1486,8 @@ let trans_visitor (* FIXME (issue #5): mutability flag *) : Il.referent_ty = let rc = Il.ScalarTy word_sty in - let targ = referent_type abi (mk_simple_ty_fn [||]) in - let bindings = Array.map (slot_referent_type abi) bs in + let targ = referent_type word_bits (mk_simple_ty_fn [||]) in + let bindings = Array.map (slot_referent_type word_bits) bs in Il.StructTy [| rc; targ; Il.StructTy bindings |] (* FIXME (issue #2): this should eventually use tail calling logic *) @@ -1923,8 +1951,8 @@ let trans_visitor : quad_idx list = emit (Il.cmp (Il.Cell (Il.Reg (force_to_reg lhs))) rhs); let jmp = mark() in - emit (Il.jmp cjmp Il.CodeNone); - [ jmp ] + emit (Il.jmp cjmp Il.CodeNone); + [ jmp ] and trans_compare ?ty_params:(ty_params=get_ty_params_of_current_frame()) @@ -1943,7 +1971,6 @@ let trans_visitor | _ -> trans_compare_simple cjmp lhs rhs and trans_cond (invert:bool) (expr:Ast.expr) : quad_idx list = - let anno _ = iflog begin @@ -2075,15 +2102,14 @@ let trans_visitor trans_atom a and trans_block (block:Ast.block) : unit = + flush_emitter_size_cache(); trace_str cx.ctxt_sess.Session.sess_trace_block "entering block"; - push_emitter_size_cache (); emit (Il.Enter (Hashtbl.find cx.ctxt_block_fixups block.id)); Array.iter trans_stmt block.node; trace_str cx.ctxt_sess.Session.sess_trace_block "exiting block"; emit Il.Leave; - pop_emitter_size_cache (); trace_str cx.ctxt_sess.Session.sess_trace_block "exited block"; @@ -2115,10 +2141,12 @@ let trans_visitor ((*initializing*)_:bool) (dst:Ast.lval) (domain:Ast.domain) + (name:string) (fn_lval:Ast.lval) (args:Ast.atom array) : unit = let (task_cell, _) = trans_lval_init dst in + let runtime_name = trans_static_string name in let (fptr_operand, fn_ty) = trans_callee fn_lval in (*let fn_ty_params = [| |] in*) let _ = @@ -2152,7 +2180,7 @@ let trans_visitor match domain with Ast.DOMAIN_thread -> begin - trans_upcall "upcall_new_thread" new_task [| |]; + trans_upcall "upcall_new_thread" new_task [| runtime_name |]; copy_fn_args false true (CLONE_all new_task) call; trans_upcall "upcall_start_thread" task_cell [| @@ -2164,7 +2192,7 @@ let trans_visitor end | _ -> begin - trans_upcall "upcall_new_task" new_task [| |]; + trans_upcall "upcall_new_task" new_task [| runtime_name |]; copy_fn_args false true (CLONE_chan new_task) call; trans_upcall "upcall_start_task" task_cell [| @@ -2318,7 +2346,7 @@ let trans_visitor (get_element_ptr_dyn_in_current_frame vec Abi.vec_elt_data)) in - let unit_rty = referent_type abi unit_ty in + let unit_rty = referent_type word_bits unit_ty in let body_rty = Il.StructTy (Array.map (fun _ -> unit_rty) atoms) in let body = Il.Mem (body_mem, body_rty) in Array.iteri @@ -2364,12 +2392,12 @@ let trans_visitor let root_desc = Il.Cell (crate_rel_to_ptr (get_static_tydesc idopt t 0L 0L force_stateful) - (tydesc_rty abi)) + (tydesc_rty word_bits)) in let (t, param_descs) = linearize_ty_params t in let descs = Array.append [| root_desc |] param_descs in let n = Array.length descs in - let rty = referent_type abi t in + let rty = referent_type word_bits t in let (size_sz, align_sz) = Il.referent_ty_layout word_bits rty in let size = calculate_sz_in_current_frame size_sz in let align = calculate_sz_in_current_frame align_sz in @@ -2405,7 +2433,7 @@ let trans_visitor (ty_sz abi ty) (ty_align abi ty) mut) - (tydesc_rty abi)) + (tydesc_rty word_bits)) and box_rc_cell (cell:Il.cell) : Il.cell = get_element_ptr (deref cell) Abi.box_rc_field_refcnt @@ -2422,7 +2450,7 @@ let trans_visitor in let ty = simplified_ty ty in let refty_sz = - Il.referent_ty_size abi.Abi.abi_word_bits (referent_type abi ty) + Il.referent_ty_size abi.Abi.abi_word_bits (referent_type word_bits ty) in match refty_sz with SIZE_fixed _ -> imm (Int64.add (ty_sz abi ty) header_sz) @@ -2519,7 +2547,7 @@ let trans_visitor trans_compare_simple Il.JAE (Il.Cell ptr) (Il.Cell lim) in let unit_cell = - deref (ptr_cast ptr (referent_type abi unit_ty)) + deref (ptr_cast ptr (referent_type word_bits unit_ty)) in f unit_cell unit_cell unit_ty curr_iso; add_to ptr unit_sz; @@ -2919,6 +2947,7 @@ let trans_visitor (slot:Ast.slot) (curr_iso:Ast.ty_iso option) : unit = + check_and_flush_chan cell slot; drop_slot (get_ty_params_of_current_frame()) cell slot curr_iso and drop_ty_in_current_frame @@ -4175,6 +4204,25 @@ let trans_visitor let last_jumps = Array.map trans_arm at.Ast.alt_tag_arms in Array.iter patch last_jumps + (* If we're about to drop a channel, synthesize an upcall_flush_chan. + * TODO: This should rather appear in a chan dtor when chans become + * objects. *) + and check_and_flush_chan + (cell:Il.cell) + (slot:Ast.slot) + : unit = + let ty = strip_mutable_or_constrained_ty (slot_ty slot) in + match simplified_ty ty with + Ast.TY_chan _ -> + annotate "check_and_flush_chan, flush_chan"; + let rc = box_rc_cell cell in + emit (Il.cmp (Il.Cell rc) one); + let jump = mark () in + emit (Il.jmp Il.JNE Il.CodeNone); + trans_void_upcall "upcall_flush_chan" [| Il.Cell cell |]; + patch jump; + | _ -> () + and drop_slots_at_curr_stmt _ : unit = let stmt = Stack.top curr_stmt in match htab_search cx.ctxt_post_stmt_slot_drops stmt with @@ -4277,7 +4325,7 @@ let trans_visitor push_new_emitter_with_vregs (Some id); iflog (fun _ -> annotate "prologue"); abi.Abi.abi_emit_fn_prologue (emitter()) - framesz callsz nabi_rust (upcall_fixup "upcall_grow_task"); + framesz callsz nabi_rust (upcall_fixup "upcall_grow_task") false; write_frame_info_ptrs None; iflog (fun _ -> annotate "finished prologue"); trans_block fe.Ast.for_each_body; @@ -4361,7 +4409,7 @@ let trans_visitor let dst_fill = get_element_ptr dst_vec Abi.vec_elt_fill in (* Copy loop: *) - let eltp_rty = Il.AddrTy (referent_type abi elt_ty) in + let eltp_rty = Il.AddrTy (referent_type word_bits elt_ty) in let dptr = next_vreg_cell eltp_rty in let sptr = next_vreg_cell eltp_rty in let dlim = next_vreg_cell eltp_rty in @@ -4395,11 +4443,11 @@ let trans_visitor let back_jmp = trans_compare_simple Il.JB (Il.Cell dptr) (Il.Cell dlim) in - List.iter - (fun j -> patch_existing j back_jmp_targ) back_jmp; - let v = next_vreg_cell word_sty in - mov v (Il.Cell src_fill); - add_to dst_fill (Il.Cell v); + List.iter + (fun j -> patch_existing j back_jmp_targ) back_jmp; + let v = next_vreg_cell word_sty in + mov v (Il.Cell src_fill); + add_to dst_fill (Il.Cell v); | t -> begin bug () "unsupported vector-append type %a" Ast.sprintf_ty t @@ -4483,8 +4531,9 @@ let trans_visitor | Ast.STMT_send (chan,src) -> trans_send chan src - | Ast.STMT_spawn (dst, domain, plv, args) -> - trans_spawn (maybe_init stmt.id "spawn" dst) dst domain plv args + | Ast.STMT_spawn (dst, domain, name, plv, args) -> + trans_spawn (maybe_init stmt.id "spawn" dst) dst + domain name plv args | Ast.STMT_recv (dst, chan) -> trans_recv (maybe_init stmt.id "recv" dst) dst chan @@ -4680,6 +4729,8 @@ let trans_visitor let get_frame_glue glue inner = get_mem_glue glue begin + (* `mem` here is a pointer to the frame we are marking, dropping, + or relocing, etc. *) fun mem -> iter_frame_and_arg_slots cx fnid begin @@ -4687,10 +4738,19 @@ let trans_visitor match htab_search cx.ctxt_slot_offsets slot_id with Some off when not (slot_is_obj_state cx slot_id) -> let referent_type = slot_id_referent_type slot_id in + (* + * This might look as though we're always taking the + * pointer-to-frame and giving it the type of the + * frame/arg of interest, but this is because our + * deref_off a few lines later takes the referent + * type of the given poiinter (`st`) as the referent + * type of the mem-offset-from-the-given-pointer + * that it returns. + *) let fp_cell = rty_ptr_at mem referent_type in let (fp, st) = force_to_reg (Il.Cell fp_cell) in let ty_params = - get_ty_params_of_frame fp n_ty_params + get_ty_params_of_frame fnid fp n_ty_params in let slot_cell = deref_off_sz ty_params (Il.Reg (fp,st)) off @@ -4738,7 +4798,7 @@ let trans_visitor end in - let trans_frame_entry (fnid:node_id) : unit = + let trans_frame_entry (fnid:node_id) (obj_fn:bool) : unit = let framesz = get_framesz cx fnid in let callsz = get_callsz cx fnid in Stack.push (Stack.create()) epilogue_jumps; @@ -4752,7 +4812,7 @@ let trans_visitor (string_of_size callsz))); abi.Abi.abi_emit_fn_prologue (emitter()) framesz callsz nabi_rust - (upcall_fixup "upcall_grow_task"); + (upcall_fixup "upcall_grow_task") obj_fn; write_frame_info_ptrs (Some fnid); check_interrupt_flag (); @@ -4776,8 +4836,9 @@ let trans_visitor let trans_fn (fnid:node_id) (body:Ast.block) + (obj_fn:bool) : unit = - trans_frame_entry fnid; + trans_frame_entry fnid obj_fn; trans_block body; trans_frame_exit fnid true; in @@ -4786,7 +4847,7 @@ let trans_visitor (obj_id:node_id) (header:Ast.header_slots) : unit = - trans_frame_entry obj_id; + trans_frame_entry obj_id true; let all_args_rty = current_fn_args_rty None in let all_args_cell = caller_args_cell all_args_rty in @@ -4805,7 +4866,7 @@ let trans_visitor let obj_args_ty = Ast.TY_tup obj_args_tup in let state_ty = Ast.TY_tup [| Ast.TY_type; obj_args_ty |] in let state_ptr_ty = Ast.TY_box state_ty in - let state_ptr_rty = referent_type abi state_ptr_ty in + let state_ptr_rty = referent_type word_bits state_ptr_ty in let state_malloc_sz = box_allocation_size state_ptr_ty in let ctor_ty = Hashtbl.find cx.ctxt_all_item_types obj_id in @@ -4907,7 +4968,7 @@ let trans_visitor in let trans_required_fn (fnid:node_id) (blockid:node_id) : unit = - trans_frame_entry fnid; + trans_frame_entry fnid false; emit (Il.Enter (Hashtbl.find cx.ctxt_block_fixups blockid)); let (ilib, conv) = Hashtbl.find cx.ctxt_required_items fnid in let lib_num = @@ -5045,7 +5106,7 @@ let trans_visitor (tagid:node_id) (tag:(Ast.header_tup * Ast.ty_tag * node_id)) : unit = - trans_frame_entry tagid; + trans_frame_entry tagid false; trace_str cx.ctxt_sess.Session.sess_trace_tag ("in tag constructor " ^ n); let (header_tup, _, _) = tag in @@ -5108,7 +5169,7 @@ let trans_visitor iflog (fun _ -> log cx "translating defined item #%d = %s" (int_of_node i.id) (path_name())); match i.node.Ast.decl_item with - Ast.MOD_ITEM_fn f -> trans_fn i.id f.Ast.fn_body + Ast.MOD_ITEM_fn f -> trans_fn i.id f.Ast.fn_body false | Ast.MOD_ITEM_tag t -> trans_tag n i.id t | Ast.MOD_ITEM_obj ob -> trans_obj_ctor i.id @@ -5142,7 +5203,7 @@ let trans_visitor push_new_emitter_with_vregs (Some b.id); iflog (fun _ -> annotate "prologue"); abi.Abi.abi_emit_fn_prologue (emitter()) - framesz callsz nabi_rust (upcall_fixup "upcall_grow_task"); + framesz callsz nabi_rust (upcall_fixup "upcall_grow_task") true; write_frame_info_ptrs None; iflog (fun _ -> annotate "finished prologue"); trans_block b; @@ -5152,7 +5213,7 @@ let trans_visitor in let visit_defined_obj_fn_pre _ _ fn = - trans_fn fn.id fn.node.Ast.fn_body + trans_fn fn.id fn.node.Ast.fn_body true in let visit_required_obj_fn_pre _ _ _ = diff --git a/src/boot/me/type.ml b/src/boot/me/type.ml index 787855f0..b2d5a622 100644 --- a/src/boot/me/type.ml +++ b/src/boot/me/type.ml @@ -380,19 +380,20 @@ let check_stmt (cx:Semant.ctxt) : (fn_ctx -> Ast.stmt -> unit) = sprintf_itype () | `Type (Ast.TY_vec ty_vec), Ast.COMP_atom atom -> - demand Ast.TY_int (check_atom atom); + demand_integer (check_atom atom); LTYPE_mono ty_vec | `Type (Ast.TY_vec _), _ -> - Common.err None "the vector type '%a' must be indexed via an int" + Common.err None + "the vector type '%a' must be indexed by an integral type" sprintf_itype () | `Type Ast.TY_str, Ast.COMP_atom atom -> - demand Ast.TY_int (check_atom atom); + demand_integer (check_atom atom); LTYPE_mono (Ast.TY_mach Common.TY_u8) | `Type Ast.TY_str, _ -> - Common.err None "strings must be indexed via an int" + Common.err None "strings must be indexed by an integral type" | `Type (Ast.TY_box ty_box), Ast.COMP_deref -> LTYPE_mono ty_box @@ -691,7 +692,7 @@ let check_stmt (cx:Semant.ctxt) : (fn_ctx -> Ast.stmt -> unit) = and check_stmt (stmt:Ast.stmt) : unit = check_ret stmt; match stmt.Common.node with - Ast.STMT_spawn (dst, _, callee, args) -> + Ast.STMT_spawn (dst, _, _, callee, args) -> infer_lval Ast.TY_task dst; demand Ast.TY_nil (check_fn callee args) diff --git a/src/boot/me/typestate.ml b/src/boot/me/typestate.ml index baf4a543..20702990 100644 --- a/src/boot/me/typestate.ml +++ b/src/boot/me/typestate.ml @@ -664,7 +664,7 @@ let condition_assigning_visitor let precond = Array.append dst_init src_init in raise_pre_post_cond s.id precond; - | Ast.STMT_spawn (dst, _, lv, args) + | Ast.STMT_spawn (dst, _, _, lv, args) | Ast.STMT_call (dst, lv, args) -> raise_dst_init_precond_if_writing_through s.id dst; visit_callable_pre s.id (lval_slots cx dst) lv args @@ -1350,7 +1350,7 @@ let lifecycle_visitor match s.node with Ast.STMT_copy (lv_dst, _) | Ast.STMT_call (lv_dst, _, _) - | Ast.STMT_spawn (lv_dst, _, _, _) + | Ast.STMT_spawn (lv_dst, _, _, _, _) | Ast.STMT_recv (lv_dst, _) | Ast.STMT_bind (lv_dst, _, _) | Ast.STMT_new_rec (lv_dst, _, _) diff --git a/src/boot/me/walk.ml b/src/boot/me/walk.ml index 0e65406a..cadfd66b 100644 --- a/src/boot/me/walk.ml +++ b/src/boot/me/walk.ml @@ -451,7 +451,7 @@ and walk_stmt walk_lval v f; Array.iter (walk_opt_atom v) az - | Ast.STMT_spawn (dst,_,p,az) -> + | Ast.STMT_spawn (dst,_,_,p,az) -> walk_lval v dst; walk_lval v p; Array.iter (walk_atom v) az diff --git a/src/etc/x86.supp b/src/etc/x86.supp index f829f2ad..beb48223 100644 --- a/src/etc/x86.supp +++ b/src/etc/x86.supp @@ -11,4 +11,14 @@ Memcheck:Leak fun:calloc fun:_dl_allocate_tls -}
\ No newline at end of file +} + +{ + mac-dyld-oddity + Memcheck:Cond + fun:_ZN4dyld5_mainEPK12macho_headermiPPKcS5_S5_ + fun:_ZN13dyldbootstrap5startEPK12macho_headeriPPKcl + fun:_dyld_start + obj:* +} + diff --git a/src/lib/_int.rs b/src/lib/_int.rs index 9b756675..e76c2bf5 100644 --- a/src/lib/_int.rs +++ b/src/lib/_int.rs @@ -44,3 +44,47 @@ fn next_power_of_two(uint n) -> uint { } ret tmp + 1u; } + +fn uto_str(mutable uint n, uint radix) -> str +{ + check (0u < radix && radix <= 16u); + fn digit(uint n) -> str { + alt (n) { + case (0u) { ret "0"; } + case (1u) { ret "1"; } + case (2u) { ret "2"; } + case (3u) { ret "3"; } + case (4u) { ret "4"; } + case (5u) { ret "5"; } + case (6u) { ret "6"; } + case (7u) { ret "7"; } + case (8u) { ret "8"; } + case (9u) { ret "9"; } + case (10u) { ret "a"; } + case (11u) { ret "b"; } + case (12u) { ret "c"; } + case (13u) { ret "d"; } + case (14u) { ret "e"; } + case (15u) { ret "f"; } + } + } + + if (n == 0u) { ret "0"; } + + let str s = ""; + while (n > 0u) { + s = digit(n % radix) + s; + n /= radix; + } + ret s; +} + +fn to_str(mutable int n, uint radix) -> str +{ + check (0u < radix && radix <= 16u); + if (n < 0) { + ret "-" + uto_str((-n) as uint, radix); + } else { + ret uto_str(n as uint, radix); + } +} diff --git a/src/lib/_io.rs b/src/lib/_io.rs index 142f808a..93d06d41 100644 --- a/src/lib/_io.rs +++ b/src/lib/_io.rs @@ -1,3 +1,7 @@ +import std.os; +import std._str; +import std._vec; + type buf_reader = unsafe obj { fn read() -> vec[u8]; }; @@ -107,3 +111,22 @@ fn new_buf_writer(str path, vec[fileflag] flags) -> buf_writer { } ret fd_buf_writer(fd); } + +type writer = + unsafe obj { + fn write_str(str s); + fn write_int(int n); + fn write_uint(uint n); + }; + +fn file_writer(str path, + vec[fileflag] flags) + -> writer +{ + unsafe obj fw(buf_writer out) { + fn write_str(str s) { out.write(_str.bytes(s)); } + fn write_int(int n) { out.write(_str.bytes(_int.to_str(n, 10u))); } + fn write_uint(uint n) { out.write(_str.bytes(_int.uto_str(n, 10u))); } + } + ret fw(new_buf_writer(path, flags)); +} diff --git a/src/lib/_str.rs b/src/lib/_str.rs index 062d8bf1..a607c7d5 100644 --- a/src/lib/_str.rs +++ b/src/lib/_str.rs @@ -3,7 +3,7 @@ import rustrt.sbuf; native "rust" mod rustrt { type sbuf; fn str_buf(str s) -> sbuf; - fn str_len(str s) -> uint; + fn str_byte_len(str s) -> uint; fn str_alloc(uint n_bytes) -> str; fn refcount[T](str s) -> uint; } @@ -12,14 +12,37 @@ fn is_utf8(vec[u8] v) -> bool { fail; // FIXME } +fn is_ascii(str s) -> bool { + let uint i = byte_len(s); + while (i > 0u) { + i -= 1u; + if ((s.(i) & 0x80u8) != 0u8) { + ret false; + } + } + ret true; +} + fn alloc(uint n_bytes) -> str { ret rustrt.str_alloc(n_bytes); } -fn len(str s) -> uint { - ret rustrt.str_len(s); +// Returns the number of bytes (a.k.a. UTF-8 code units) in s. +// Contrast with a function that would return the number of code +// points (char's), combining character sequences, words, etc. See +// http://icu-project.org/apiref/icu4c/classBreakIterator.html for a +// way to implement those. +fn byte_len(str s) -> uint { + ret rustrt.str_byte_len(s); } fn buf(str s) -> sbuf { ret rustrt.str_buf(s); } + +fn bytes(&str s) -> vec[u8] { + fn ith(str s, uint i) -> u8 { + ret s.(i); + } + ret _vec.init_fn[u8](bind ith(s, _), _str.byte_len(s)); +} diff --git a/src/lib/deque.rs b/src/lib/deque.rs index bd42d7cb..3f2a81a4 100644 --- a/src/lib/deque.rs +++ b/src/lib/deque.rs @@ -36,7 +36,7 @@ fn create[T]() -> t[T] { fn fill[T](uint i, uint nelts, uint lo, &vec[cell[T]] old) -> cell[T] { if (i < nelts) { - ret old.(((lo + i) % nelts) as int); + ret old.((lo + i) % nelts); } else { ret util.none[T](); } @@ -47,14 +47,8 @@ fn create[T]() -> t[T] { ret _vec.init_fn[cell[T]](copy_op, nalloc); } - /** - * FIXME (issue #94): We're converting to int every time we index into the - * vec, but we really want to index with the lo and hi uints that we have - * around. - */ - - fn get[T](&vec[cell[T]] elts, uint i) -> T { - alt (elts.(i as int)) { + fn get[T](vec[cell[T]] elts, uint i) -> T { + alt (elts.(i)) { case (util.some[T](t)) { ret t; } case (_) { fail; } } @@ -82,7 +76,7 @@ fn create[T]() -> t[T] { hi = nelts; } - elts.(lo as int) = util.some[T](t); + elts.(lo) = util.some[T](t); nelts += 1u; } @@ -93,7 +87,7 @@ fn create[T]() -> t[T] { hi = nelts; } - elts.(hi as int) = util.some[T](t); + elts.(hi) = util.some[T](t); hi = (hi + 1u) % _vec.len[cell[T]](elts); nelts += 1u; } @@ -104,8 +98,9 @@ fn create[T]() -> t[T] { */ fn pop_front() -> T { let T t = get[T](elts, lo); - elts.(lo as int) = util.none[T](); + elts.(lo) = util.none[T](); lo = (lo + 1u) % _vec.len[cell[T]](elts); + nelts -= 1u; ret t; } @@ -117,7 +112,8 @@ fn create[T]() -> t[T] { } let T t = get[T](elts, hi); - elts.(hi as int) = util.none[T](); + elts.(hi) = util.none[T](); + nelts -= 1u; ret t; } diff --git a/src/lib/map.rs b/src/lib/map.rs index f9574176..ff7b4411 100644 --- a/src/lib/map.rs +++ b/src/lib/map.rs @@ -75,8 +75,7 @@ fn mk_hashmap[K, V](&hashfn[K] hasher, &eqfn[K] eqer) -> hashmap[K, V] { { let uint i = 0u; while (i < nbkts) { - // FIXME (issue #94): as in find_common() - let int j = (hash[K](hasher, nbkts, key, i)) as int; + let uint j = (hash[K](hasher, nbkts, key, i)); alt (bkts.(j)) { case (some[K, V](k, _)) { if (eqer(key, k)) { @@ -103,8 +102,7 @@ fn mk_hashmap[K, V](&hashfn[K] hasher, &eqfn[K] eqer) -> hashmap[K, V] { { let uint i = 0u; while (i < nbkts) { - // FIXME (issue #94): Pending bugfix, remove uint coercion. - let int j = (hash[K](hasher, nbkts, key, i)) as int; + let uint j = (hash[K](hasher, nbkts, key, i)); alt (bkts.(j)) { case (some[K, V](k, v)) { if (eqer(key, k)) { @@ -149,9 +147,6 @@ fn mk_hashmap[K, V](&hashfn[K] hasher, &eqfn[K] eqer) -> hashmap[K, V] { if (!util.rational_leq(load, lf)) { let uint nnewbkts = _int.next_power_of_two(nbkts + 1u); - // FIXME (issue #94): Enforce our workaround to issue #94. - check ((nnewbkts as int) > 0); - let vec[mutable bucket[K, V]] newbkts = make_buckets[K, V](nnewbkts); rehash[K, V](hasher, eqer, bkts, nbkts, newbkts, nnewbkts); } @@ -183,8 +178,7 @@ fn mk_hashmap[K, V](&hashfn[K] hasher, &eqfn[K] eqer) -> hashmap[K, V] { fn remove(&K key) -> util.option[V] { let uint i = 0u; while (i < nbkts) { - // FIXME (issue #94): as in find_common() - let int j = (hash[K](hasher, nbkts, key, i)) as int; + let uint j = (hash[K](hasher, nbkts, key, i)); alt (bkts.(j)) { case (some[K, V](_, val)) { bkts.(j) = deleted[K, V](); diff --git a/src/rt/circular_buffer.cpp b/src/rt/circular_buffer.cpp index a93f2572..dbf5059b 100644 --- a/src/rt/circular_buffer.cpp +++ b/src/rt/circular_buffer.cpp @@ -31,9 +31,7 @@ circular_buffer::circular_buffer(rust_dom *dom, size_t unit_sz) : } circular_buffer::~circular_buffer() { - dom->log(rust_log::MEM | rust_log::COMM, - "~circular_buffer 0x%" PRIxPTR, - this); + dom->log(rust_log::MEM, "~circular_buffer 0x%" PRIxPTR, this); I(dom, _buffer); W(dom, _unread == 0, "~circular_buffer with unread messages."); dom->free(_buffer); diff --git a/src/rt/globals.h b/src/rt/globals.h index f8025a40..85aa5860 100644 --- a/src/rt/globals.h +++ b/src/rt/globals.h @@ -20,12 +20,14 @@ extern "C" { } #elif defined(__GNUC__) #include <unistd.h> +#include <sys/time.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <dlfcn.h> #include <pthread.h> #include <errno.h> +#include <time.h> #else #error "Platform not supported." #endif diff --git a/src/rt/rust.cpp b/src/rt/rust.cpp index 9cc2fe41..d0215edc 100644 --- a/src/rt/rust.cpp +++ b/src/rt/rust.cpp @@ -53,7 +53,9 @@ rust_srv::realloc(void *p, size_t bytes) } void * val = ::realloc(p, bytes); #ifdef TRACK_ALLOCATIONS - if (allocation_list.replace(p, val) == NULL) { + if (allocation_list.replace(p, val) == false) { + printf("realloc: ptr 0x%" PRIxPTR " is not in allocation_list\n", + (uintptr_t) p); fatal("not in allocation_list", __FILE__, __LINE__); } #endif @@ -64,8 +66,8 @@ void rust_srv::free(void *p) { #ifdef TRACK_ALLOCATIONS - if (allocation_list.replace(p, NULL) == NULL) { - printf("ptr 0x%" PRIxPTR " is not in allocation_list\n", + if (allocation_list.replace(p, NULL) == false) { + printf("free: ptr 0x%" PRIxPTR " is not in allocation_list\n", (uintptr_t) p); fatal("not in allocation_list", __FILE__, __LINE__); } @@ -182,7 +184,7 @@ rust_start(uintptr_t main_fn, rust_crate const *crate, int argc, char **argv) int ret; { rust_srv srv; - rust_dom dom(&srv, crate); + rust_dom dom(&srv, crate, "main"); command_line_args args(dom, argc, argv); dom.log(rust_log::DOM, "startup: %d args", args.argc); diff --git a/src/rt/rust_builtin.cpp b/src/rt/rust_builtin.cpp index 657109c6..d8d9b8d6 100644 --- a/src/rt/rust_builtin.cpp +++ b/src/rt/rust_builtin.cpp @@ -115,6 +115,12 @@ str_buf(rust_task *task, rust_str *s) return (char const *)&s->data[0]; } +extern "C" CDECL size_t +str_byte_len(rust_task *task, rust_str *s) +{ + return s->fill - 1; // -1 for the '\0' terminator. +} + extern "C" CDECL void * vec_buf(rust_task *task, type_desc *ty, rust_vec *v, size_t offset) { diff --git a/src/rt/rust_chan.cpp b/src/rt/rust_chan.cpp index f107d287..2a0a61db 100644 --- a/src/rt/rust_chan.cpp +++ b/src/rt/rust_chan.cpp @@ -18,9 +18,11 @@ rust_chan::rust_chan(rust_task *task, maybe_proxy<rust_port> *port) : } rust_chan::~rust_chan() { - if (port && !port->is_proxy()) { - port->delegate()->chans.swap_delete(this); - } + task->log(rust_log::MEM | rust_log::COMM, + "del rust_chan(task=0x%" PRIxPTR ")", (uintptr_t) this); + + A(task->dom, is_associated() == false, + "Channel must be disassociated before being freed."); } /** @@ -28,7 +30,10 @@ rust_chan::~rust_chan() { */ void rust_chan::associate(maybe_proxy<rust_port> *port) { this->port = port; - if (!port->is_proxy()) { + if (port->is_proxy() == false) { + task->log(rust_log::TASK, + "associating chan: 0x%" PRIxPTR " with port: 0x%" PRIxPTR, + this, port); this->port->delegate()->chans.push(this); } } @@ -43,14 +48,23 @@ bool rust_chan::is_associated() { void rust_chan::disassociate() { A(task->dom, is_associated(), "Channel must be associated with a port."); + if (port->is_proxy() == false) { + task->log(rust_log::TASK, + "disassociating chan: 0x%" PRIxPTR " from port: 0x%" PRIxPTR, + this, port->delegate()); + port->delegate()->chans.swap_delete(this); + } + // Delete reference to the port. port = NULL; } /** - * Attempt to transmit channel data to the associated port. + * Attempt to send data to the associated port. */ -void rust_chan::transmit() { +void rust_chan::send(void *sptr) { + buffer.enqueue(sptr); + rust_dom *dom = task->dom; if (!is_associated()) { W(dom, is_associated(), @@ -81,7 +95,6 @@ void rust_chan::transmit() { return; } - // // Local Variables: // mode: C++ diff --git a/src/rt/rust_chan.h b/src/rt/rust_chan.h index 055e359a..6aa98247 100644 --- a/src/rt/rust_chan.h +++ b/src/rt/rust_chan.h @@ -17,7 +17,7 @@ public: void disassociate(); bool is_associated(); - void transmit(); + void send(void *sptr); }; // diff --git a/src/rt/rust_dom.cpp b/src/rt/rust_dom.cpp index 99aaddb2..f7b8e97b 100644 --- a/src/rt/rust_dom.cpp +++ b/src/rt/rust_dom.cpp @@ -4,12 +4,16 @@ template class ptr_vec<rust_task>; +// Keeps track of all live domains, for debugging purposes. +array_list<rust_dom*> _live_domains; -rust_dom::rust_dom(rust_srv *srv, rust_crate const *root_crate) : +rust_dom::rust_dom(rust_srv *srv, rust_crate const *root_crate, + const char *name) : interrupt_flag(0), root_crate(root_crate), _log(srv, this), srv(srv), + name(name), running_tasks(this), blocked_tasks(this), dead_tasks(this), @@ -25,14 +29,19 @@ rust_dom::rust_dom(rust_srv *srv, rust_crate const *root_crate) : pthread_attr_setstacksize(&attr, 1024 * 1024); pthread_attr_setdetachstate(&attr, true); #endif - root_task = new (this) rust_task(this, NULL); + root_task = new (this) rust_task(this, NULL, name); + + if (_live_domains.replace(NULL, this) == false) { + _live_domains.append(this); + } } static void del_all_tasks(rust_dom *dom, ptr_vec<rust_task> *v) { I(dom, v); while (v->length()) { - dom->log(rust_log::TASK, "deleting task %" PRIdPTR, v->length() - 1); + dom->log(rust_log::TASK, "deleting task 0x%" PRIdPTR, + v->length() - 1); delete v->pop(); } } @@ -42,23 +51,25 @@ rust_dom::delete_proxies() { rust_task *task; rust_proxy<rust_task> *task_proxy; while (_task_proxies.pop(&task, &task_proxy)) { - log(rust_log::TASK, "deleting proxy %" PRIxPTR - " in dom %" PRIxPTR, task_proxy, task_proxy->dom); + log(rust_log::TASK, + "deleting proxy 0x%" PRIxPTR " in dom %s 0x%" PRIxPTR, + task_proxy, task_proxy->dom->name, task_proxy->dom); delete task_proxy; } rust_port *port; rust_proxy<rust_port> *port_proxy; while (_port_proxies.pop(&port, &port_proxy)) { - log(rust_log::TASK, "deleting proxy %" PRIxPTR - " in dom %" PRIxPTR, port_proxy, port_proxy->dom); + log(rust_log::TASK, + "deleting proxy 0x%" PRIxPTR " in dom %s 0x%" PRIxPTR, + port_proxy, port_proxy->dom->name, port_proxy->dom); delete port_proxy; } } rust_dom::~rust_dom() { log(rust_log::MEM | rust_log::DOM, - "~rust_dom 0x%" PRIxPTR, (uintptr_t)this); + "~rust_dom %s @0x%" PRIxPTR, name, (uintptr_t)this); log(rust_log::TASK, "deleting all proxies"); delete_proxies(); @@ -73,6 +84,8 @@ rust_dom::~rust_dom() { #endif while (caches.length()) delete caches.pop(); + + _live_domains.replace(this, NULL); } void @@ -124,7 +137,8 @@ rust_dom::logptr(char const *msg, T* ptrval) { void rust_dom::fail() { - log(rust_log::DOM, "domain 0x%" PRIxPTR " root task failed", this); + log(rust_log::DOM, "domain %s @0x%" PRIxPTR " root task failed", + name, this); I(this, rval == 0); rval = 1; } @@ -133,8 +147,9 @@ void * rust_dom::malloc(size_t sz) { void *p = srv->malloc(sz); I(this, p); - log(rust_log::MEM, "0x%" PRIxPTR " rust_dom::malloc(%d) -> 0x%" PRIxPTR, - (uintptr_t) this, sz, p); + log(rust_log::MEM, + "%s @0x%" PRIxPTR " rust_dom::malloc(%d) -> 0x%" PRIxPTR, + name, (uintptr_t) this, sz, p); return p; } @@ -190,8 +205,8 @@ void rust_dom::add_task_to_state_vec(ptr_vec<rust_task> *v, rust_task *task) { log(rust_log::MEM|rust_log::TASK, - "adding task 0x%" PRIxPTR " in state '%s' to vec 0x%" PRIxPTR, - (uintptr_t)task, state_vec_name(v), (uintptr_t)v); + "adding task %s @0x%" PRIxPTR " in state '%s' to vec 0x%" PRIxPTR, + task->name, (uintptr_t)task, state_vec_name(v), (uintptr_t)v); v->push(task); } @@ -200,8 +215,8 @@ void rust_dom::remove_task_from_state_vec(ptr_vec<rust_task> *v, rust_task *task) { log(rust_log::MEM|rust_log::TASK, - "removing task 0x%" PRIxPTR " in state '%s' from vec 0x%" PRIxPTR, - (uintptr_t)task, state_vec_name(v), (uintptr_t)v); + "removing task %s @0x%" PRIxPTR " in state '%s' from vec 0x%" PRIxPTR, + task->name, (uintptr_t)task, state_vec_name(v), (uintptr_t)v); I(this, (*v)[task->idx] == task); v->swap_delete(task); } @@ -226,10 +241,10 @@ rust_dom::reap_dead_tasks() { rust_task *task = dead_tasks[i]; if (task->ref_count == 0) { I(this, task->tasks_waiting_to_join.is_empty()); - dead_tasks.swap_delete(task); log(rust_log::TASK, - "deleting unreferenced dead task 0x%" PRIxPTR, task); + "deleting unreferenced dead task %s @0x%" PRIxPTR, + task->name, task); delete task; continue; } @@ -243,14 +258,14 @@ rust_dom::reap_dead_tasks() { */ void rust_dom::send_message(rust_message *message) { log(rust_log::COMM, "==> enqueueing \"%s\" 0x%" PRIxPTR - " in queue 0x%" PRIxPTR, + " in queue 0x%" PRIxPTR + " in domain 0x%" PRIxPTR, message->label, message, - &_incoming_message_queue); + &_incoming_message_queue, + this); A(this, message->dom == this, "Message owned by non-local domain."); _incoming_message_queue.enqueue(message); - _incoming_message_pending.signal(); - _progress.signal(); } /** @@ -272,7 +287,7 @@ rust_dom::get_task_proxy(rust_task *task) { if (_task_proxies.get(task, &proxy)) { return proxy; } - log(rust_log::COMM, "no proxy for 0x%" PRIxPTR, task); + log(rust_log::COMM, "no proxy for %s @0x%" PRIxPTR, task->name, task); proxy = new (this) rust_proxy<rust_task> (this, task, false); _task_proxies.put(task, proxy); return proxy; @@ -322,12 +337,20 @@ rust_dom::schedule_task() } void +rust_dom::log_all_state() { + for (uint32_t i = 0; i < _live_domains.size(); i++) { + _live_domains[i]->log_state(); + } +} + +void rust_dom::log_state() { if (!running_tasks.is_empty()) { log(rust_log::TASK, "running tasks:"); for (size_t i = 0; i < running_tasks.length(); i++) { log(rust_log::TASK, - "\t task: 0x%" PRIxPTR, running_tasks[i]); + "\t task: %s @0x%" PRIxPTR, + running_tasks[i]->name, running_tasks[i]); } } @@ -335,15 +358,18 @@ rust_dom::log_state() { log(rust_log::TASK, "blocked tasks:"); for (size_t i = 0; i < blocked_tasks.length(); i++) { log(rust_log::TASK, - "\t task: 0x%" PRIxPTR ", blocked on: 0x%" PRIxPTR, - blocked_tasks[i], blocked_tasks[i]->cond); + "\t task: %s @0x%" PRIxPTR ", blocked on: 0x%" PRIxPTR, + blocked_tasks[i]->name, blocked_tasks[i], + blocked_tasks[i]->cond); } } if (!dead_tasks.is_empty()) { log(rust_log::TASK, "dead tasks:"); for (size_t i = 0; i < dead_tasks.length(); i++) { - log(rust_log::TASK, "\t task: 0x%" PRIxPTR, dead_tasks[i]); + log(rust_log::TASK, "\t task: %s 0x%" PRIxPTR ", ref_count: %d", + dead_tasks[i], dead_tasks[i]->name, + dead_tasks[i]->ref_count); } } } @@ -360,7 +386,8 @@ rust_dom::start_main_loop() // Make sure someone is watching, to pull us out of infinite loops. rust_timer timer(this); - log(rust_log::DOM, "running main-loop on domain 0x%" PRIxPTR, this); + log(rust_log::DOM, "running main-loop on domain %s @0x%" PRIxPTR, + name, this); logptr("exit-task glue", root_crate->get_exit_task_glue()); while (n_live_tasks() > 0) { @@ -373,29 +400,38 @@ rust_dom::start_main_loop() // if progress is made in other domains. if (scheduled_task == NULL) { - log(rust_log::TASK, - "all tasks are blocked, waiting for progress ..."); - if (_log.is_tracing(rust_log::TASK)) + if (_log.is_tracing(rust_log::TASK)) { log_state(); - _progress.wait(); + } log(rust_log::TASK, - "progress made, resuming ..."); + "all tasks are blocked, scheduler yielding ..."); + sync::yield(); + log(rust_log::TASK, + "scheduler resuming ..."); continue; } I(this, scheduled_task->running()); log(rust_log::TASK, - "activating task 0x%" PRIxPTR ", sp=0x%" PRIxPTR, - (uintptr_t)scheduled_task, scheduled_task->rust_sp); + "activating task %s 0x%" PRIxPTR + ", sp=0x%" PRIxPTR + ", ref_count=%d" + ", state: %s", + scheduled_task->name, + (uintptr_t)scheduled_task, + scheduled_task->rust_sp, + scheduled_task->ref_count, + scheduled_task->state_str()); interrupt_flag = 0; activate(scheduled_task); log(rust_log::TASK, - "returned from task 0x%" PRIxPTR + "returned from task %s @0x%" PRIxPTR " in state '%s', sp=0x%" PRIxPTR, + scheduled_task->name, (uintptr_t)scheduled_task, state_vec_name(scheduled_task->state), scheduled_task->rust_sp); @@ -410,20 +446,15 @@ rust_dom::start_main_loop() log(rust_log::DOM, "terminated scheduler loop, reaping dead tasks ..."); while (dead_tasks.length() > 0) { - log(rust_log::DOM, - "waiting for %d dead tasks to become dereferenced ...", - dead_tasks.length()); - - if (_log.is_tracing(rust_log::DOM)) { - for (size_t i = 0; i < dead_tasks.length(); i++) { - log(rust_log::DOM, - "task: 0x%" PRIxPTR ", index: %d, ref_count: %d", - dead_tasks[i], i, dead_tasks[i]->ref_count); - } - } - if (_incoming_message_queue.is_empty()) { - _incoming_message_pending.wait(); + log(rust_log::DOM, + "waiting for %d dead tasks to become dereferenced, " + "scheduler yielding ...", + dead_tasks.length()); + if (_log.is_tracing(rust_log::TASK)) { + log_state(); + } + sync::yield(); } else { drain_incoming_message_queue(); } diff --git a/src/rt/rust_dom.h b/src/rt/rust_dom.h index 528790d5..3aaf635a 100644 --- a/src/rt/rust_dom.h +++ b/src/rt/rust_dom.h @@ -25,6 +25,7 @@ struct rust_dom rust_crate const *root_crate; rust_log _log; rust_srv *srv; + const char *const name; ptr_vec<rust_task> running_tasks; ptr_vec<rust_task> blocked_tasks; ptr_vec<rust_task> dead_tasks; @@ -34,20 +35,19 @@ struct rust_dom rust_task *curr_task; int rval; - condition_variable _progress; - hash_map<rust_task *, rust_proxy<rust_task> *> _task_proxies; hash_map<rust_port *, rust_proxy<rust_port> *> _port_proxies; // Incoming messages from other domains. - condition_variable _incoming_message_pending; lock_free_queue _incoming_message_queue; #ifndef __WIN32__ pthread_attr_t attr; #endif - rust_dom(rust_srv *srv, rust_crate const *root_crate); + // Only a pointer to 'name' is kept, so it must live as long as this + // domain. + rust_dom(rust_srv *srv, rust_crate const *root_crate, const char *name); ~rust_dom(); void activate(rust_task *task); @@ -84,6 +84,7 @@ struct rust_dom int start_main_loop(); void log_state(); + static void log_all_state(); }; // diff --git a/src/rt/rust_internal.h b/src/rt/rust_internal.h index a89144d7..46ea843f 100644 --- a/src/rt/rust_internal.h +++ b/src/rt/rust_internal.h @@ -38,6 +38,7 @@ extern "C" { #error "Platform not supported." #endif +#include "sync/sync.h" #include "sync/condition_variable.h" #ifndef __i386__ @@ -75,7 +76,7 @@ template <typename T> struct rc_base { - size_t ref_count; + int32_t ref_count; void ref() { ++ref_count; @@ -151,6 +152,7 @@ public: T *& operator[](size_t offset); void push(T *p); T *pop(); + T *peek(); void trim(size_t fill); void swap_delete(T* p); }; diff --git a/src/rt/rust_log.cpp b/src/rt/rust_log.cpp index 48fcd0f1..8e089791 100644 --- a/src/rt/rust_log.cpp +++ b/src/rt/rust_log.cpp @@ -27,6 +27,7 @@ read_type_bit_mask() { bits |= strstr(env_str, "timer") ? rust_log::TIMER : 0; bits |= strstr(env_str, "gc") ? rust_log::GC : 0; bits |= strstr(env_str, "all") ? rust_log::ALL : 0; + bits = strstr(env_str, "none") ? 0 : bits; } return bits; } diff --git a/src/rt/rust_message.cpp b/src/rt/rust_message.cpp index 8b396b4d..1de804c9 100644 --- a/src/rt/rust_message.cpp +++ b/src/rt/rust_message.cpp @@ -90,8 +90,7 @@ send(uint8_t *buffer, size_t buffer_sz, const char* label, rust_task *source, } void data_message::process() { - _port->remote_channel->buffer.enqueue(_buffer); - _port->remote_channel->transmit(); + _port->remote_channel->send(_buffer); _target->log(rust_log::COMM, "<=== received data via message ==="); } diff --git a/src/rt/rust_port.cpp b/src/rt/rust_port.cpp index 0a5b7ee7..c97b5d41 100644 --- a/src/rt/rust_port.cpp +++ b/src/rt/rust_port.cpp @@ -17,16 +17,50 @@ rust_port::~rust_port() { task->log(rust_log::COMM | rust_log::MEM, "~rust_port 0x%" PRIxPTR, (uintptr_t) this); + log_state(); + // Disassociate channels from this port. while (chans.is_empty() == false) { - chans.pop()->disassociate(); + rust_chan *chan = chans.peek(); + chan->disassociate(); } - // We're the only ones holding a reference to the remote channel, so - // clean it up. delete remote_channel; } +bool rust_port::receive(void *dptr) { + for (uint32_t i = 0; i < chans.length(); i++) { + rust_chan *chan = chans[i]; + if (chan->buffer.is_empty() == false) { + chan->buffer.dequeue(dptr); + if (chan->buffer.is_empty() && chan->task->blocked()) { + task->log(rust_log::COMM, + "chan: 0x%" PRIxPTR + " is flushing, wakeup task: 0x%" PRIxPTR, + chan, chan->task); + chan->task->wakeup(this); + } + task->log(rust_log::COMM, "<=== read data ==="); + return true; + } + } + return false; +} + +void rust_port::log_state() { + task->log(rust_log::COMM, + "rust_port: 0x%" PRIxPTR ", associated channel(s): %d", + this, chans.length()); + for (uint32_t i = 0; i < chans.length(); i++) { + rust_chan *chan = chans[i]; + task->log(rust_log::COMM, + "\tchan: 0x%" PRIxPTR ", data pending: %s, remote: %s", + chan, + !chan->buffer.is_empty() ? "yes" : "no", + chan == remote_channel ? "yes" : "no"); + } +} + // // Local Variables: // mode: C++ diff --git a/src/rt/rust_port.h b/src/rt/rust_port.h index 49a89437..7a58f839 100644 --- a/src/rt/rust_port.h +++ b/src/rt/rust_port.h @@ -15,6 +15,8 @@ public: rust_port(rust_task *task, size_t unit_sz); ~rust_port(); + void log_state(); + bool receive(void *dptr); }; // diff --git a/src/rt/rust_task.cpp b/src/rt/rust_task.cpp index 9f4fa611..aca8bca7 100644 --- a/src/rt/rust_task.cpp +++ b/src/rt/rust_task.cpp @@ -52,7 +52,7 @@ align_down(uintptr_t sp) } -rust_task::rust_task(rust_dom *dom, rust_task *spawner) : +rust_task::rust_task(rust_dom *dom, rust_task *spawner, const char *name) : maybe_proxy<rust_task>(this), stk(new_stk(dom, 0)), runtime_sp(0), @@ -60,6 +60,7 @@ rust_task::rust_task(rust_dom *dom, rust_task *spawner) : gc_alloc_chain(0), dom(dom), cache(NULL), + name(name), state(&dom->running_tasks), cond(NULL), supervisor(spawner), @@ -77,8 +78,8 @@ rust_task::rust_task(rust_dom *dom, rust_task *spawner) : rust_task::~rust_task() { dom->log(rust_log::MEM|rust_log::TASK, - "~rust_task 0x%" PRIxPTR ", refcnt=%d", - (uintptr_t)this, ref_count); + "~rust_task %s @0x%" PRIxPTR ", refcnt=%d", + name, (uintptr_t)this, ref_count); /* for (uintptr_t fp = get_fp(); fp; fp = get_previous_fp(fp)) { @@ -311,7 +312,7 @@ void rust_task::yield(size_t nargs) { log(rust_log::TASK, - "task 0x%" PRIxPTR " yielding", this); + "task %s @0x%" PRIxPTR " yielding", name, this); run_after_return(nargs, dom->root_crate->get_yield_glue()); } @@ -323,23 +324,29 @@ get_callee_save_fp(uintptr_t *top_of_callee_saves) void rust_task::kill() { + if (dead()) { + // Task is already dead, can't kill what's already dead. + return; + } + // Note the distinction here: kill() is when you're in an upcall // from task A and want to force-fail task B, you do B->kill(). // If you want to fail yourself you do self->fail(upcall_nargs). - log(rust_log::TASK, "killing task 0x%" PRIxPTR, this); + log(rust_log::TASK, "killing task %s @0x%" PRIxPTR, name, this); // Unblock the task so it can unwind. unblock(); if (this == dom->root_task) dom->fail(); + log(rust_log::TASK, "preparing to unwind task: 0x%" PRIxPTR, this); run_on_resume(dom->root_crate->get_unwind_glue()); } void rust_task::fail(size_t nargs) { // See note in ::kill() regarding who should call this. - dom->log(rust_log::TASK, "task 0x%" PRIxPTR " failing", this); + dom->log(rust_log::TASK, "task %s @0x%" PRIxPTR " failing", name, this); // Unblock the task so it can unwind. unblock(); if (this == dom->root_task) @@ -347,9 +354,9 @@ rust_task::fail(size_t nargs) { run_after_return(nargs, dom->root_crate->get_unwind_glue()); if (supervisor) { dom->log(rust_log::TASK, - "task 0x%" PRIxPTR - " propagating failure to supervisor 0x%" PRIxPTR, - this, supervisor); + "task %s @0x%" PRIxPTR + " propagating failure to supervisor %s @0x%" PRIxPTR, + name, this, supervisor->name, supervisor); supervisor->kill(); } } @@ -358,7 +365,7 @@ void rust_task::gc(size_t nargs) { dom->log(rust_log::TASK|rust_log::MEM, - "task 0x%" PRIxPTR " garbage collecting", this); + "task %s @0x%" PRIxPTR " garbage collecting", name, this); run_after_return(nargs, dom->root_crate->get_gc_glue()); } @@ -366,8 +373,9 @@ void rust_task::unsupervise() { dom->log(rust_log::TASK, - "task 0x%" PRIxPTR " disconnecting from supervisor 0x%" PRIxPTR, - this, supervisor); + "task %s @0x%" PRIxPTR + " disconnecting from supervisor %s @0x%" PRIxPTR, + name, this, supervisor->name, supervisor); supervisor = NULL; } @@ -468,8 +476,9 @@ rust_task::malloc(size_t sz, type_desc *td) if (td) { gc_alloc *gcm = (gc_alloc*) mem; dom->log(rust_log::TASK|rust_log::MEM|rust_log::GC, - "task 0x%" PRIxPTR " allocated %d GC bytes = 0x%" PRIxPTR, - (uintptr_t)this, sz, gcm); + "task %s @0x%" PRIxPTR + " allocated %d GC bytes = 0x%" PRIxPTR, + name, (uintptr_t)this, sz, gcm); memset((void*) gcm, 0, sizeof(gc_alloc)); link_gc(gcm); gcm->ctrl_word = (uintptr_t)td; @@ -488,8 +497,9 @@ rust_task::realloc(void *data, size_t sz, bool is_gc) sz += sizeof(gc_alloc); gcm = (gc_alloc*) dom->realloc((void*)gcm, sz); dom->log(rust_log::TASK|rust_log::MEM|rust_log::GC, - "task 0x%" PRIxPTR " reallocated %d GC bytes = 0x%" PRIxPTR, - (uintptr_t)this, sz, gcm); + "task %s @0x%" PRIxPTR + " reallocated %d GC bytes = 0x%" PRIxPTR, + name, (uintptr_t)this, sz, gcm); if (!gcm) return gcm; link_gc(gcm); @@ -507,21 +517,26 @@ rust_task::free(void *p, bool is_gc) gc_alloc *gcm = (gc_alloc*)(((char *)p) - sizeof(gc_alloc)); unlink_gc(gcm); dom->log(rust_log::TASK|rust_log::MEM|rust_log::GC, - "task 0x%" PRIxPTR " freeing GC memory = 0x%" PRIxPTR, - (uintptr_t)this, gcm); + "task %s @0x%" PRIxPTR " freeing GC memory = 0x%" PRIxPTR, + name, (uintptr_t)this, gcm); dom->free(gcm); } else { dom->free(p); } } +const char * +rust_task::state_str() { + return dom->state_vec_name(state); +} void rust_task::transition(ptr_vec<rust_task> *src, ptr_vec<rust_task> *dst) { I(dom, state == src); dom->log(rust_log::TASK, - "task 0x%" PRIxPTR " state change '%s' -> '%s'", + "task %s @0x%" PRIxPTR " state change '%s' -> '%s'", + name, (uintptr_t)this, dom->state_vec_name(src), dom->state_vec_name(dst)); @@ -551,9 +566,6 @@ rust_task::wakeup(rust_cond *from) A(dom, cond == from, "Cannot wake up blocked task on wrong condition."); transition(&dom->blocked_tasks, &dom->running_tasks); - // TODO: Signaling every time the task is awaken is kind of silly, - // do this a nicer way. - dom->_progress.signal(); I(dom, cond == from); cond = NULL; } diff --git a/src/rt/rust_task.h b/src/rt/rust_task.h index b657592a..27495e2c 100644 --- a/src/rt/rust_task.h +++ b/src/rt/rust_task.h @@ -21,6 +21,7 @@ rust_task : public maybe_proxy<rust_task>, rust_crate_cache *cache; // Fields known only to the runtime. + const char *const name; ptr_vec<rust_task> *state; rust_cond *cond; rust_task *supervisor; // Parent-link for failure propagation. @@ -41,8 +42,10 @@ rust_task : public maybe_proxy<rust_task>, rust_alarm alarm; + // Only a pointer to 'name' is kept, so it must live as long as this task. rust_task(rust_dom *dom, - rust_task *spawner); + rust_task *spawner, + const char *name); ~rust_task(); void start(uintptr_t exit_task_glue, diff --git a/src/rt/rust_upcall.cpp b/src/rt/rust_upcall.cpp index 574cb703..90d6f6d9 100644 --- a/src/rt/rust_upcall.cpp +++ b/src/rt/rust_upcall.cpp @@ -6,19 +6,37 @@ #define LOG_UPCALL_ENTRY(task) \ (task)->dom->get_log().reset_indent(0); \ (task)->log(rust_log::UPCALL, \ - "> UPCALL %s - task: 0x%" PRIxPTR \ - " retpc: x%" PRIxPTR, \ + "> UPCALL %s - task: %s 0x%" PRIxPTR \ + " retpc: x%" PRIxPTR \ + " ref_count: %d", \ __FUNCTION__, \ - (task), __builtin_return_address(0)); \ + (task)->name, (task), \ + __builtin_return_address(0), \ + (task->ref_count)); \ (task)->dom->get_log().indent(); #else #define LOG_UPCALL_ENTRY(task) \ (task)->dom->get_log().reset_indent(0); \ (task)->log(rust_log::UPCALL, \ - "> UPCALL task: x%" PRIxPTR (task)); \ + "> UPCALL task: %s @x%" PRIxPTR, \ + (task)->name, (task)); \ (task)->dom->get_log().indent(); #endif +void +log_task_state(rust_task *task, maybe_proxy<rust_task> *target) { + rust_task *delegate = target->delegate(); + if (target->is_proxy()) { + task->log(rust_log::TASK, + "remote task: 0x%" PRIxPTR ", ref_count: %d state: %s", + delegate, delegate->ref_count, delegate->state_str()); + } else { + task->log(rust_log::TASK, + "local task: 0x%" PRIxPTR ", ref_count: %d state: %s", + delegate, delegate->ref_count, delegate->state_str()); + } +} + extern "C" CDECL char const *str_buf(rust_task *task, rust_str *s); extern "C" void upcall_grow_task(rust_task *task, size_t n_frame_bytes) { @@ -29,14 +47,13 @@ extern "C" void upcall_grow_task(rust_task *task, size_t n_frame_bytes) { extern "C" CDECL void upcall_log_int(rust_task *task, int32_t i) { LOG_UPCALL_ENTRY(task); task->log(rust_log::UPCALL | rust_log::ULOG, - "upcall log_int(0x%" PRIx32 " = %" PRId32 " = '%c')", i, i, - (char) i); + "rust: %" PRId32 " (0x%" PRIx32 ")", i, i); } extern "C" CDECL void upcall_log_str(rust_task *task, rust_str *str) { LOG_UPCALL_ENTRY(task); const char *c = str_buf(task, str); - task->log(rust_log::UPCALL | rust_log::ULOG, "upcall log_str(\"%s\")", c); + task->log(rust_log::UPCALL | rust_log::ULOG, "rust: %s", c); } extern "C" CDECL void upcall_trace_word(rust_task *task, uintptr_t i) { @@ -55,8 +72,8 @@ upcall_new_port(rust_task *task, size_t unit_sz) { LOG_UPCALL_ENTRY(task); rust_dom *dom = task->dom; task->log(rust_log::UPCALL | rust_log::MEM | rust_log::COMM, - "upcall_new_port(task=0x%" PRIxPTR ", unit_sz=%d)", - (uintptr_t) task, unit_sz); + "upcall_new_port(task=0x%" PRIxPTR " (%s), unit_sz=%d)", + (uintptr_t) task, task->name, unit_sz); return new (dom) rust_port(task, unit_sz); } @@ -76,33 +93,58 @@ upcall_new_chan(rust_task *task, rust_port *port) { LOG_UPCALL_ENTRY(task); rust_dom *dom = task->dom; task->log(rust_log::UPCALL | rust_log::MEM | rust_log::COMM, - "upcall_new_chan(task=0x%" PRIxPTR ", port=0x%" PRIxPTR ")", - (uintptr_t) task, port); + "upcall_new_chan(" + "task=0x%" PRIxPTR " (%s), port=0x%" PRIxPTR ")", + (uintptr_t) task, task->name, port); I(dom, port); return new (dom) rust_chan(task, port); } /** + * Called whenever this channel needs to be flushed. This can happen due to a + * flush statement, or automatically whenever a channel's ref count is + * about to drop to zero. + */ +extern "C" CDECL void +upcall_flush_chan(rust_task *task, rust_chan *chan) { + LOG_UPCALL_ENTRY(task); + rust_dom *dom = task->dom; + task->log(rust_log::UPCALL | rust_log::COMM, + "flush chan: 0x%" PRIxPTR, chan); + + if (chan->buffer.is_empty()) { + return; + } + + A(dom, chan->port->is_proxy() == false, + "Channels to remote ports should be flushed automatically."); + + // Block on the port until this channel has been completely drained + // by the port. + task->block(chan->port); + task->yield(2); +} + +/** * Called whenever the channel's ref count drops to zero. + * + * Cannot Yield: If the task were to unwind, the dropped ref would still + * appear to be live, causing modify-after-free errors. */ extern "C" CDECL void upcall_del_chan(rust_task *task, rust_chan *chan) { LOG_UPCALL_ENTRY(task); - rust_dom *dom = task->dom; + task->log(rust_log::UPCALL | rust_log::MEM | rust_log::COMM, "upcall del_chan(0x%" PRIxPTR ")", (uintptr_t) chan); - I(dom, !chan->ref_count); - - if (!chan->buffer.is_empty() && chan->is_associated()) { - A(dom, !chan->port->is_proxy(), - "Channels to remote ports should be flushed automatically."); - // A target port may still be reading from this channel. - // Block on this channel until it has been completely drained - // by the port. - task->block(chan); - task->yield(2); - return; - } + A(task->dom, chan->ref_count == 0, + "Channel's ref count should be zero."); + + if (chan->is_associated()) { + A(task->dom, chan->buffer.is_empty(), + "Channel's buffer should be empty."); + chan->disassociate(); + } delete chan; } @@ -136,11 +178,11 @@ extern "C" CDECL void upcall_yield(rust_task *task) { extern "C" CDECL void upcall_join(rust_task *task, maybe_proxy<rust_task> *target) { LOG_UPCALL_ENTRY(task); + rust_task *target_task = target->delegate(); task->log(rust_log::UPCALL | rust_log::COMM, - "target: 0x%" PRIxPTR ", task: 0x%" PRIxPTR, - target, target->delegate()); + "target: 0x%" PRIxPTR ", task: %s @0x%" PRIxPTR, + target, target_task->name, target_task); - rust_task *target_task = target->delegate(); if (target->is_proxy()) { notify_message:: send(notify_message::JOIN, "join", task, target->as_proxy()); @@ -168,8 +210,7 @@ upcall_send(rust_task *task, rust_chan *chan, void *sptr) { "chan: 0x%" PRIxPTR ", sptr: 0x%" PRIxPTR ", size: %d", (uintptr_t) chan, (uintptr_t) sptr, chan->port->delegate()->unit_sz); - chan->buffer.enqueue(sptr); - chan->transmit(); + chan->send(sptr); task->log(rust_log::COMM, "=== sent data ===>"); } @@ -182,17 +223,8 @@ upcall_recv(rust_task *task, uintptr_t *dptr, rust_port *port) { (uintptr_t) port, (uintptr_t) dptr, port->unit_sz, port->chans.length()); - for (uint32_t i = 0; i < port->chans.length(); i++) { - rust_chan *chan = port->chans[i]; - if (chan->buffer.is_empty() == false) { - chan->buffer.dequeue(dptr); - if (chan->buffer.is_empty() && chan->task->blocked()) { - chan->task->wakeup(chan); - delete chan; - } - task->log(rust_log::COMM, "<=== read data ==="); - return; - } + if (port->receive(dptr)) { + return; } // No data was buffered on any incoming channel, so block this task @@ -219,11 +251,12 @@ extern "C" CDECL void upcall_fail(rust_task *task, char const *expr, extern "C" CDECL void upcall_kill(rust_task *task, maybe_proxy<rust_task> *target) { LOG_UPCALL_ENTRY(task); + log_task_state(task, target); rust_task *target_task = target->delegate(); task->log(rust_log::UPCALL | rust_log::TASK, - "kill task 0x%" PRIxPTR ", ref count %d", - target_task, + "kill task %s @0x%" PRIxPTR ", ref count %d", + target_task->name, target_task, target_task->ref_count); if (target->is_proxy()) { @@ -244,6 +277,8 @@ upcall_exit(rust_task *task) { LOG_UPCALL_ENTRY(task); task->log(rust_log::UPCALL | rust_log::TASK, "task ref_count: %d", task->ref_count); + A(task->dom, task->ref_count >= 0, + "Task ref_count should not be negative on exit!"); task->die(); task->notify_tasks_waiting_to_join(); task->yield(1); @@ -498,14 +533,14 @@ static void *rust_thread_start(void *ptr) } extern "C" CDECL rust_task * -upcall_new_task(rust_task *spawner) { +upcall_new_task(rust_task *spawner, const char *name) { LOG_UPCALL_ENTRY(spawner); rust_dom *dom = spawner->dom; - rust_task *task = new (dom) rust_task(dom, spawner); + rust_task *task = new (dom) rust_task(dom, spawner, name); dom->log(rust_log::UPCALL | rust_log::MEM | rust_log::TASK, - "upcall new_task(spawner 0x%" PRIxPTR ") = 0x%" PRIxPTR, - spawner, task); + "upcall new_task(spawner %s @0x%" PRIxPTR ", %s) = 0x%" PRIxPTR, + spawner->name, spawner, name, task); return task; } @@ -516,26 +551,27 @@ upcall_start_task(rust_task *spawner, rust_task *task, rust_dom *dom = spawner->dom; dom->log(rust_log::UPCALL | rust_log::MEM | rust_log::TASK, - "upcall start_task(task 0x%" PRIxPTR + "upcall start_task(task %s @0x%" PRIxPTR " exit_task_glue 0x%" PRIxPTR ", spawnee 0x%" PRIxPTR - ", callsz %" PRIdPTR ")", task, exit_task_glue, spawnee_fn, - callsz); + ", callsz %" PRIdPTR ")", task->name, task, exit_task_glue, + spawnee_fn, callsz); task->start(exit_task_glue, spawnee_fn, spawner->rust_sp, callsz); return task; } extern "C" CDECL maybe_proxy<rust_task> * -upcall_new_thread(rust_task *task) { +upcall_new_thread(rust_task *task, const char *name) { LOG_UPCALL_ENTRY(task); rust_dom *old_dom = task->dom; rust_dom *new_dom = new rust_dom(old_dom->srv->clone(), - old_dom->root_crate); + old_dom->root_crate, + name); task->log(rust_log::UPCALL | rust_log::MEM, - "upcall new_thread() = dom 0x%" PRIxPTR " task 0x%" PRIxPTR, - new_dom, new_dom->root_task); + "upcall new_thread(%s) = dom 0x%" PRIxPTR " task 0x%" PRIxPTR, + name, new_dom, new_dom->root_task); rust_proxy<rust_task> *proxy = new (old_dom) rust_proxy<rust_task>(old_dom, new_dom->root_task, true); diff --git a/src/rt/rust_util.h b/src/rt/rust_util.h index 42082119..03b7766d 100644 --- a/src/rt/rust_util.h +++ b/src/rt/rust_util.h @@ -70,6 +70,13 @@ ptr_vec<T>::pop() } template <typename T> +T * +ptr_vec<T>::peek() +{ + return data[fill - 1]; +} + +template <typename T> void ptr_vec<T>::trim(size_t sz) { @@ -140,11 +147,20 @@ isaac_init(rust_dom *dom, randctx *rctx) CryptReleaseContext(hProv, 0)); } #else - int fd = open("/dev/urandom", O_RDONLY); - I(dom, fd > 0); - I(dom, read(fd, (void*) &rctx->randrsl, sizeof(rctx->randrsl)) - == sizeof(rctx->randrsl)); - I(dom, close(fd) == 0); + char *rust_seed = getenv("RUST_SEED"); + if (rust_seed != NULL) { + ub4 seed = (ub4) atoi(rust_seed); + for (size_t i = 0; i < RANDSIZ; i ++) { + memcpy(&rctx->randrsl[i], &seed, sizeof(ub4)); + seed = (seed + 0x7ed55d16) + (seed << 12); + } + } else { + int fd = open("/dev/urandom", O_RDONLY); + I(dom, fd > 0); + I(dom, read(fd, (void*) &rctx->randrsl, sizeof(rctx->randrsl)) + == sizeof(rctx->randrsl)); + I(dom, close(fd) == 0); + } #endif randinit(rctx, 1); } diff --git a/src/rt/sync/condition_variable.cpp b/src/rt/sync/condition_variable.cpp index d4032572..c34ab7f4 100644 --- a/src/rt/sync/condition_variable.cpp +++ b/src/rt/sync/condition_variable.cpp @@ -9,14 +9,16 @@ // #define TRACE -condition_variable::condition_variable() { #if defined(__WIN32__) +condition_variable::condition_variable() { _event = CreateEvent(NULL, FALSE, FALSE, NULL); +} #else +condition_variable::condition_variable() { pthread_cond_init(&_cond, NULL); pthread_mutex_init(&_mutex, NULL); -#endif } +#endif condition_variable::~condition_variable() { #if defined(__WIN32__) @@ -31,15 +33,31 @@ condition_variable::~condition_variable() { * Wait indefinitely until condition is signaled. */ void condition_variable::wait() { + timed_wait(0); +} + +void condition_variable::timed_wait(size_t timeout_in_ns) { #ifdef TRACE - printf("waiting on condition_variable: 0x%" PRIxPTR "\n", - (uintptr_t)this); + printf("waiting on condition_variable: 0x%" PRIxPTR " for %d ns. \n", + (uintptr_t) this, (int) timeout_in_ns); #endif #if defined(__WIN32__) WaitForSingleObject(_event, INFINITE); #else pthread_mutex_lock(&_mutex); - pthread_cond_wait(&_cond, &_mutex); + // wait() automatically releases the mutex while it waits, and acquires + // it right before exiting. This allows signal() to acquire the mutex + // when signaling.) + if (timeout_in_ns == 0) { + pthread_cond_wait(&_cond, &_mutex); + } else { + timeval time_val; + gettimeofday(&time_val, NULL); + timespec time_spec; + time_spec.tv_sec = time_val.tv_sec + 0; + time_spec.tv_nsec = time_val.tv_usec * 1000 + timeout_in_ns; + pthread_cond_timedwait(&_cond, &_mutex, &time_spec); + } pthread_mutex_unlock(&_mutex); #endif #ifdef TRACE diff --git a/src/rt/sync/condition_variable.h b/src/rt/sync/condition_variable.h index f847ef99..f336a7f2 100644 --- a/src/rt/sync/condition_variable.h +++ b/src/rt/sync/condition_variable.h @@ -13,6 +13,7 @@ public: virtual ~condition_variable(); void wait(); + void timed_wait(size_t timeout_in_ns); void signal(); }; diff --git a/src/rt/sync/sync.cpp b/src/rt/sync/sync.cpp new file mode 100644 index 00000000..eba51a49 --- /dev/null +++ b/src/rt/sync/sync.cpp @@ -0,0 +1,12 @@ +#include "../globals.h" +#include "sync.h" + +void sync::yield() { +#ifdef __APPLE__ + pthread_yield_np(); +#elif __WIN32__ + +#else + pthread_yield(); +#endif +} diff --git a/src/rt/sync/sync.h b/src/rt/sync/sync.h new file mode 100644 index 00000000..902b5661 --- /dev/null +++ b/src/rt/sync/sync.h @@ -0,0 +1,9 @@ +#ifndef SYNC_H +#define SYNC_H + +class sync { +public: + static void yield(); +}; + +#endif /* SYNC_H */ diff --git a/src/rt/util/array_list.h b/src/rt/util/array_list.h index e6ce55ab..929117f3 100644 --- a/src/rt/util/array_list.h +++ b/src/rt/util/array_list.h @@ -16,7 +16,7 @@ public: int32_t append(T value); int32_t push(T value); T pop(); - T replace(T old_value, T new_value); + bool replace(T old_value, T new_value); int32_t index_of(T value); bool is_empty(); T & operator[](size_t index); @@ -62,16 +62,16 @@ array_list<T>::pop() { /** * Replaces the old_value in the list with the new_value. - * Returns the old_value if the replacement succeeded, or NULL otherwise. + * Returns the true if the replacement succeeded, or false otherwise. */ -template<typename T> T +template<typename T> bool array_list<T>::replace(T old_value, T new_value) { int index = index_of(old_value); if (index < 0) { - return NULL; + return false; } _data[index] = new_value; - return old_value; + return true; } template<typename T> int32_t diff --git a/src/run.py b/src/run.py new file mode 100644 index 00000000..4cce4102 --- /dev/null +++ b/src/run.py @@ -0,0 +1,81 @@ +import os +import sys +import time +import glob +import fnmatch +from optparse import OptionParser + +rustDir = os.path.abspath('.') +rustTestDir = rustDir + "/test"; +rustTestRunPassDir = rustTestDir + "/run-pass"; +rustTestRunFailDir = rustTestDir + "/run-fail"; +rustTestCompileFailDir = rustTestDir + "/run-compile-fail"; +rustTestRunBenchDir = rustTestDir + "/run-bench"; + +parser = OptionParser() +parser.set_usage("run.py [options] pattern : run.py -n 100 \"bas*\" -q"); +parser.add_option("-n", dest="repetitions", + help="number of repetitions", metavar="NUMBER") +parser.add_option("-q", action="store_true", dest="quiet", default=False, + help="suppresses rust log output") +parser.add_option("-p", action="store_true", dest="printSource", + default=False, help="prints the test case's source") +parser.add_option("-s", dest="seed", metavar="NUMBER", default=-1, + help="seeds the rust scheduler, use -1 to generate seeds, " + + " or >= 0 to specify a seed") + +(options, args) = parser.parse_args() + +def getRustTests(filter): + tests = [] + for root, dirnames, filenames in os.walk(rustTestDir): + for filename in fnmatch.filter(filenames, filter + '.rs'): + tests.append(os.path.join(root, filename). + replace(rustDir + "/", "")); + return tests + + +if len(args) != 1: + parser.print_usage(); + sys.exit(0); + +tests = getRustTests(args[0]); + +# Make +for rustProgram in tests: + print "Making: " + rustProgram; + result = os.system("make " + rustProgram.replace(".rs", ".x86")) >> 8; + if (result != 0): + print "Make failed!"; + sys.exit(1); + +if (options.quiet): + os.putenv("RUST_LOG", "none") + +# Rut +totalPassed = 0; +for rustProgram in tests: + repetitions = 1; + if (options.repetitions): + repetitions = int(options.repetitions); + passed = 0; + if (options.printSource): + os.system("cat " + rustProgram); + for i in range(0, repetitions): + print "Running: " + rustProgram + " " + str(i) + \ + " of " + str(repetitions); + if (options.seed): + if (int(options.seed) >= 0): + os.putenv("RUST_SEED", options.seed); + else: + os.putenv("RUST_SEED", str(i)); + result = os.system(rustProgram.replace(".rs", ".x86")); + exitStatus = result >> 8; + signalNumber = result & 0xF; + if (result == 0): + passed += 1; + print "Result for: " + rustProgram + " " + str(passed) + \ + " of " + str(repetitions) + " passed."; + totalPassed += passed; +print "Total: " + str(totalPassed) + " of " + \ + str(len(tests) * repetitions) + " passed."
\ No newline at end of file diff --git a/src/test/run-pass/arith-0.rs b/src/test/run-pass/arith-0.rs new file mode 100644 index 00000000..7587b005 --- /dev/null +++ b/src/test/run-pass/arith-0.rs @@ -0,0 +1,5 @@ +fn main() -> () { + let int a = 10; + log a; + check (a * (a - 1) == 90); +}
\ No newline at end of file diff --git a/src/test/run-pass/arith-1.rs b/src/test/run-pass/arith-1.rs new file mode 100644 index 00000000..bac6a055 --- /dev/null +++ b/src/test/run-pass/arith-1.rs @@ -0,0 +1,22 @@ +fn main() -> () { + let int i32_a = 10; + check(i32_a == 10); + check(i32_a - 10 == 0); + check(i32_a / 10 == 1); + check(i32_a - 20 == -10); + check(i32_a << 10 == 10240); + check(i32_a << 16 == 655360); + check(i32_a * 16 == 160); + check(i32_a * i32_a * i32_a == 1000); + check(i32_a * i32_a * i32_a * i32_a == 10000); + check(((i32_a * i32_a) / i32_a) * i32_a == 100); + check(i32_a * (i32_a - 1) << 2 + i32_a == 368640); + + let int i32_b = 0x10101010; + check(i32_b + 1 - 1 == i32_b); + check(i32_b << 1 == i32_b << 1); + check(i32_b >> 1 == i32_b >> 1); + check((i32_b & (i32_b << 1)) == 0); + log ((i32_b | (i32_b << 1))); + check((i32_b | (i32_b << 1)) == 0x30303030); +}
\ No newline at end of file diff --git a/src/test/run-pass/arith-2.rs b/src/test/run-pass/arith-2.rs new file mode 100644 index 00000000..33a740c8 --- /dev/null +++ b/src/test/run-pass/arith-2.rs @@ -0,0 +1,5 @@ +fn main() -> () { + let int i32_c = 0x10101010; + check (i32_c + i32_c * 2 / 3 * 2 + (i32_c - 7 % 3) == + i32_c + (((i32_c * 2) / 3) * 2) + (i32_c - (7 % 3))); +}
\ No newline at end of file diff --git a/src/test/run-pass/basic-1.rs b/src/test/run-pass/basic-1.rs new file mode 100644 index 00000000..bdd7ee25 --- /dev/null +++ b/src/test/run-pass/basic-1.rs @@ -0,0 +1,25 @@ +// -*- rust -*- + +io fn a(chan[int] c) { + c <| 10; +} + +io fn main() { + let port[int] p = port(); + spawn a(chan(p)); + spawn b(chan(p)); + let int n = 0; + n <- p; + n <- p; +// log "Finished."; +} + +io fn b(chan[int] c) { +// log "task b0"; +// log "task b1"; +// log "task b2"; +// log "task b3"; +// log "task b4"; +// log "task b5"; + c <| 10; +}
\ No newline at end of file diff --git a/src/test/run-pass/basic-2.rs b/src/test/run-pass/basic-2.rs new file mode 100644 index 00000000..975a0d4b --- /dev/null +++ b/src/test/run-pass/basic-2.rs @@ -0,0 +1,26 @@ +// -*- rust -*- + +io fn a(chan[int] c) { + log "task a0"; + log "task a1"; + c <| 10; +} + +io fn main() { + let port[int] p = port(); + spawn a(chan(p)); + spawn b(chan(p)); + let int n = 0; + n <- p; + n <- p; + log "Finished."; +} + +io fn b(chan[int] c) { + log "task b0"; + log "task b1"; + log "task b2"; + log "task b2"; + log "task b3"; + c <| 10; +} diff --git a/src/test/run-pass/destructor-ordering.rs b/src/test/run-pass/destructor-ordering.rs index 99479c55..f7f5b8c8 100644 --- a/src/test/run-pass/destructor-ordering.rs +++ b/src/test/run-pass/destructor-ordering.rs @@ -1,58 +1,93 @@ -// We share an instance of this type among all the destructor-order -// checkers. It tracks how many destructors have run so far and -// 'fail's when one runs out of order. -// FIXME: Make it easier to collect a failure message. -state obj order_tracker(mutable int init) { - fn assert_order(int expected, str fail_message) { - if (expected != init) { - log expected; +// This test checks that destructors run in the right order. Because +// stateful objects can't have destructors, we have the destructors +// record their expected order into a channel when they execute (so +// the object becomes 'io' rather than 'state'). Then each test case +// asserts that the channel produces values in ascending order. +// +// FIXME: Write an int->str function and concatenate the whole failure +// message into a single log statement (or, even better, a print). +// +// FIXME: check_order should take only 1 line in a test, not 2+a block +// block. Since destructor-having objects can't refer to mutable state +// (like the port), we'd need a with-like construct to do the same for +// stateful objects within a scope. +// +// FIXME #21: Each test should execute in its own task, so it can fail +// independently, writing its error message to a channel that the +// parent task aggregates. + +type order_info = rec(int order, str msg); + +io fn check_order(port[order_info] expected_p) { + chan(expected_p) <| rec(order=-1, msg=""); + let mutable int actual = 0; + auto expected <- expected_p; // FIXME #121: Workaround for while(true) bug. + auto done = -1; // FIXME: Workaround for typechecking bug. + while(expected.order != done) { + if (expected.order != actual) { + log expected.order; log " != "; - log init; - log fail_message; + log actual; + log expected.msg; fail; } - init += 1; + actual += 1; + expected <- expected_p; } } -obj dorder(@order_tracker tracker, int order, str message) { +obj dorder(chan[order_info] expected, int order, str message) { drop { - tracker.assert_order(order, message); + expected <| rec(order=order, msg=message); } } -fn test_simple() { - auto tracker = @order_tracker(0); +io fn test_simple() { + let port[order_info] tracker_p = port(); + auto tracker = chan(tracker_p); dorder(tracker, 1, "Reverse decl order"); dorder(tracker, 0, "Reverse decl order"); + check_order(tracker_p); } -fn test_block() { - auto tracker = @order_tracker(0); - dorder(tracker, 2, "Before block"); +io fn test_block() { + let port[order_info] tracker_p = port(); + auto tracker = chan(tracker_p); { - dorder(tracker, 0, "Inside block"); + dorder(tracker, 2, "Before block"); + { + dorder(tracker, 0, "Inside block"); + } + dorder(tracker, 1, "After block"); } - dorder(tracker, 1, "After block"); + check_order(tracker_p); } -fn test_decl_v_init() { - auto tracker = @order_tracker(0); - auto var1; - auto var2; - var2 = dorder(tracker, 0, "decl, not init"); - var1 = dorder(tracker, 1, "decl, not init"); +io fn test_decl_v_init() { + let port[order_info] tracker_p = port(); + auto tracker = chan(tracker_p); + { + auto var1; + auto var2; + var2 = dorder(tracker, 0, "decl, not init"); + var1 = dorder(tracker, 1, "decl, not init"); + } + check_order(tracker_p); } -fn test_overwritten_obj() { - auto tracker = @order_tracker(0); - auto var1 = dorder(tracker, 0, "overwritten object destroyed first"); - auto var2 = dorder(tracker, 2, "destroyed at end of scope"); - var1 = dorder(tracker, 3, "overwriter deleted in rev decl order"); +io fn test_overwritten_obj() { + let port[order_info] tracker_p = port(); + auto tracker = chan(tracker_p); { - dorder(tracker, 1, "overwritten object destroyed before end of scope"); + auto var1 = dorder(tracker, 0, "overwritten object destroyed first"); + auto var2 = dorder(tracker, 2, "destroyed at end of scope"); + var1 = dorder(tracker, 3, "overwriter deleted in rev decl order"); + { + dorder(tracker, 1, "overwritten object destroyed before end of scope"); + } } + check_order(tracker_p); } // Used to embed dorder objects into an expression. Note that the @@ -60,17 +95,21 @@ fn test_overwritten_obj() { fn combine_dorders(dorder d1, dorder d2) -> int { ret 1; } -fn test_expression_destroyed_right_to_left() { - auto tracker = @order_tracker(0); - combine_dorders(dorder(tracker, 4, ""), dorder(tracker, 3, "")) - / combine_dorders(dorder(tracker, 2, ""), dorder(tracker, 1, "")); +io fn test_expression_destroyed_right_to_left() { + let port[order_info] tracker_p = port(); + auto tracker = chan(tracker_p); { - dorder(tracker, 0, - "expression objects live to end of block, not statement"); + combine_dorders(dorder(tracker, 4, ""), dorder(tracker, 3, "")) + / combine_dorders(dorder(tracker, 2, ""), dorder(tracker, 1, "")); + { + dorder(tracker, 0, + "expression objects live to end of block, not statement"); + } } + check_order(tracker_p); } -fn main() { +io fn main() { test_simple(); test_block(); test_decl_v_init(); diff --git a/src/test/run-pass/int-lib.rs b/src/test/run-pass/int-lib.rs new file mode 100644 index 00000000..ce39de43 --- /dev/null +++ b/src/test/run-pass/int-lib.rs @@ -0,0 +1,15 @@ +use std; + +import std._int; + +fn test_to_str() { + check (_int.to_str(0, 10u) == "0"); + check (_int.to_str(1, 10u) == "1"); + check (_int.to_str(-1, 10u) == "-1"); + check (_int.to_str(255, 16u) == "ff"); + check (_int.to_str(-71, 36u) == "-1z"); +} + +fn main() { + test_to_str(); +} diff --git a/src/test/run-pass/integral-indexing.rs b/src/test/run-pass/integral-indexing.rs new file mode 100644 index 00000000..fe7d147c --- /dev/null +++ b/src/test/run-pass/integral-indexing.rs @@ -0,0 +1,22 @@ +// This is a testcase for issue #94. + +fn main() { + + let vec[int] v = vec(0, 1, 2, 3, 4, 5); + let str s = "abcdef"; + check (v.(3u) == 3); + check (v.(3u8) == 3); + check (v.(3i8) == 3); + check (v.(3u32) == 3); + check (v.(3i32) == 3); + + log v.(3u8); + + check (s.(3u) == 'd' as u8); + check (s.(3u8) == 'd' as u8); + check (s.(3i8) == 'd' as u8); + check (s.(3u32) == 'd' as u8); + check (s.(3i32) == 'd' as u8); + + log s.(3u8); +}
\ No newline at end of file diff --git a/src/test/run-pass/lib-map.rs b/src/test/run-pass/lib-map.rs index 11101c84..058fb237 100644 --- a/src/test/run-pass/lib-map.rs +++ b/src/test/run-pass/lib-map.rs @@ -4,13 +4,17 @@ use std; import std.map; fn test_simple() { + log "*** starting test_simple"; + fn eq(&uint x, &uint y) -> bool { ret x == y; } let map.hashfn[uint] hasher = std.util.id[uint]; let map.eqfn[uint] eqer = eq; let map.hashmap[uint, uint] hm = map.mk_hashmap[uint, uint](hasher, eqer); + + log "*** finished test_simple"; } fn main() { test_simple(); -}
\ No newline at end of file +} diff --git a/src/test/run-pass/str-lib.rs b/src/test/run-pass/str-lib.rs new file mode 100644 index 00000000..585f9b8d --- /dev/null +++ b/src/test/run-pass/str-lib.rs @@ -0,0 +1,16 @@ +use std; +import std._str; + +fn test_bytes_len() { + check (_str.byte_len("") == 0u); + check (_str.byte_len("hello world") == 11u); + check (_str.byte_len("\x63") == 1u); + check (_str.byte_len("\xa2") == 2u); + check (_str.byte_len("\u03c0") == 2u); + check (_str.byte_len("\u2620") == 3u); + check (_str.byte_len("\U0001d11e") == 4u); +} + +fn main() { + test_bytes_len(); +} diff --git a/src/test/run-pass/task-comm-0.rs b/src/test/run-pass/task-comm-0.rs index 5992ba5c..28294422 100644 --- a/src/test/run-pass/task-comm-0.rs +++ b/src/test/run-pass/task-comm-0.rs @@ -15,5 +15,5 @@ io fn test05() { let int value <- po; value <- po; value <- po; - log value; + check(value == 30); } diff --git a/src/test/run-pass/task-comm-2.rs b/src/test/run-pass/task-comm-2.rs index 9c85da34..864d49de 100644 --- a/src/test/run-pass/task-comm-2.rs +++ b/src/test/run-pass/task-comm-2.rs @@ -1,8 +1,8 @@ fn main() -> () { - log "===== THREADS ====="; + log "===== SPAWNING and JOINING TASKS ====="; + test00(false); + log "===== SPAWNING and JOINING THREAD TASKS ====="; test00(true); - log "====== TASKS ======"; - // test00(false); } fn start(int task_number) { @@ -15,7 +15,7 @@ fn start(int task_number) { } fn test00(bool create_threads) { - let int number_of_tasks = 0; + let int number_of_tasks = 8; let int i = 0; let vec[task] tasks = vec(); diff --git a/src/test/run-pass/task-comm-3.rs b/src/test/run-pass/task-comm-3.rs index 6dd620cc..9a3f9e16 100644 --- a/src/test/run-pass/task-comm-3.rs +++ b/src/test/run-pass/task-comm-3.rs @@ -1,6 +1,8 @@ io fn main() -> () { - log "===== THREADS ====="; + log "===== WITHOUT THREADS ====="; test00(false); + log "====== WITH THREADS ======"; + test00(true); } io fn test00_start(chan[int] ch, int message, int count) { @@ -15,8 +17,9 @@ io fn test00_start(chan[int] ch, int message, int count) { } io fn test00(bool is_multithreaded) { - let int number_of_tasks = 1; - let int number_of_messages = 0; + let int number_of_tasks = 16; + let int number_of_messages = 4; + log "Creating tasks"; let port[int] po = port(); @@ -27,13 +30,13 @@ io fn test00(bool is_multithreaded) { // Create and spawn tasks... let vec[task] tasks = vec(); while (i < number_of_tasks) { - i = i + 1; if (is_multithreaded) { tasks += vec( spawn thread test00_start(ch, i, number_of_messages)); } else { tasks += vec(spawn test00_start(ch, i, number_of_messages)); } + i = i + 1; } // Read from spawned tasks... @@ -53,7 +56,7 @@ io fn test00(bool is_multithreaded) { } log "Completed: Final number is: "; - check (sum + 1 == number_of_messages * - (number_of_tasks * number_of_tasks + number_of_tasks) / 2); - log sum; -}
\ No newline at end of file + // check (sum == (((number_of_tasks * (number_of_tasks - 1)) / 2) * + // number_of_messages)); + check (sum == 480); +} diff --git a/src/test/run-pass/task-comm-6.rs b/src/test/run-pass/task-comm-6.rs index c579a98c..2774c0ba 100644 --- a/src/test/run-pass/task-comm-6.rs +++ b/src/test/run-pass/task-comm-6.rs @@ -31,6 +31,8 @@ io fn test00() { r <- p; sum += r; i += 1; } - - check (sum == 4 * ((number_of_messages * (number_of_messages - 1)) / 2)); + + check (sum == 1998000); + // check (sum == 4 * ((number_of_messages * + // (number_of_messages - 1)) / 2)); }
\ No newline at end of file diff --git a/src/test/run-pass/task-comm.rs b/src/test/run-pass/task-comm.rs index ef71c6e1..3c5d3216 100644 --- a/src/test/run-pass/task-comm.rs +++ b/src/test/run-pass/task-comm.rs @@ -1,11 +1,11 @@ -fn main() -> () { - // test00(true); +io fn main() -> () { + test00(true); // test01(); - // test02(); - // test03(); - // test04(); - // test05(); + test02(); + test03(); + test04(); + test05(); test06(); } @@ -22,7 +22,7 @@ io fn test00_start(chan[int] ch, int message, int count) { io fn test00(bool is_multithreaded) { let int number_of_tasks = 1; - let int number_of_messages = 64; + let int number_of_messages = 4; log "Creating tasks"; let port[int] po = port(); @@ -103,7 +103,7 @@ fn test04_start() { fn test04() { log "Spawning lots of tasks."; - let int i = 64; + let int i = 4; while (i > 0) { i = i - 1; spawn thread test04_start(); @@ -139,7 +139,7 @@ fn test06_start(int task_number) { } fn test06() { - let int number_of_tasks = 32; + let int number_of_tasks = 4; log "Creating tasks"; let int i = 0; |