diff options
105 files changed, 4087 insertions, 1289 deletions
@@ -2,6 +2,8 @@ *.x86 *.llvm *.out +*.x86.tmp +*.llvm.tmp *.cmx *.dll *.exe @@ -30,6 +32,8 @@ *.swp .hg/ .hgignore +.cproject +.project lexer.ml rustboot rustc diff --git a/doc/rust.texi b/doc/rust.texi index 3aa079d7..fcfb6499 100644 --- a/doc/rust.texi +++ b/doc/rust.texi @@ -599,6 +599,7 @@ Unicode characters. * Ref.Lex.Ignore:: Ignored characters. * Ref.Lex.Ident:: Identifier tokens. * Ref.Lex.Key:: Keyword tokens. +* Ref.Lex.Res:: Reserved tokens. * Ref.Lex.Num:: Numeric tokens. * Ref.Lex.Text:: String and character tokens. * Ref.Lex.Syntax:: Syntactic extension tokens. @@ -636,7 +637,7 @@ token or a syntactic extension token. Multi-line comments may be nested. Identifiers follow the pattern of C identifiers: they begin with a @emph{letter} or @emph{underscore}, and continue with any combination of @emph{letters}, @emph{decimal digits} and underscores, and must not be equal -to any keyword. @xref{Ref.Lex.Key}. +to any keyword or reserved token. @xref{Ref.Lex.Key}. @xref{Ref.Lex.Res}. A @emph{letter} is a Unicode character in the ranges U+0061-U+007A and U+0041-U+005A (@code{'a'}-@code{'z'} and @code{'A'}-@code{'Z'}). @@ -728,6 +729,35 @@ The keywords are: @tab @code{be} @end multitable +@node Ref.Lex.Res +@subsection Ref.Lex.Res +@c * Ref.Lex.Res:: Reserved tokens. + +The reserved tokens are: +@cindex Reserved + +@sp 2 + +@multitable @columnfractions .15 .15 .15 .15 .15 +@item @code{f16} +@tab @code{f80} +@tab @code{f128} +@item @code{m32} +@tab @code{m64} +@tab @code{m128} +@tab @code{dec} +@end multitable + +@sp 2 + +At present these tokens have no defined meaning in the Rust language. + +These tokens may correspond, in some current or future implementation, +to additional built-in types for decimal floating-point, extended +binary and interchange floating-point formats, as defined in the IEEE +754-1985 and IEEE 754-2008 specifications. + + @node Ref.Lex.Num @subsection Ref.Lex.Num @c * Ref.Lex.Num:: Numeric tokens. @@ -785,6 +815,10 @@ only two floating-point suffixes: @code{f32} and @code{f64}. Each of these gives the floating point literal the associated type, rather than @code{float}. +A set of suffixes are also reserved to accommodate literal support for +types corresponding to reserved tokens. The reserved suffixes are @code{f16}, +@code{f80}, @code{f128}, @code{m}, @code{m32}, @code{m64} and @code{m128}. + @sp 1 A @dfn{hex digit} is either a @emph{decimal digit} or else a character in the ranges U+0061-U+0066 and U+0041-U+0046 (@code{'a'}-@code{'f'}, @@ -2024,7 +2058,7 @@ The signed two's complement word types @code{i8}, @code{i16}, @code{i32} and @end ifhtml respectively. @item -The IEEE 754 single-precision and double-precision floating-point types: +The IEEE 754-2008 @code{binary32} and @code{binary64} floating-point types: @code{f32} and @code{f64}, respectively. @end itemize @@ -2822,12 +2856,15 @@ x.y = z + 2; @c * Ref.Stmt.Spawn:: Statements creating new tasks. @cindex Spawn statement -A @code{spawn} statement consists of keyword @code{spawn}, followed by a -normal @emph{call} statement (@pxref{Ref.Stmt.Call}). A @code{spawn} -statement causes the runtime to construct a new task executing the called -function. The called function is referred to as the @dfn{entry function} for -the spawned task, and its arguments are copied from the spawning task to the -spawned task before the spawned task begins execution. +A @code{spawn} statement consists of keyword @code{spawn}, followed by +an optional literal string naming the new task and then a normal +@emph{call} statement (@pxref{Ref.Stmt.Call}). A @code{spawn} +statement causes the runtime to construct a new task executing the +called function with the given name. The called function is referred +to as the @dfn{entry function} for the spawned task, and its arguments +are copied from the spawning task to the spawned task before the +spawned task begins execution. If no explicit name is present, the +task is implicitly named with the string of the call statement. Functions taking alias-slot arguments, or returning non-nil values, cannot be spawned. Iterators cannot be spawned. @@ -2843,6 +2880,7 @@ fn helper(chan[u8] out) @{ let port[u8] out; let task p = spawn helper(chan(out)); +let task p2 = spawn "my_helper" helper(chan(out)); // let task run, do other things. auto result <- out; diff --git a/src/Makefile b/src/Makefile index 10810c05..76e11272 100644 --- a/src/Makefile +++ b/src/Makefile @@ -35,7 +35,7 @@ ifeq ($(CFG_OSTYPE), Linux) CFG_RUNTIME := librustrt.so CFG_STDLIB := libstd.so CFG_GCC_CFLAGS += -fPIC - CFG_GCC_LINK_FLAGS += -shared -fPIC -ldl -lpthread + CFG_GCC_LINK_FLAGS += -shared -fPIC -ldl -lpthread -lrt ifeq ($(CFG_CPUTYPE), x86_64) CFG_GCC_CFLAGS += -m32 CFG_GCC_LINK_FLAGS += -m32 @@ -245,7 +245,9 @@ 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/timer.cpp \ + rt/sync/sync.cpp \ + rt/sync/spin_lock.cpp \ rt/sync/lock_free_queue.cpp \ rt/sync/condition_variable.cpp \ rt/rust.cpp \ @@ -263,7 +265,9 @@ RUNTIME_CS := rt/sync/spin_lock.cpp \ rt/rust_message.cpp \ rt/rust_timer.cpp \ rt/circular_buffer.cpp \ - rt/isaac/randport.cpp + rt/isaac/randport.cpp \ + rt/rust_srv.cpp \ + rt/memory_region.cpp RUNTIME_HDR := rt/globals.h \ rt/rust.h \ @@ -279,7 +283,12 @@ 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 \ + rt/sync/timer.h \ + rt/rust_srv.h \ + rt/memory_region.h \ + rt/memory.h RUNTIME_INCS := -Irt/isaac -Irt/uthash RUNTIME_OBJS := $(RUNTIME_CS:.cpp=$(CFG_OBJ_SUFFIX)) @@ -296,7 +305,7 @@ all: $(CFG_COMPILER) $(MKFILES) $(GENERATED) boot/util/version.ml: Makefile $(CFG_QUIET)git log -1 \ - --format='let version = "prerelease (%h %ci)";;' >$@ + --format='let version = "prerelease (%h %ci)";;' >$@ || exit 1 loc: $(CFG_QUIET)wc -l $(BOOT_MLS) $(RUNTIME_CS) $(RUNTIME_HDR) @@ -380,13 +389,14 @@ TASK_XFAILS := test/run-pass/acyclic-unwind.rs \ test/run-pass/task-comm-7.rs \ test/run-pass/task-comm-8.rs \ test/run-pass/task-comm-9.rs \ + test/run-pass/task-comm-10.rs \ + test/run-pass/task-comm-11.rs \ + test/run-pass/task-life-0.rs \ test/run-pass/task-comm.rs \ test/run-pass/threads.rs \ test/run-pass/yield.rs TEST_XFAILS_X86 := $(TASK_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 \ @@ -398,16 +408,13 @@ TEST_XFAILS_X86 := $(TASK_XFAILS) \ 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-io.rs \ test/run-pass/lib-map.rs \ test/run-pass/mlist-cycle.rs \ test/run-pass/obj-as.rs \ test/run-pass/task-comm.rs \ test/run-pass/vec-slice.rs \ - test/run-pass/task-comm-2.rs \ test/run-pass/task-comm-3.rs \ - test/run-pass/task-comm-5.rs \ - test/run-pass/task-comm-6.rs \ test/compile-fail/bad-recv.rs \ test/compile-fail/bad-send.rs \ test/compile-fail/infinite-tag-type-recursion.rs \ @@ -416,14 +423,17 @@ TEST_XFAILS_X86 := $(TASK_XFAILS) \ TEST_XFAILS_LLVM := $(TASK_XFAILS) \ $(addprefix test/run-pass/, \ + arith-1.rs \ acyclic-unwind.rs \ alt-pattern-simple.rs \ alt-tag.rs \ - arithmetic-interference.rs \ + append-units.rs \ argv.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 \ @@ -464,7 +474,6 @@ 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 \ @@ -474,8 +483,10 @@ TEST_XFAILS_LLVM := $(TASK_XFAILS) \ lazy-init.rs \ lazychan.rs \ lib-deque.rs \ + lib-io.rs \ lib-map.rs \ lib-rand.rs \ + lib-vec-str-conversions.rs \ linear-for-loop.rs \ list.rs \ many.rs \ @@ -500,6 +511,7 @@ TEST_XFAILS_LLVM := $(TASK_XFAILS) \ rec-tup.rs \ rec.rs \ simple-obj.rs \ + size-and-align.rs \ spawn-fn.rs \ spawn-module-qualified.rs \ spawn.rs \ @@ -508,6 +520,7 @@ TEST_XFAILS_LLVM := $(TASK_XFAILS) \ str-concat.rs \ str-idx.rs \ str-lib.rs \ + task-lib.rs \ tag.rs \ tail-cps.rs \ tail-direct.rs \ @@ -522,6 +535,9 @@ TEST_XFAILS_LLVM := $(TASK_XFAILS) \ task-comm-7.rs \ task-comm-8.rs \ task-comm-9.rs \ + task-comm-10.rs \ + task-comm-11.rs \ + task-life-0.rs \ threads.rs \ type-sizes.rs \ u8-incr.rs \ @@ -541,6 +557,7 @@ TEST_XFAILS_LLVM := $(TASK_XFAILS) \ vec-lib.rs \ vec-slice.rs \ vec.rs \ + while-flow-graph.rs \ writealias.rs \ yield.rs \ yield2.rs \ @@ -602,6 +619,10 @@ TEST_RPASS_OUTS_X86 := \ $(TEST_RPASS_EXES_X86:.x86$(CFG_EXE_SUFFIX)=.x86.out) TEST_RPASS_OUTS_LLVM := \ $(TEST_RPASS_EXES_LLVM:.llvm$(CFG_EXE_SUFFIX)=.llvm.out) +TEST_RPASS_TMPS_X86 := \ + $(TEST_RPASS_EXES_X86:.x86$(CFG_EXE_SUFFIX)=.x86$(CFG_EXE_SUFFIX).tmp) +TEST_RPASS_TMPS_LLVM := \ + $(TEST_RPASS_EXES_LLVM:.llvm$(CFG_EXE_SUFFIX)=.llvm$(CFG_EXE_SUFFIX).tmp) TEST_RFAIL_CRATES_X86 := $(filter-out $(TEST_XFAILS_X86), $(RFAIL_RC)) @@ -619,6 +640,10 @@ TEST_RFAIL_OUTS_X86 := \ $(TEST_RFAIL_EXES_X86:.x86$(CFG_EXE_SUFFIX)=.x86.out) TEST_RFAIL_OUTS_LLVM := \ $(TEST_RFAIL_EXES_LLVM:.llvm$(CFG_EXE_SUFFIX)=.llvm.out) +TEST_RFAIL_TMPS_X86 := \ + $(TEST_RFAIL_EXES_X86:.x86$(CFG_EXE_SUFFIX)=.x86$(CFG_EXE_SUFFIX).tmp) +TEST_RFAIL_TMPS_LLVM := \ + $(TEST_RFAIL_EXES_LLVM:.llvm$(CFG_EXE_SUFFIX)=.llvm$(CFG_EXE_SUFFIX).tmp) TEST_CFAIL_CRATES_X86 := $(filter-out $(TEST_XFAILS_X86), $(CFAIL_RC)) @@ -636,6 +661,11 @@ TEST_CFAIL_OUTS_X86 := \ $(TEST_CFAIL_EXES_X86:.x86$(CFG_EXE_SUFFIX)=.x86.out) TEST_CFAIL_OUTS_LLVM := \ $(TEST_CFAIL_EXES_LLVM:.llvm$(CFG_EXE_SUFFIX)=.llvm.out) +TEST_CFAIL_TMPS_X86 := \ + $(TEST_CFAIL_EXES_X86:.x86$(CFG_EXE_SUFFIX)=.x86$(CFG_EXE_SUFFIX).tmp) +TEST_CFAIL_TMPS_LLVM := \ + $(TEST_CFAIL_EXES_LLVM:.llvm$(CFG_EXE_SUFFIX)=.llvm$(CFG_EXE_SUFFIX).tmp) + ALL_TEST_CRATES := $(TEST_CFAIL_CRATES_X86) \ $(TEST_RFAIL_CRATES_X86) \ @@ -690,10 +720,12 @@ BOOT := $(CFG_QUIET)OCAMLRUNPARAM="b1" $(CFG_BOOT) $(CFG_BOOT_FLAGS) $(CFG_QUIET)mv $< $@ test/run-pass/%.out.tmp: test/run-pass/%$(CFG_EXE_SUFFIX) $(CFG_RUNTIME) + $(CFG_QUIET)rm -f $<.tmp @$(call CFG_ECHO, run: $<) $(CFG_QUIET)$(call CFG_RUN_TARG, $<) > $@ test/run-fail/%.out.tmp: test/run-fail/%$(CFG_EXE_SUFFIX) $(CFG_RUNTIME) + $(CFG_QUIET)rm -f $<.tmp @$(call CFG_ECHO, run: $<) $(CFG_QUIET)rm -f $@ $(CFG_QUIET)$(call CFG_RUN_TARG, $<) >$@ 2>&1 ; X=$$? ; \ @@ -886,12 +918,18 @@ clean: $(CFG_QUIET)rm -f $(ML_DEPFILES) $(C_DEPFILES) $(CRATE_DEPFILES) $(CFG_QUIET)rm -f $(GENERATED) $(CFG_QUIET)rm -f $(CFG_BOOT) $(CFG_RUNTIME) $(CFG_STDLIB) - $(CFG_QUIET)rm -f $(TEST_RPASS_EXES_X86) $(TEST_RPASS_OUTS_X86) - $(CFG_QUIET)rm -f $(TEST_RPASS_EXES_LLVM) $(TEST_RPASS_OUTS_LLVM) - $(CFG_QUIET)rm -f $(TEST_RFAIL_EXES_X86) $(TEST_RFAIL_OUTS_X86) - $(CFG_QUIET)rm -f $(TEST_RFAIL_EXES_LLVM) $(TEST_RFAIL_OUTS_LLVM) - $(CFG_QUIET)rm -f $(TEST_CFAIL_EXES_X86) $(TEST_CFAIL_OUTS_X86) - $(CFG_QUIET)rm -f $(TEST_CFAIL_EXES_LLVM) $(TEST_CFAIL_OUTS_LLVM) + $(CFG_QUIET)rm -f $(TEST_RPASS_EXES_X86) $(TEST_RPASS_OUTS_X86) \ + $(TEST_RPASS_TMPS_X86) + $(CFG_QUIET)rm -f $(TEST_RPASS_EXES_LLVM) $(TEST_RPASS_OUTS_LLVM) \ + $(TEST_RPASS_TMPS_LLVM) + $(CFG_QUIET)rm -f $(TEST_RFAIL_EXES_X86) $(TEST_RFAIL_OUTS_X86) \ + $(TEST_RFAIL_TMPS_X86) + $(CFG_QUIET)rm -f $(TEST_RFAIL_EXES_LLVM) $(TEST_RFAIL_OUTS_LLVM) \ + $(TEST_RFAIL_TMPS_LLVM) + $(CFG_QUIET)rm -f $(TEST_CFAIL_EXES_X86) $(TEST_CFAIL_OUTS_X86) \ + $(TEST_CFAIL_TMPS_X86) + $(CFG_QUIET)rm -f $(TEST_CFAIL_EXES_LLVM) $(TEST_CFAIL_OUTS_LLVM) \ + $(TEST_CFAIL_TMPS_LLVM) $(CFG_QUIET)rm -rf $(TEST_RPASS_EXES_LLVM:.llvm=.llvm.dSYM) $(CFG_QUIET)rm -rf $(TEST_RFAIL_EXES_LLVM:.llvm=.llvm.dSYM) $(CFG_QUIET)rm -Rf $(PKG_NAME)-*.tar.gz dist 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/elf.ml b/src/boot/be/elf.ml index 3d25657b..406508e4 100644 --- a/src/boot/be/elf.ml +++ b/src/boot/be/elf.ml @@ -169,14 +169,16 @@ type sh_flags = let section_header + ?(sh_link:int64 option=None) + ?(sh_info:int64 option=None) + ?(zero_sh_addr:bool=false) + ?(sh_flags:sh_flags list=[]) + ?(section_fixup:fixup option=None) + ?(sh_addralign:int64=1L) + ?(sh_entsize:int64=0L) ~(shstring_table_fixup:fixup) ~(shname_string_fixup:fixup) - ~(sh_type:sh_type) - ~(sh_flags:sh_flags list) - ~(section_fixup:fixup option) - ~(sh_addralign:int64) - ~(sh_entsize:int64) - ~(sh_link:int64 option) + (sh_type:sh_type) : frag = SEQ [| @@ -201,9 +203,12 @@ let section_header SHF_WRITE -> 0x1L | SHF_ALLOC -> 0x2L | SHF_EXECINSTR -> 0x4L) sh_flags))); - WORD (TY_u32, (match section_fixup with - None -> (IMM 0L) - | Some s -> (M_POS s))); + WORD (TY_u32, + if zero_sh_addr + then IMM 0L + else (match section_fixup with + None -> (IMM 0L) + | Some s -> (M_POS s))); WORD (TY_u32, (match section_fixup with None -> (IMM 0L) | Some s -> (F_POS s))); @@ -213,7 +218,9 @@ let section_header WORD (TY_u32, (IMM (match sh_link with None -> 0L | Some i -> i))); - WORD (TY_u32, (IMM 0L)); (* sh_info *) + WORD (TY_u32, (IMM (match sh_info with + None -> 0L + | Some i -> i))); WORD (TY_u32, (IMM sh_addralign)); WORD (TY_u32, (IMM sh_entsize)); |] @@ -633,7 +640,7 @@ let elf32_linux_x86_file let dynsymndx = 4L in (* Section index of .dynsym *) let dynstrndx = 5L in (* Section index of .dynstr *) (* let hashndx = 6L in *) (* Section index of .hash *) - (* let pltndx = 7L in *) (* Section index of .plt *) + let pltndx = 7L in (* Section index of .plt *) (* let gotpltndx = 8L in *) (* Section index of .got.plt *) (* let relapltndx = 9L in *) (* Section index of .rela.plt *) let datandx = 10L in (* Section index of .data *) @@ -690,155 +697,129 @@ let elf32_linux_x86_file (section_header ~shstring_table_fixup: shstrtab_section_fixup ~shname_string_fixup: null_section_name_fixup - ~sh_type: SHT_NULL - ~sh_flags: [] ~section_fixup: None ~sh_addralign: 0L - ~sh_entsize: 0L - ~sh_link: None); + SHT_NULL); (* .interp *) (section_header ~shstring_table_fixup: shstrtab_section_fixup ~shname_string_fixup: interp_section_name_fixup - ~sh_type: SHT_PROGBITS ~sh_flags: [ SHF_ALLOC ] ~section_fixup: (Some interp_section_fixup) - ~sh_addralign: 1L - ~sh_entsize: 0L - ~sh_link: None); + SHT_PROGBITS); (* .text *) (section_header ~shstring_table_fixup: shstrtab_section_fixup ~shname_string_fixup: text_section_name_fixup - ~sh_type: SHT_PROGBITS ~sh_flags: [ SHF_ALLOC; SHF_EXECINSTR ] ~section_fixup: (Some text_section_fixup) ~sh_addralign: 32L - ~sh_entsize: 0L - ~sh_link: None); + SHT_PROGBITS); (* .rodata *) (section_header ~shstring_table_fixup: shstrtab_section_fixup ~shname_string_fixup: rodata_section_name_fixup - ~sh_type: SHT_PROGBITS ~sh_flags: [ SHF_ALLOC ] ~section_fixup: (Some rodata_section_fixup) ~sh_addralign: 32L - ~sh_entsize: 0L - ~sh_link: None); + SHT_PROGBITS); (* .dynsym *) (section_header ~shstring_table_fixup: shstrtab_section_fixup ~shname_string_fixup: dynsym_section_name_fixup - ~sh_type: SHT_DYNSYM ~sh_flags: [ SHF_ALLOC ] ~section_fixup: (Some dynsym_section_fixup) - ~sh_addralign: 8L + ~sh_addralign: 4L ~sh_entsize: elf32_symsize - ~sh_link: (Some dynstrndx) ); + ~sh_link: (Some dynstrndx) + SHT_DYNSYM); (* .dynstr *) (section_header ~shstring_table_fixup: shstrtab_section_fixup ~shname_string_fixup: dynstr_section_name_fixup - ~sh_type: SHT_STRTAB ~sh_flags: [ SHF_ALLOC ] ~section_fixup: (Some dynstr_section_fixup) - ~sh_addralign: 1L - ~sh_entsize: 0L - ~sh_link: None); + SHT_STRTAB); (* .hash *) (section_header ~shstring_table_fixup: shstrtab_section_fixup ~shname_string_fixup: hash_section_name_fixup - ~sh_type: SHT_PROGBITS ~sh_flags: [ SHF_ALLOC ] ~section_fixup: (Some hash_section_fixup) ~sh_addralign: 4L ~sh_entsize: 4L - ~sh_link: (Some dynsymndx)); + SHT_PROGBITS); (* .plt *) (section_header ~shstring_table_fixup: shstrtab_section_fixup ~shname_string_fixup: plt_section_name_fixup - ~sh_type: SHT_PROGBITS ~sh_flags: [ SHF_ALLOC; SHF_EXECINSTR ] ~section_fixup: (Some plt_section_fixup) ~sh_addralign: 4L - ~sh_entsize: 0L - ~sh_link: None); + SHT_PROGBITS); (* .got.plt *) (section_header ~shstring_table_fixup: shstrtab_section_fixup ~shname_string_fixup: got_plt_section_name_fixup - ~sh_type: SHT_PROGBITS ~sh_flags: [ SHF_ALLOC; SHF_WRITE ] ~section_fixup: (Some got_plt_section_fixup) ~sh_addralign: 4L - ~sh_entsize: 0L - ~sh_link: None); + SHT_PROGBITS); (* .rela.plt *) (section_header ~shstring_table_fixup: shstrtab_section_fixup ~shname_string_fixup: rela_plt_section_name_fixup - ~sh_type: SHT_RELA ~sh_flags: [ SHF_ALLOC ] ~section_fixup: (Some rela_plt_section_fixup) ~sh_addralign: 4L ~sh_entsize: elf32_rela_entsz - ~sh_link: (Some dynsymndx)); + ~sh_link: (Some dynsymndx) + ~sh_info: (Some pltndx) + SHT_RELA); (* .data *) (section_header ~shstring_table_fixup: shstrtab_section_fixup ~shname_string_fixup: data_section_name_fixup - ~sh_type: SHT_PROGBITS ~sh_flags: [ SHF_ALLOC; SHF_WRITE ] ~section_fixup: (Some data_section_fixup) ~sh_addralign: 32L - ~sh_entsize: 0L - ~sh_link: None); + SHT_PROGBITS); (* .bss *) (section_header ~shstring_table_fixup: shstrtab_section_fixup ~shname_string_fixup: bss_section_name_fixup - ~sh_type: SHT_NOBITS ~sh_flags: [ SHF_ALLOC; SHF_WRITE ] ~section_fixup: (Some bss_section_fixup) ~sh_addralign: 32L - ~sh_entsize: 0L - ~sh_link: None); + SHT_NOBITS); (* .dynamic *) (section_header ~shstring_table_fixup: shstrtab_section_fixup ~shname_string_fixup: dynamic_section_name_fixup - ~sh_type: SHT_DYNAMIC ~sh_flags: [ SHF_ALLOC; SHF_WRITE ] ~section_fixup: (Some dynamic_section_fixup) ~sh_addralign: 8L - ~sh_entsize: 0L - ~sh_link: None); + ~sh_link: (Some dynstrndx) + SHT_DYNAMIC); (* .shstrtab *) (section_header ~shstring_table_fixup: shstrtab_section_fixup ~shname_string_fixup: shstrtab_section_name_fixup - ~sh_type: SHT_STRTAB - ~sh_flags: [] ~section_fixup: (Some shstrtab_section_fixup) - ~sh_addralign: 1L - ~sh_entsize: 0L - ~sh_link: None); + SHT_STRTAB); (* FIXME: uncomment the dwarf section headers as you make use of them; @@ -852,58 +833,45 @@ let elf32_linux_x86_file (section_header ~shstring_table_fixup: shstrtab_section_fixup ~shname_string_fixup: debug_aranges_section_name_fixup - ~sh_type: SHT_PROGBITS - ~sh_flags: [] ~section_fixup: (Some sem.Semant.ctxt_debug_aranges_fixup) ~sh_addralign: 8L - ~sh_entsize: 0L - ~sh_link: None); + ~zero_sh_addr: true + SHT_PROGBITS); *) (* .debug_pubnames *) (* (section_header ~shstring_table_fixup: shstrtab_section_fixup ~shname_string_fixup: debug_pubnames_section_name_fixup - ~sh_type: SHT_PROGBITS - ~sh_flags: [] ~section_fixup: (Some sem.Semant.ctxt_debug_pubnames_fixup) - ~sh_addralign: 1L - ~sh_entsize: 0L - ~sh_link: None); + ~zero_sh_addr: true + SHT_PROGBITS); *) (* .debug_info *) (section_header ~shstring_table_fixup: shstrtab_section_fixup ~shname_string_fixup: debug_info_section_name_fixup - ~sh_type: SHT_PROGBITS - ~sh_flags: [] ~section_fixup: (Some sem.Semant.ctxt_debug_info_fixup) - ~sh_addralign: 1L - ~sh_entsize: 0L - ~sh_link: None); + ~zero_sh_addr: true + SHT_PROGBITS); (* .debug_abbrev *) (section_header ~shstring_table_fixup: shstrtab_section_fixup ~shname_string_fixup: debug_abbrev_section_name_fixup - ~sh_type: SHT_PROGBITS - ~sh_flags: [] ~section_fixup: (Some sem.Semant.ctxt_debug_abbrev_fixup) - ~sh_addralign: 1L - ~sh_entsize: 0L - ~sh_link: None); + ~zero_sh_addr: true + SHT_PROGBITS); + (* .debug_line *) (* (section_header ~shstring_table_fixup: shstrtab_section_fixup ~shname_string_fixup: debug_line_section_name_fixup - ~sh_type: SHT_PROGBITS - ~sh_flags: [] ~section_fixup: (Some sem.Semant.ctxt_debug_line_fixup) - ~sh_addralign: 1L - ~sh_entsize: 0L - ~sh_link: None); + ~zero_sh_addr: true + SHT_PROGBITS); *) (* .debug_frame *) @@ -911,24 +879,18 @@ let elf32_linux_x86_file (section_header ~shstring_table_fixup: shstrtab_section_fixup ~shname_string_fixup: debug_frame_section_name_fixup - ~sh_type: SHT_PROGBITS - ~sh_flags: [] ~section_fixup: (Some sem.Semant.ctxt_debug_frame_fixup) ~sh_addralign: 4L - ~sh_entsize: 0L - ~sh_link: None); + ~zero_sh_addr: true + SHT_PROGBITS); *) (* .note.rust *) (section_header ~shstring_table_fixup: shstrtab_section_fixup ~shname_string_fixup: note_rust_section_name_fixup - ~sh_type: SHT_NOTE - ~sh_flags: [] ~section_fixup: (Some note_rust_section_fixup) - ~sh_addralign: 1L - ~sh_entsize: 0L - ~sh_link: None); + SHT_NOTE); |] in @@ -999,7 +961,7 @@ let elf32_linux_x86_file elf32_header ~sess ~ei_data: ELFDATA2LSB - ~e_type: ET_DYN + ~e_type: (if sess.Session.sess_library_mode then ET_DYN else ET_EXEC) ~e_machine: EM_386 ~e_version: EV_CURRENT diff --git a/src/boot/be/il.ml b/src/boot/be/il.ml index 172d8661..2a5b643a 100644 --- a/src/boot/be/il.ml +++ b/src/boot/be/il.ml @@ -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/pe.ml b/src/boot/be/pe.ml index d360ddf5..b85cb1a9 100644 --- a/src/boot/be/pe.ml +++ b/src/boot/be/pe.ml @@ -783,9 +783,35 @@ let crate_exports (sem:Semant.ctxt) : pe_export array = let export_seg (_, tab) = Array.of_list (List.map export_sym (htab_pairs tab)) in + + (* Make some fake symbol table entries to aid in debugging. *) + let export_stab name fixup = + { + pe_export_name_fixup = new_fixup "export name fixup"; + pe_export_name = "rust$" ^ name; + pe_export_address_fixup = fixup + } + in + let export_stab_of_item (node_id, code) = + let name = Hashtbl.find sem.Semant.ctxt_all_item_names node_id in + let name' = "item$" ^ (Semant.string_of_name name) in + export_stab name' code.Semant.code_fixup + in + let export_stab_of_glue (glue, code) = + export_stab (Semant.glue_str sem glue) code.Semant.code_fixup + in + + let stabs = + Array.of_list (List.concat [ + (List.map export_stab_of_item + (htab_pairs sem.Semant.ctxt_all_item_code)); + (List.map export_stab_of_glue (htab_pairs sem.Semant.ctxt_glue_code)) + ]) + in + Array.concat - (List.map export_seg - (htab_pairs sem.Semant.ctxt_native_provided)) + (stabs::(List.map export_seg + (htab_pairs sem.Semant.ctxt_native_provided))) ;; diff --git a/src/boot/be/x86.ml b/src/boot/be/x86.ml index 55b101bb..f879027b 100644 --- a/src/boot/be/x86.ml +++ b/src/boot/be/x86.ml @@ -303,7 +303,7 @@ let emit_target_specific 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 rhs in - (* Horrible: we bounce complex mul inputs off spill slots + (* Horrible: we bounce mul/div/mod inputs off spill slots * to ensure non-interference between the temporaries used * during mem-base-reg reloads and the registers we're * preparing. *) @@ -311,32 +311,26 @@ let emit_target_specific 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 + let is_eax cell = + match cell with + Il.Cell (Il.Reg (Il.Hreg hr, _)) -> hr = eax | _ -> 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); - + in + if is_eax lhs + then + mov rhs_ecx rhs + else + begin + let lhs_spill = next_spill_like lhs in + let rhs_spill = next_spill_like rhs in + + mov lhs_spill lhs; + mov rhs_spill rhs; + + mov lhs_eax (Il.Cell lhs_spill); + mov rhs_ecx (Il.Cell rhs_spill); + end; + put (Il.Binary { b with Il.binary_lhs = (Il.Cell lhs_eax); @@ -344,7 +338,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 @@ -593,6 +587,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 ] @@ -1033,7 +1028,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 @@ -1045,11 +1040,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) @@ -1061,15 +1093,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) -> @@ -1185,6 +1225,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 @@ -1314,7 +1355,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) diff --git a/src/boot/driver/session.ml b/src/boot/driver/session.ml index d9e57b02..d295763a 100644 --- a/src/boot/driver/session.ml +++ b/src/boot/driver/session.ml @@ -93,7 +93,7 @@ let string_of_pos (p:pos) = let string_of_span (s:span) = let (filename, line0, col0) = s.lo in let (_, line1, col1) = s.hi in - Printf.sprintf "%s:%d:%d - %d:%d" filename line0 col0 line1 col1 + Printf.sprintf "%s:%d:%d:%d:%d" filename line0 col0 line1 col1 ;; let filename_of (fo:filename option) : filename = @@ -111,7 +111,7 @@ let report_err sess ido str = None -> fail sess "Error: %s\n%!" str | Some span -> - fail sess "%s:E:Error: %s\n%!" + fail sess "%s: error: %s\n%!" (string_of_span span) str ;; diff --git a/src/boot/fe/ast.ml b/src/boot/fe/ast.ml index 3f3d5145..79ff2c7c 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,7 @@ 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 +1272,13 @@ 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 +1286,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,18 +1300,17 @@ 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 ((ident, slot), block) = type_arm.node in let fmt_type_arm_case (ff:Format.formatter) = fmt_slot ff slot; fmt ff " "; fmt_ident ff ident in fmt_arm ff fmt_type_arm_case 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; @@ -1320,7 +1320,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 @@ -1351,9 +1350,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/cexp.ml b/src/boot/fe/cexp.ml index fc849b28..9c1b40e1 100644 --- a/src/boot/fe/cexp.ml +++ b/src/boot/fe/cexp.ml @@ -310,7 +310,7 @@ type cdir = | CDIR_mod of (Ast.ident * Ast.mod_item) | CDIR_auth of auth -type env = { env_bindings: (Ast.ident * pval) list; +type env = { env_bindings: ((Ast.ident * pval) list) ref; env_prefix: filename list; env_items: (filename, Ast.mod_items) Hashtbl.t; env_files: (node_id,filename) Hashtbl.t; @@ -357,10 +357,11 @@ and eval_cexp (env:env) (exp:cexp) : cdir array = | CEXP_let {node=cl} -> let ident = cl.let_ident in let v = eval_pexp env cl.let_value in - let env = { env with - env_bindings = ((ident,v)::env.env_bindings ) } - in - eval_cexps env cl.let_body + let old_bindings = !(env.env_bindings) in + env.env_bindings := (ident,v)::old_bindings; + let res = eval_cexps env cl.let_body in + env.env_bindings := old_bindings; + res | CEXP_src_mod {node=s; id=id} -> let name = s.src_ident in @@ -381,6 +382,7 @@ and eval_cexp (env:env) (exp:cexp) : cdir array = ps.pstate_opaque_id ps.pstate_sess ps.pstate_get_mod + ps.pstate_get_cenv_tok ps.pstate_infer_lib_name env.env_required env.env_required_syms @@ -518,7 +520,7 @@ and eval_pexp (env:env) (exp:Pexp.pexp) : pval = | Pexp.PEXP_lval (Pexp.PLVAL_ident ident) -> begin - match ltab_search env.env_bindings ident with + match ltab_search !(env.env_bindings) ident with None -> raise (err (Printf.sprintf "no binding for '%s' found" ident) env.env_ps) | Some v -> v @@ -622,11 +624,6 @@ let parse_crate_file let oref = ref (Opaque 0) in let required = Hashtbl.create 4 in let required_syms = Hashtbl.create 4 in - let ps = - make_parser tref nref oref sess get_mod - infer_lib_name required required_syms fname - in - let files = Hashtbl.create 0 in let items = Hashtbl.create 4 in let target_bindings = @@ -648,11 +645,23 @@ let parse_crate_file ("build_input", PVAL_str fname); ] in - let initial_bindings = - target_bindings - @ build_bindings + let bindings = + ref (target_bindings + @ build_bindings) in - let env = { env_bindings = initial_bindings; + let get_cenv_tok ps ident = + match ltab_search (!bindings) ident with + None -> raise (err (Printf.sprintf "no binding for '%s' found" + ident) ps) + | Some (PVAL_bool b) -> LIT_BOOL b + | Some (PVAL_str s) -> LIT_STR s + | Some (PVAL_num n) -> LIT_INT n + in + let ps = + make_parser tref nref oref sess get_mod get_cenv_tok + infer_lib_name required required_syms fname + in + let env = { env_bindings = bindings; env_prefix = [Filename.dirname fname]; env_items = Hashtbl.create 0; env_files = files; @@ -720,8 +729,12 @@ let parse_src_file let oref = ref (Opaque 0) in let required = Hashtbl.create 0 in let required_syms = Hashtbl.create 0 in + let get_cenv_tok ps ident = + raise (err (Printf.sprintf "no binding for '%s' found" + ident) ps) + in let ps = - make_parser tref nref oref sess get_mod + make_parser tref nref oref sess get_mod get_cenv_tok infer_lib_name required required_syms fname in with_err_handling sess diff --git a/src/boot/fe/item.ml b/src/boot/fe/item.ml index 67a482a6..a47fca5a 100644 --- a/src/boot/fe/item.ml +++ b/src/boot/fe/item.ml @@ -556,7 +556,11 @@ and parse_stmts (ps:pstate) : Ast.stmt array = let stmts = expand_tags_to_stmts ps item in spans ps stmts apos (Ast.STMT_decl decl) - | _ -> + | token -> + if token = SPAWN then + prerr_endline ("warning: \"spawn\" with unused result spawns a " ^ + "task that immediately dies"); + let (lstmts, lval) = ctxt "stmt: lval" parse_lval ps in let stmts = match peek ps with @@ -834,9 +838,17 @@ and parse_mod_item (ps:pstate) : (Ast.ident * Ast.mod_item) = EQ -> begin bump ps; - match peek ps with - LIT_STR s -> (bump ps; s) - | _ -> raise (unexpected ps) + let do_tok t = + bump ps; + match t with + LIT_STR s -> s + | _ -> raise (unexpected ps) + in + match peek ps with + IDENT i -> + do_tok (ps.pstate_get_cenv_tok ps i) + | t -> + do_tok t end | _ -> ps.pstate_infer_lib_name ident in @@ -888,14 +900,14 @@ and parse_mod_item (ps:pstate) : (Ast.ident * Ast.mod_item) = | _ -> CONV_cdecl in expect ps MOD; - let (ident, params) = parse_ident_and_params ps "native mod" in + let ident = Pexp.parse_ident ps in let path = parse_lib_name ident in let items = parse_mod_items_from_signature ps in let bpos = lexpos ps in let rlib = REQUIRED_LIB_c { required_libname = path; required_prefix = ps.pstate_depth } in - let item = decl params (Ast.MOD_ITEM_mod items) in + let item = decl [||] (Ast.MOD_ITEM_mod items) in let item = span ps apos bpos item in note_required_mod ps {lo=apos; hi=bpos} conv rlib item; (ident, item) diff --git a/src/boot/fe/lexer.mll b/src/boot/fe/lexer.mll index ed548b1e..af8eab6a 100644 --- a/src/boot/fe/lexer.mll +++ b/src/boot/fe/lexer.mll @@ -27,8 +27,12 @@ <- (bump_line lexbuf.Lexing.lex_curr_p) ;; - let mach_suf_table = Hashtbl.create 0 + let mach_suf_table = Hashtbl.create 10 ;; + + let reserved_suf_table = Hashtbl.create 10 + ;; + let _ = List.iter (fun (suf, ty) -> Common.htab_put mach_suf_table suf ty) [ ("u8", Common.TY_u8); @@ -43,8 +47,24 @@ ("f64", Common.TY_f64); ] ;; + let _ = + List.iter (fun suf -> Common.htab_put reserved_suf_table suf ()) + [ "f16"; (* IEEE 754-2008 'binary16' interchange format. *) + "f80"; (* IEEE 754-1985 'extended' *) + "f128"; (* IEEE 754-2008 'binary128' *) + "m32"; (* IEEE 754-2008 'decimal32' *) + "m64"; (* IEEE 754-2008 'decimal64' *) + "m128"; (* IEEE 754-2008 'decimal128' *) + "m"; (* One of m32, m64, m128. *) + ] + ;; + let keyword_table = Hashtbl.create 100 ;; + + let reserved_table = Hashtbl.create 10 + ;; + let _ = List.iter (fun (kwd, tok) -> Common.htab_put keyword_table kwd tok) [ ("mod", MOD); @@ -141,6 +161,19 @@ ("f64", MACH TY_f64) ] ;; + + let _ = + List.iter (fun kwd -> Common.htab_put reserved_table kwd ()) + [ "f16"; (* IEEE 754-2008 'binary16' interchange format. *) + "f80"; (* IEEE 754-1985 'extended' *) + "f128"; (* IEEE 754-2008 'binary128' *) + "m32"; (* IEEE 754-2008 'decimal32' *) + "m64"; (* IEEE 754-2008 'decimal64' *) + "m128"; (* IEEE 754-2008 'decimal128' *) + "dec"; (* One of m32, m64, m128. *) + ]; + ;; + } let hexdig = ['0'-'9' 'a'-'f' 'A'-'F'] @@ -153,6 +186,7 @@ let flo = (dec '.' dec (exp?)) | (dec exp) let mach_float_suf = "f32"|"f64" let mach_int_suf = ['u''i']('8'|"16"|"32"|"64") +let flo_suf = ['m''f']("16"|"32"|"64"|"80"|"128") let ws = [ ' ' '\t' '\r' ] @@ -218,26 +252,39 @@ rule token = parse | ']' { RBRACKET } | id as i - { try - Hashtbl.find keyword_table i - with - Not_found -> IDENT (i) } + { + match Common.htab_search keyword_table i with + Some tok -> tok + | None -> + if Hashtbl.mem reserved_table i + then fail lexbuf "reserved keyword" + else IDENT (i) + } | (bin|hex|dec) as n { LIT_INT (Int64.of_string n) } | ((bin|hex|dec) as n) 'u' { LIT_UINT (Int64.of_string n) } | ((bin|hex|dec) as n) - (mach_int_suf as s) { try - let tm = - Hashtbl.find mach_suf_table s - in - LIT_MACH_INT - (tm, Int64.of_string n) - with - Not_found -> - fail lexbuf - "bad mach-int suffix" } + (mach_int_suf as s) + { + match Common.htab_search mach_suf_table s with + Some tm -> LIT_MACH_INT (tm, Int64.of_string n) + | None -> + if Hashtbl.mem reserved_suf_table s + then fail lexbuf "reserved mach-int suffix" + else fail lexbuf "bad mach-int suffix" + } | flo as n { LIT_FLOAT (float_of_string n) } +| flo 'm' { fail lexbuf "reseved mach-float suffix" } +| (flo as n) (flo_suf as s) + { + match Common.htab_search mach_suf_table s with + Some tm -> LIT_MACH_FLOAT (tm, float_of_string n) + | None -> + if Hashtbl.mem reserved_suf_table s + then fail lexbuf "reserved mach-float suffix" + else fail lexbuf "bad mach-float suffix" + } | '\'' { char lexbuf } | '"' { let buf = Buffer.create 32 in @@ -411,3 +458,13 @@ and comment depth = parse comment depth lexbuf } | _ { comment depth lexbuf } + + +(* + * Local Variables: + * fill-column: 78; + * indent-tabs-mode: nil + * buffer-file-coding-system: utf-8-unix + * compile-command: "make -k -C ../.. 2>&1 | sed -e 's/\\/x\\//x:\\//g'"; + * End: + *) diff --git a/src/boot/fe/parser.ml b/src/boot/fe/parser.ml index ab7ff56c..4add7b01 100644 --- a/src/boot/fe/parser.ml +++ b/src/boot/fe/parser.ml @@ -23,6 +23,7 @@ type pstate = pstate_node_id : node_id ref; pstate_opaque_id : opaque_id ref; pstate_get_mod : get_mod_fn; + pstate_get_cenv_tok : pstate -> Ast.ident -> token; pstate_infer_lib_name : (Ast.ident -> filename); pstate_required : (node_id, (required_lib * nabi_conv)) Hashtbl.t; pstate_required_syms : (node_id, string) Hashtbl.t; } @@ -45,6 +46,7 @@ let make_parser (oref:opaque_id ref) (sess:Session.sess) (get_mod:get_mod_fn) + (get_cenv_tok:pstate -> Ast.ident -> token) (infer_lib_name:Ast.ident -> filename) (required:(node_id, (required_lib * nabi_conv)) Hashtbl.t) (required_syms:(node_id, string) Hashtbl.t) @@ -68,6 +70,7 @@ let make_parser pstate_node_id = nref; pstate_opaque_id = oref; pstate_get_mod = get_mod; + pstate_get_cenv_tok = get_cenv_tok; pstate_infer_lib_name = infer_lib_name; pstate_required = required; pstate_required_syms = required_syms; } diff --git a/src/boot/fe/pexp.ml b/src/boot/fe/pexp.ml index 3e17e0e4..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 @@ -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/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..552b41e4 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 @@ -2176,14 +2176,8 @@ let dwarf_visitor in let addr_ranges (fix:fixup) : frag = - let image_is_relocated = - match cx.ctxt_sess.Session.sess_targ with - Win32_x86_pe -> - cx.ctxt_sess.Session.sess_library_mode - | _ -> true - in let lo = - if image_is_relocated + if cx.ctxt_sess.Session.sess_library_mode then image_base_rel fix else M_POS fix in @@ -2801,6 +2795,7 @@ let rec extract_meta queue_to_arr meta ;; +let external_opaques = Hashtbl.create 0;; let rec extract_mod_items (nref:node_id ref) @@ -2822,7 +2817,6 @@ let rec extract_mod_items id in - let external_opaques = Hashtbl.create 0 in let get_opaque_of o = htab_search_or_add external_opaques o (fun _ -> next_opaque_id()) 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/resolve.ml b/src/boot/me/resolve.ml index bf11ad23..25eb544a 100644 --- a/src/boot/me/resolve.ml +++ b/src/boot/me/resolve.ml @@ -48,6 +48,7 @@ let stmt_collecting_visitor : Walk.visitor = let block_ids = Stack.create () in let visit_block_pre (b:Ast.block) = + htab_put cx.ctxt_all_blocks b.id b.node; Stack.push b.id block_ids; inner.Walk.visit_block_pre b in diff --git a/src/boot/me/semant.ml b/src/boot/me/semant.ml index bcaec2b4..7d1b21ef 100644 --- a/src/boot/me/semant.ml +++ b/src/boot/me/semant.ml @@ -98,6 +98,7 @@ type ctxt = ctxt_all_cast_types: (node_id,Ast.ty) Hashtbl.t; ctxt_all_type_items: (node_id,Ast.ty) Hashtbl.t; ctxt_all_stmts: (node_id,Ast.stmt) Hashtbl.t; + ctxt_all_blocks: (node_id,Ast.block') Hashtbl.t; ctxt_item_files: (node_id,filename) Hashtbl.t; ctxt_all_lvals: (node_id,Ast.lval) Hashtbl.t; ctxt_call_lval_params: (node_id,Ast.ty array) Hashtbl.t; @@ -183,6 +184,7 @@ let new_ctxt sess abi crate = ctxt_all_cast_types = Hashtbl.create 0; ctxt_all_type_items = Hashtbl.create 0; ctxt_all_stmts = Hashtbl.create 0; + ctxt_all_blocks = Hashtbl.create 0; ctxt_item_files = crate.Ast.crate_files; ctxt_all_lvals = Hashtbl.create 0; ctxt_all_defns = Hashtbl.create 0; @@ -1012,7 +1014,8 @@ let type_is_unsigned_2s_complement t = | Ast.TY_mach TY_u64 | Ast.TY_char | Ast.TY_uint - | Ast.TY_bool -> true + | Ast.TY_bool + | Ast.TY_native _ -> true | _ -> false ;; @@ -1822,24 +1825,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 +1852,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 +1919,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 +1931,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 +1961,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 +1973,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 +2009,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 +2063,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 b708bb26..03174b0a 100644 --- a/src/boot/me/trans.ml +++ b/src/boot/me/trans.ml @@ -88,6 +88,7 @@ let trans_visitor let zero = imm 0L in let imm_true = imm_of_ty 1L TY_u8 in let imm_false = imm_of_ty 0L TY_u8 in + let zero_byte = imm_of_ty 0L TY_u8 in let nil_ptr = Il.Mem ((Il.Abs (Asm.IMM 0L)), Il.NilTy) in let wordptr_ty = Il.AddrTy (Il.ScalarTy word_sty) in @@ -181,7 +182,7 @@ let trans_visitor match q with Il.Jmp _ -> flush_emitter_size_cache(); | _ -> () - end; + end; Il.emit (emitter()) q in @@ -292,7 +293,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 = @@ -330,11 +331,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) @@ -453,13 +450,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) @@ -469,11 +475,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 _ = @@ -520,34 +558,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 = @@ -721,7 +735,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 @@ -730,7 +744,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 @@ -931,7 +945,7 @@ let trans_visitor 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 @@ -1120,7 +1134,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) @@ -1226,7 +1240,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 @@ -1394,7 +1408,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 @@ -1473,8 +1487,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 *) @@ -2128,10 +2142,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 _ = @@ -2165,7 +2181,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 [| @@ -2177,7 +2193,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 [| @@ -2243,8 +2259,17 @@ let trans_visitor trans_void_upcall "upcall_join" [| trans_atom (Ast.ATOM_lval task) |] and trans_send (chan:Ast.lval) (src:Ast.lval) : unit = - let (srccell, _) = trans_lval src in - aliasing false srccell + let (src_cell, src_ty) = trans_lval src in + begin + match (ty_mem_ctrl src_ty) with + | MEM_rc_opaque + | MEM_rc_struct + | MEM_gc -> + iflog (fun _ -> annotate "incr_refcount of src obj"); + incr_refcount src_cell; + | _ -> () + end; + aliasing false src_cell begin fun src_alias -> trans_void_upcall "upcall_send" @@ -2331,7 +2356,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 @@ -2377,12 +2402,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 @@ -2418,7 +2443,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 @@ -2435,7 +2460,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) @@ -2532,7 +2557,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; @@ -2932,6 +2957,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 @@ -4188,6 +4214,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 @@ -4290,7 +4335,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; @@ -4345,18 +4390,18 @@ let trans_visitor (src_ty:Ast.ty) : unit = let elt_ty = seq_unit_ty dst_ty in - let trim_trailing_null = dst_ty = Ast.TY_str in - assert (simplified_ty src_ty = simplified_ty dst_ty); - match simplified_ty src_ty with - Ast.TY_str - | Ast.TY_vec _ -> + let trailing_null = simplified_ty dst_ty = Ast.TY_str in + match (simplified_ty dst_ty, simplified_ty src_ty) with + (Ast.TY_str, Ast.TY_str) + | (Ast.TY_vec _, Ast.TY_vec _) + when (simplified_ty dst_ty) = (simplified_ty src_ty) -> let is_gc = if type_has_state src_ty then 1L else 0L in let src_cell = need_cell src_oper in let src_vec = deref src_cell in let src_fill = get_element_ptr src_vec Abi.vec_elt_fill in let dst_vec = deref dst_cell in let dst_fill = get_element_ptr dst_vec Abi.vec_elt_fill in - if trim_trailing_null + if trailing_null then sub_from dst_fill (imm 1L); trans_upcall "upcall_vec_grow" dst_cell @@ -4374,7 +4419,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 @@ -4413,9 +4458,53 @@ let trans_visitor let v = next_vreg_cell word_sty in mov v (Il.Cell src_fill); add_to dst_fill (Il.Cell v); - | t -> + + | (Ast.TY_str, e) + | (Ast.TY_vec _, e) + when e = simplified_ty elt_ty -> + + let dst_is_gc = if type_has_state dst_ty then 1L else 0L in + let elt_sz = ty_sz_in_current_frame elt_ty in + trans_upcall "upcall_vec_grow" + dst_cell + [| Il.Cell dst_cell; + elt_sz; + imm dst_is_gc |]; + + (* + * By now, dst_cell points to a vec/str with room for us + * to add to. + *) + + (* Reload dst vec, fill; might have changed. *) + let dst_vec = deref dst_cell in + let dst_fill = get_element_ptr dst_vec Abi.vec_elt_fill in + + let eltp_rty = Il.AddrTy (referent_type word_bits elt_ty) in + let dptr = next_vreg_cell eltp_rty in + let dst_data = + get_element_ptr_dyn_in_current_frame + dst_vec Abi.vec_elt_data + in + lea dptr (fst (need_mem_cell dst_data)); + add_to dptr (Il.Cell dst_fill); + if trailing_null + then sub_from dptr elt_sz; + trans_copy_ty + (get_ty_params_of_current_frame()) true + (deref dptr) elt_ty + (Il.Mem (force_to_mem src_oper)) elt_ty + None; + add_to dptr elt_sz; + if trailing_null + then mov (deref dptr) zero_byte; + add_to dst_fill elt_sz; + + | _ -> begin - bug () "unsupported vector-append type %a" Ast.sprintf_ty t + bug () "unsupported vector-append types %a += %a" + Ast.sprintf_ty dst_ty + Ast.sprintf_ty src_ty end @@ -4496,8 +4585,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 @@ -4693,6 +4783,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 @@ -4700,10 +4792,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 @@ -4751,7 +4852,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; @@ -4765,7 +4866,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 (); @@ -4789,8 +4890,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 @@ -4799,7 +4901,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 @@ -4818,7 +4920,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 @@ -4920,7 +5022,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 = @@ -5058,7 +5160,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 @@ -5121,7 +5223,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 @@ -5155,7 +5257,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; @@ -5165,7 +5267,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 23210ea1..17a4b38f 100644 --- a/src/boot/me/type.ml +++ b/src/boot/me/type.ml @@ -101,11 +101,11 @@ let check_stmt (cx:Semant.ctxt) : (fn_ctx -> Ast.stmt -> unit) = if not (is_integer (fundamental_ty actual)) then type_error "integer" actual in - let demand_bool_or_char_or_integer (actual:Ast.ty) : unit = + let demand_bool_or_char_or_integer_or_native (actual:Ast.ty) : unit = match fundamental_ty actual with - Ast.TY_bool | Ast.TY_char -> () + Ast.TY_bool | Ast.TY_char | Ast.TY_native _ -> () | ty when is_integer ty -> () - | _ -> type_error "bool, char, or integer" actual + | _ -> type_error "bool, char, integer or native" actual in let demand_number (actual:Ast.ty) : unit = match fundamental_ty actual with @@ -634,9 +634,11 @@ let check_stmt (cx:Semant.ctxt) : (fn_ctx -> Ast.stmt -> unit) = ty | Ast.EXPR_unary (Ast.UNOP_cast dst_ty_id, atom) -> (* TODO: probably we want to handle more cases here *) - demand_bool_or_char_or_integer (check_atom atom); - let dst_ty = dst_ty_id.Common.node in - demand_bool_or_char_or_integer dst_ty; + demand_bool_or_char_or_integer_or_native (check_atom atom); + let dst_ty = + Hashtbl.find cx.Semant.ctxt_all_cast_types dst_ty_id.Common.id + in + demand_bool_or_char_or_integer_or_native dst_ty; dst_ty in @@ -692,7 +694,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) @@ -761,6 +763,27 @@ let check_stmt (cx:Semant.ctxt) : (fn_ctx -> Ast.stmt -> unit) = | Ast.STMT_copy (dst, src) -> infer_lval (check_expr src) dst + | Ast.STMT_copy_binop (dst, Ast.BINOP_add, src) -> + begin + let src_ty = check_atom ~deref:true src in + let dst_ty = check_lval dst in + match fundamental_ty dst_ty, fundamental_ty src_ty with + Ast.TY_vec elt1, Ast.TY_vec elt2 + | Ast.TY_vec elt1, elt2 -> + if elt1 = elt2 + then () + else + Common.err None + "mismatched types in vec-append: %a += %a" + Ast.sprintf_ty dst_ty + Ast.sprintf_ty src_ty + | Ast.TY_str, (Ast.TY_mach Common.TY_u8) + | Ast.TY_str, Ast.TY_str -> () + | _ -> + infer_lval src_ty dst; + demand src_ty (check_binop Ast.BINOP_add src_ty) + end + | Ast.STMT_copy_binop (dst, binop, src) -> let ty = check_atom ~deref:true src in infer_lval ty dst; diff --git a/src/boot/me/typestate.ml b/src/boot/me/typestate.ml index baf4a543..8f8d7179 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 @@ -697,7 +697,8 @@ let condition_assigning_visitor | Ast.STMT_while sw -> let (_, expr) = sw.Ast.while_lval in let precond = slot_inits (expr_slots cx expr) in - raise_pre_post_cond s.id precond + raise_precondition sw.Ast.while_body.id precond; + raise_postcondition sw.Ast.while_body.id precond | Ast.STMT_alt_tag at -> let precond = slot_inits (lval_slots cx at.Ast.alt_tag_lval) in @@ -947,16 +948,17 @@ let graph_special_block_structure_building_visitor let ts = tables () in let graph = ts.ts_graph in let cond_id = s.id in + let succ = Hashtbl.find graph cond_id in let then_id = sif.Ast.if_then.id in let then_end_id = last_id_or_block_id sif.Ast.if_then in let show_node = show_node cx graph in + let succ = List.filter (fun x -> not (x = then_id)) succ in show_node "initial cond" cond_id; show_node "initial then" then_id; show_node "initial then_end" then_end_id; begin match sif.Ast.if_else with None -> - let succ = Hashtbl.find graph then_end_id in Hashtbl.replace graph cond_id (then_id :: succ); (* Kill residual messed-up block wiring.*) remove_flow_edges graph then_end_id [then_id]; @@ -966,8 +968,10 @@ let graph_special_block_structure_building_visitor | Some e -> let else_id = e.id in + let succ = + List.filter (fun x -> not (x = else_id)) succ + in let else_end_id = last_id_or_block_id e in - let succ = Hashtbl.find graph else_end_id in show_node "initial else" else_id; show_node "initial else_end" else_end_id; Hashtbl.replace graph cond_id [then_id; else_id]; @@ -1049,19 +1053,23 @@ let graph_special_block_structure_building_visitor ;; let find_roots + (cx:ctxt) (graph:(node_id, (node_id list)) Hashtbl.t) : (node_id,unit) Hashtbl.t = let roots = Hashtbl.create 0 in Hashtbl.iter (fun src _ -> Hashtbl.replace roots src ()) graph; Hashtbl.iter (fun _ dsts -> List.iter (fun d -> Hashtbl.remove roots d) dsts) graph; + iflog cx + (fun _ -> Hashtbl.iter + (fun k _ -> log cx "root: %d" (int_of_node k)) roots); roots ;; let run_dataflow (cx:ctxt) (ts:typestate_tables) : unit = let graph = ts.ts_graph in let idref = ts.ts_maxid in - let roots = find_roots graph in + let roots = find_roots cx graph in let nodes = Queue.create () in let progress = ref true in @@ -1138,9 +1146,17 @@ let run_dataflow (cx:ctxt) (ts:typestate_tables) : unit = begin fun _ -> log cx "stmt %d: '%s'" (int_of_node node) - (match htab_search cx.ctxt_all_stmts node with - None -> "??" - | Some stmt -> Fmt.fmt_to_str Ast.fmt_stmt stmt); + begin + match htab_search cx.ctxt_all_stmts node with + None -> + begin + match htab_search cx.ctxt_all_blocks node with + None -> "??" + | Some b -> + Fmt.fmt_to_str Ast.fmt_block b + end + | Some stmt -> Fmt.fmt_to_str Ast.fmt_stmt stmt + end; log cx "stmt %d:" (int_of_node node); log cx " prestate %s" (fmt_constr_bitv prestate); @@ -1227,27 +1243,35 @@ let typestate_verify_visitor let tables _ = Stack.top tables_stack in - let visit_stmt_pre s = + let check_states id = let ts = tables () in - let prestate = Hashtbl.find ts.ts_prestates s.id in - let precond = Hashtbl.find ts.ts_preconditions s.id in + let prestate = Hashtbl.find ts.ts_prestates id in + let precond = Hashtbl.find ts.ts_preconditions id in List.iter (fun i -> if not (Bits.get prestate i) then let ckey = Hashtbl.find ts.ts_constrs (Constr i) in let constr_str = fmt_constr_key cx ckey in - err (Some s.id) - "Unsatisfied precondition constraint %s at stmt %d: %s" - constr_str - (int_of_node s.id) - (Fmt.fmt_to_str Ast.fmt_stmt - (Hashtbl.find cx.ctxt_all_stmts s.id))) - (Bits.to_list precond); - inner.Walk.visit_stmt_pre s + err (Some id) + "Unsatisfied precondition constraint %s" + constr_str) + (Bits.to_list precond) + in + + let visit_stmt_pre s = + check_states s.id; + inner.Walk.visit_stmt_pre s + in + + let visit_block_pre b = + check_states b.id; + inner.Walk.visit_block_pre b in + { inner with - Walk.visit_stmt_pre = visit_stmt_pre } + Walk.visit_stmt_pre = visit_stmt_pre; + Walk.visit_block_pre = visit_block_pre } ;; let lifecycle_visitor @@ -1350,7 +1374,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/boot/util/common.ml b/src/boot/util/common.ml index 3a467f1c..d9b91856 100644 --- a/src/boot/util/common.ml +++ b/src/boot/util/common.ml @@ -717,7 +717,7 @@ let rec max_sz (a:size) (b:size) : size = | (SIZE_rt_max (b, c), a) when a = c -> max_sz a b | (SIZE_fixed a, SIZE_fixed b) -> SIZE_fixed (i64_max a b) | (SIZE_fixed 0L, b) when no_negs b -> b - | (a, SIZE_fixed 0L) when no_negs a -> b + | (a, SIZE_fixed 0L) when no_negs a -> a | (a, SIZE_fixed b) -> max_sz (SIZE_fixed b) a | (a, b) when a = b -> a | (a, b) -> SIZE_rt_max (a, b) diff --git a/src/comp/driver/rustc.rs b/src/comp/driver/rustc.rs index e5a614ba..42bd91c5 100644 --- a/src/comp/driver/rustc.rs +++ b/src/comp/driver/rustc.rs @@ -1,6 +1,23 @@ // -*- rust -*- -fn main(vec[str] args) -> () { +import std._str; +import lib.llvm.llvm; +import lib.llvm.builder; +import fe.parser; +import fe.token; + +fn write_module() { + auto llmod = + llvm.LLVMModuleCreateWithNameInContext(_str.buf("rust_out"), + llvm.LLVMGetGlobalContext()); + + auto b = builder(llvm.LLVMCreateBuilder()); + + llvm.LLVMWriteBitcodeToFile(llmod, _str.buf("rust_out.bc")); + llvm.LLVMDisposeModule(llmod); +} + +fn main(vec[str] args) { log "This is the rust 'self-hosted' compiler."; log "The one written in rust."; @@ -10,13 +27,36 @@ fn main(vec[str] args) -> () { auto i = 0; for (str filename in args) { if (i > 0) { - auto br = std._io.new_buf_reader(filename); - log "opened file: " + filename; - for (u8 b in br.read()) { - log b; - } + auto p = parser.new_parser(filename); + log "opened file: " + filename; + auto tok = p.peek(); + while (true) { + alt (tok) { + case (token.EOF()) { ret; } + case (_) { + log token.to_str(tok); + p.bump(); + tok = p.peek(); + } + } + } } i += 1; } + // Test LLVM module-writing. Nothing interesting yet. + write_module(); + } + + +// +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// compile-command: "make -k -C ../.. 2>&1 | sed -e 's/\\/x\\//x:\\//g'"; +// End: +// diff --git a/src/comp/fe/ast.rs b/src/comp/fe/ast.rs new file mode 100644 index 00000000..3a329ded --- /dev/null +++ b/src/comp/fe/ast.rs @@ -0,0 +1,58 @@ + +import std.util.option; +import std.map.hashmap; + +type ident = str; + +type crate = rec( str filename, + _mod module); + +type block = vec[stmt]; + +type stmt = tag( stmt_block(block), + stmt_decl(@decl), + stmt_ret(option[@lval]) ); + +type decl = tag( decl_local(ident, option[ty]), + decl_item(ident, @item) ); + +type lval = tag( lval_ident(ident), + lval_ext(@lval, ident), + lval_idx(@lval, @atom) ); + +type atom = tag( atom_lit(lit)); + +type lit = tag( lit_char(char), + lit_int(int), + lit_nil(), + lit_bool(bool) ); + +type ty = tag( ty_nil(), + ty_bool(), + ty_int(), + ty_char() ); + +type mode = tag( local(), alias() ); + +type slot = rec(ty ty, mode mode); + +type _fn = rec(vec[rec(slot slot, ident ident)] inputs, + slot output, + block body); + +type _mod = hashmap[ident,item]; + +type item = tag( item_fn(@_fn), + item_mod(@_mod) ); + + +// +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// compile-command: "make -k -C ../.. 2>&1 | sed -e 's/\\/x\\//x:\\//g'"; +// End: +// diff --git a/src/comp/fe/lexer.rs b/src/comp/fe/lexer.rs index 87809b3b..b0ee557d 100644 --- a/src/comp/fe/lexer.rs +++ b/src/comp/fe/lexer.rs @@ -1,20 +1,103 @@ -import std._io.buf_reader; +import std._io.stdio_reader; -iter buffers(buf_reader rdr) -> vec[u8] { - while (true) { - let vec[u8] v = rdr.read(); - if (std._vec.len[u8](v) == 0u) { - ret; - } - put v; - } +fn in_range(char c, char lo, char hi) -> bool { + ret lo <= c && c <= hi; +} + +fn is_alpha(char c) -> bool { + ret in_range(c, 'a', 'z') || + in_range(c, 'A', 'Z'); +} + +fn is_dec_digit(char c) -> bool { + ret in_range(c, '0', '9'); +} + +fn is_hex_digit(char c) -> bool { + ret in_range(c, '0', '9') || + in_range(c, 'a', 'f') || + in_range(c, 'A', 'F'); +} + +fn is_bin_digit(char c) -> bool { + ret c == '0' || c == '1'; +} + +fn is_whitespace(char c) -> bool { + ret c == ' ' || c == '\t' || c == '\r' || c == '\n'; } -iter bytes(buf_reader rdr) -> u8 { - for each (vec[u8] buf in buffers(rdr)) { - for (u8 b in buf) { - // FIXME: doesn't compile at the moment. - // put b; +fn next_token(stdio_reader rdr) -> token.token { + auto eof = (-1) as char; + auto c = rdr.getc() as char; + auto accum_str = ""; + auto accum_int = 0; + + while (is_whitespace(c) && c != eof) { + c = rdr.getc() as char; + } + + if (c == eof) { ret token.EOF(); } + + if (is_alpha(c)) { + while (is_alpha(c)) { + accum_str += (c as u8); + c = rdr.getc() as char; + } + rdr.ungetc(c as int); + ret token.IDENT(accum_str); + } + + if (is_dec_digit(c)) { + if (c == '0') { + } else { + while (is_dec_digit(c)) { + accum_int *= 10; + accum_int += (c as int) - ('0' as int); + c = rdr.getc() as char; + } + rdr.ungetc(c as int); + ret token.LIT_INT(accum_int); + } } - } + + // One-byte structural symbols. + alt (c) { + case (';') { ret token.SEMI(); } + case (',') { ret token.COMMA(); } + case ('.') { ret token.DOT(); } + case ('(') { ret token.LPAREN(); } + case (')') { ret token.RPAREN(); } + case ('{') { ret token.LBRACE(); } + case ('}') { ret token.RBRACE(); } + case ('[') { ret token.LBRACKET(); } + case (']') { ret token.RBRACKET(); } + case ('@') { ret token.AT(); } + case ('#') { ret token.POUND(); } + case ('=') { + auto c2 = rdr.getc() as char; + if (c2 == '=') { + ret token.OP(token.EQEQ()); + } else { + rdr.ungetc(c2 as int); + ret token.OP(token.EQ()); + } + } + } + + log "lexer stopping at "; + log c; + ret token.EOF(); } + + +// +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// compile-command: "make -k -C ../.. 2>&1 | sed -e 's/\\/x\\//x:\\//g'"; +// End: +// diff --git a/src/comp/fe/parser.rs b/src/comp/fe/parser.rs index e69de29b..02de22a7 100644 --- a/src/comp/fe/parser.rs +++ b/src/comp/fe/parser.rs @@ -0,0 +1,33 @@ +import std._io; + +state type parser = + state obj { + state fn peek() -> token.token; + state fn bump(); + }; + +fn new_parser(str path) -> parser { + state obj stdio_parser(mutable token.token tok, + _io.stdio_reader rdr) + { + state fn peek() -> token.token { + ret tok; + } + state fn bump() { + tok = lexer.next_token(rdr); + } + } + auto rdr = _io.new_stdio_reader(path); + ret stdio_parser(lexer.next_token(rdr), rdr); +} + +// +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// compile-command: "make -k -C ../.. 2>&1 | sed -e 's/\\/x\\//x:\\//g'"; +// End: +// diff --git a/src/comp/fe/token.rs b/src/comp/fe/token.rs new file mode 100644 index 00000000..5e8171bc --- /dev/null +++ b/src/comp/fe/token.rs @@ -0,0 +1,333 @@ +import util.common.ty_mach; +import util.common.ty_mach_to_str; +import std._int; +import std._uint; + +type op = tag + (PLUS(), + MINUS(), + STAR(), + SLASH(), + PERCENT(), + EQ(), + LT(), + LE(), + EQEQ(), + NE(), + GE(), + GT(), + NOT(), + TILDE(), + CARET(), + AND(), + ANDAND(), + OR(), + OROR(), + LSL(), + LSR(), + ASR()); + +type token = tag + (OP(op), + OPEQ(op), + AS(), + WITH(), + + /* Structural symbols */ + AT(), + DOT(), + COMMA(), + SEMI(), + COLON(), + RARROW(), + SEND(), + LARROW(), + LPAREN(), + RPAREN(), + LBRACKET(), + RBRACKET(), + LBRACE(), + RBRACE(), + + /* Module and crate keywords */ + MOD(), + USE(), + AUTH(), + META(), + + /* Metaprogramming keywords */ + SYNTAX(), + POUND(), + + /* Statement keywords */ + IF(), + ELSE(), + DO(), + WHILE(), + ALT(), + CASE(), + + FAIL(), + DROP(), + + IN(), + FOR(), + EACH(), + PUT(), + RET(), + BE(), + + /* Type and type-state keywords */ + TYPE(), + CHECK(), + CLAIM(), + PROVE(), + + /* Effect keywords */ + IO(), + STATE(), + UNSAFE(), + + /* Type qualifiers */ + NATIVE(), + AUTO(), + MUTABLE(), + + /* Name management */ + IMPORT(), + EXPORT(), + + /* Value / stmt declarators */ + LET(), + + /* Magic runtime services */ + LOG(), + SPAWN(), + BIND(), + THREAD(), + YIELD(), + JOIN(), + + /* Literals */ + LIT_INT(int), + LIT_UINT(uint), + LIT_MACH_INT(ty_mach, int), + LIT_STR(str), + LIT_CHAR(char), + LIT_BOOL(bool), + + /* Name components */ + IDENT(str), + IDX(int), + UNDERSCORE(), + + /* Reserved type names */ + BOOL(), + INT(), + UINT(), + FLOAT(), + CHAR(), + STR(), + MACH(ty_mach), + + /* Algebraic type constructors */ + REC(), + TUP(), + TAG(), + VEC(), + ANY(), + + /* Callable type constructors */ + FN(), + ITER(), + + /* Object type */ + OBJ(), + + /* Comm and task types */ + CHAN(), + PORT(), + TASK(), + + BRACEQUOTE(str), + EOF()); + +fn op_to_str(op o) -> str { + alt (o) { + case (PLUS()) { ret "+"; } + case (MINUS()) { ret "-"; } + case (STAR()) { ret "*"; } + case (SLASH()) { ret "/"; } + case (PERCENT()) { ret "%"; } + case (EQ()) { ret "="; } + case (LT()) { ret "<"; } + case (LE()) { ret "<="; } + case (EQEQ()) { ret "=="; } + case (NE()) { ret "!="; } + case (GE()) { ret ">="; } + case (GT()) { ret ">"; } + case (NOT()) { ret "!"; } + case (TILDE()) { ret "~"; } + case (CARET()) { ret "^"; } + case (AND()) { ret "&"; } + case (ANDAND()) { ret "&&"; } + case (OR()) { ret "|"; } + case (OROR()) { ret "||"; } + case (LSL()) { ret "<<"; } + case (LSR()) { ret ">>"; } + case (ASR()) { ret ">>>"; } + } +} + +fn to_str(token t) -> str { + alt (t) { + case (OP(op)) { ret op_to_str(op); } + case (OPEQ(op)) { ret op_to_str(op) + "="; } + case (AS()) { ret "as"; } + case (WITH()) { ret "with"; } + + /* Structural symbols */ + case (AT()) { ret "@"; } + case (DOT()) { ret "."; } + case (COMMA()) { ret ","; } + case (SEMI()) { ret ";"; } + case (COLON()) { ret ":"; } + case (RARROW()) { ret "->"; } + case (SEND()) { ret "<|"; } + case (LARROW()) { ret "<-"; } + case (LPAREN()) { ret "("; } + case (RPAREN()) { ret ")"; } + case (LBRACKET()) { ret "["; } + case (RBRACKET()) { ret "]"; } + case (LBRACE()) { ret "{"; } + case (RBRACE()) { ret "}"; } + + /* Module and crate keywords */ + case (MOD()) { ret "mod"; } + case (USE()) { ret "use"; } + case (AUTH()) { ret "auth"; } + case (META()) { ret "meta"; } + + /* Metaprogramming keywords */ + case (SYNTAX()) { ret "syntax"; } + case (POUND()) { ret "#"; } + + /* Statement keywords */ + case (IF()) { ret "if"; } + case (ELSE()) { ret "else"; } + case (DO()) { ret "do"; } + case (WHILE()) { ret "while"; } + case (ALT()) { ret "alt"; } + case (CASE()) { ret "case"; } + + case (FAIL()) { ret "fail"; } + case (DROP()) { ret "drop"; } + + case (IN()) { ret "in"; } + case (FOR()) { ret "for"; } + case (EACH()) { ret "each"; } + case (PUT()) { ret "put"; } + case (RET()) { ret "ret"; } + case (BE()) { ret "be"; } + + /* Type and type-state keywords */ + case (TYPE()) { ret "type"; } + case (CHECK()) { ret "check"; } + case (CLAIM()) { ret "claim"; } + case (PROVE()) { ret "prove"; } + + /* Effect keywords */ + case (IO()) { ret "io"; } + case (STATE()) { ret "state"; } + case (UNSAFE()) { ret "unsafe"; } + + /* Type qualifiers */ + case (NATIVE()) { ret "native"; } + case (AUTO()) { ret "auto"; } + case (MUTABLE()) { ret "mutable"; } + + /* Name management */ + case (IMPORT()) { ret "import"; } + case (EXPORT()) { ret "export"; } + + /* Value / stmt declarators */ + case (LET()) { ret "let"; } + + /* Magic runtime services */ + case (LOG()) { ret "log"; } + case (SPAWN()) { ret "spawn"; } + case (BIND()) { ret "bind"; } + case (THREAD()) { ret "thread"; } + case (YIELD()) { ret "yield"; } + case (JOIN()) { ret "join"; } + + /* Literals */ + case (LIT_INT(i)) { ret _int.to_str(i, 10u); } + case (LIT_UINT(u)) { ret _uint.to_str(u, 10u); } + case (LIT_MACH_INT(tm, i)) { + ret _int.to_str(i, 10u) + + "_" + ty_mach_to_str(tm); + } + + case (LIT_STR(s)) { + // FIXME: escape. + ret "\"" + s + "\""; + } + case (LIT_CHAR(c)) { + // FIXME: escape and encode. + auto tmp = ""; + tmp += (c as u8); + ret tmp; + } + + case (LIT_BOOL(b)) { + if (b) { ret "true"; } else { ret "false"; } + } + + /* Name components */ + case (IDENT(s)) { ret s; } + case (IDX(i)) { ret "_" + _int.to_str(i, 10u); } + case (UNDERSCORE()) { ret "_"; } + + /* Reserved type names */ + case (BOOL()) { ret "bool"; } + case (INT()) { ret "int"; } + case (UINT()) { ret "uint"; } + case (FLOAT()) { ret "float"; } + case (CHAR()) { ret "char"; } + case (STR()) { ret "str"; } + case (MACH(tm)) { ret ty_mach_to_str(tm); } + + /* Algebraic type constructors */ + case (REC()) { ret "rec"; } + case (TUP()) { ret "tup"; } + case (TAG()) { ret "tag"; } + case (VEC()) { ret "vec"; } + case (ANY()) { ret "any"; } + + /* Callable type constructors */ + case (FN()) { ret "fn"; } + case (ITER()) { ret "iter"; } + + /* Object type */ + case (OBJ()) { ret "obj"; } + + /* Comm and task types */ + case (CHAN()) { ret "chan"; } + case (PORT()) { ret "port"; } + case (TASK()) { ret "task"; } + + case (BRACEQUOTE(_)) { ret "<bracequote>"; } + case (EOF()) { ret "<eof>"; } + } +} + + + +// Local Variables: +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// compile-command: "make -k -C ../.. 2>&1 | sed -e 's/\\/x\\//x:\\//g'"; +// End: diff --git a/src/comp/lib/llvm.rs b/src/comp/lib/llvm.rs index da748bf3..9a31a193 100644 --- a/src/comp/lib/llvm.rs +++ b/src/comp/lib/llvm.rs @@ -1,277 +1,884 @@ import std._str.rustrt.sbuf; import std._vec.rustrt.vbuf; +import llvm.ModuleRef; +import llvm.ContextRef; +import llvm.TypeRef; +import llvm.TypeHandleRef; +import llvm.ValueRef; +import llvm.BasicBlockRef; +import llvm.BuilderRef; +import llvm.ModuleProviderRef; +import llvm.MemoryBufferRef; +import llvm.PassManagerRef; +import llvm.UseRef; +import llvm.Linkage; +import llvm.Attribute; +import llvm.Visibility; +import llvm.CallConv; +import llvm.IntPredicate; +import llvm.RealPredicate; +import llvm.Opcode; + type ULongLong = u64; type LongLong = i64; type Long = i32; type Bool = int; -native mod llvm = "libLLVM-2.7.so" { - - type ModuleRef; - type ContextRef; - type TypeRef; - type TypeHandleRef; - type ValueRef; - type BasicBlockRef; - type BuilderRef; - type ModuleProviderRef; - type MemoryBufferRef; - type PassManagerRef; - type UseRef; - - - /* Create and destroy contexts. */ - fn ContextCreate() -> ContextRef; - fn GetGlobalContext() -> ContextRef; - fn ContextDispose(ContextRef C); - fn GetMDKindIDInContext(ContextRef C, sbuf Name, uint SLen) -> uint; - fn GetMDKindID(sbuf Name, uint SLen) -> uint; - - /* Create and destroy modules. */ - fn ModuleCreateWithName(sbuf ModuleID) -> ModuleRef; - fn DisposeModule(ModuleRef M); - - /** Data layout. See Module::getDataLayout. */ - fn GetDataLayout(ModuleRef M) -> sbuf; - fn SetDataLayout(ModuleRef M, sbuf Triple); - - /** Target triple. See Module::getTargetTriple. */ - fn GetTarget(ModuleRef M) -> sbuf; - fn SetTarget(ModuleRef M, sbuf Triple); - - /** See Module::addTypeName. */ - fn AddTypeName(ModuleRef M, sbuf Name, TypeRef Ty) -> Bool; - fn DeleteTypeName(ModuleRef M, sbuf Name); - fn GetTypeByName(ModuleRef M, sbuf Name) -> TypeRef; - - /** See Module::dump. */ - fn DumpModule(ModuleRef M); - - /** See Module::setModuleInlineAsm. */ - fn SetModuleInlineAsm(ModuleRef M, sbuf Asm); - - /** See llvm::LLVMType::getContext. */ - fn GetTypeContext(TypeRef Ty) -> ContextRef; - - /* Operations on integer types */ - fn Int1TypeInContext(ContextRef C) -> TypeRef; - fn Int8TypeInContext(ContextRef C) -> TypeRef; - fn Int16TypeInContext(ContextRef C) -> TypeRef; - fn Int32TypeInContext(ContextRef C) -> TypeRef; - fn Int64TypeInContext(ContextRef C) -> TypeRef; - fn IntTypeInContext(ContextRef C, uint NumBits) -> TypeRef; - - fn Int1Type() -> TypeRef; - fn Int8Type() -> TypeRef; - fn Int16Type() -> TypeRef; - fn Int32Type() -> TypeRef; - fn Int64Type() -> TypeRef; - fn IntType(uint NumBits) -> TypeRef; - fn GetIntTypeWidth(TypeRef IntegerTy) -> uint; - - /* Operations on real types */ - fn FloatTypeInContext(ContextRef C) -> TypeRef; - fn DoubleTypeInContext(ContextRef C) -> TypeRef; - fn X86FP80TypeInContext(ContextRef C) -> TypeRef; - fn FP128TypeInContext(ContextRef C) -> TypeRef; - fn PPCFP128TypeInContext(ContextRef C) -> TypeRef; - - fn FloatType() -> TypeRef; - fn DoubleType() -> TypeRef; - fn X86FP80Type() -> TypeRef; - fn FP128Type() -> TypeRef; - fn PPCFP128Type() -> TypeRef; - - /* Operations on function types */ - fn FunctionType(TypeRef ReturnType, vbuf ParamTypes, - uint ParamCount, Bool IsVarArg) -> TypeRef; - fn IsFunctionVarArg(TypeRef FunctionTy) -> Bool; - fn GetReturnType(TypeRef FunctionTy) -> TypeRef; - fn CountParamTypes(TypeRef FunctionTy) -> uint; - fn GetParamTypes(TypeRef FunctionTy, vbuf Dest); - - /* Operations on struct types */ - fn StructTypeInContext(ContextRef C, vbuf ElementTypes, - uint ElementCount, Bool Packed) -> TypeRef; - fn StructType(vbuf ElementTypes, uint ElementCount, - Bool Packed) -> TypeRef; - fn CountStructElementTypes(TypeRef StructTy) -> uint; - fn GetStructElementTypes(TypeRef StructTy, vbuf Dest); - fn IsPackedStruct(TypeRef StructTy) -> Bool; - - /* Operations on union types */ - fn UnionTypeInContext(ContextRef C, vbuf ElementTypes, - uint ElementCount) -> TypeRef; - fn UnionType(vbuf ElementTypes, uint ElementCount) -> TypeRef; - fn CountUnionElementTypes(TypeRef UnionTy) -> uint; - fn GetUnionElementTypes(TypeRef UnionTy, vbuf Dest); - - /* Operations on array, pointer, and vector types (sequence types) */ - fn ArrayType(TypeRef ElementType, uint ElementCount) -> TypeRef; - fn PointerType(TypeRef ElementType, uint AddressSpace) -> TypeRef; - fn VectorType(TypeRef ElementType, uint ElementCount) -> TypeRef; - - fn GetElementType(TypeRef Ty) -> TypeRef; - fn GetArrayLength(TypeRef ArrayTy) -> uint; - fn GetPointerAddressSpace(TypeRef PointerTy) -> uint; - fn GetVectorSize(TypeRef VectorTy) -> uint; - - /* Operations on other types */ - fn VoidTypeInContext(ContextRef C) -> TypeRef; - fn LabelTypeInContext(ContextRef C) -> TypeRef; - fn OpaqueTypeInContext(ContextRef C) -> TypeRef; - - fn VoidType() -> TypeRef; - fn LabelType() -> TypeRef; - fn OpaqueType() -> TypeRef; - - /* Operations on type handles */ - fn CreateTypeHandle(TypeRef PotentiallyAbstractTy) -> TypeHandleRef; - fn RefineType(TypeRef AbstractTy, TypeRef ConcreteTy); - fn ResolveTypeHandle(TypeHandleRef TypeHandle) -> TypeRef; - fn DisposeTypeHandle(TypeHandleRef TypeHandle); - - /* Operations on all values */ - fn TypeOf(ValueRef Val) -> TypeRef; - fn GetValueName(ValueRef Val) -> sbuf; - fn SetValueName(ValueRef Val, sbuf Name); - fn DumpValue(ValueRef Val); - fn ReplaceAllUsesWith(ValueRef OldVal, ValueRef NewVal); - fn HasMetadata(ValueRef Val) -> int; - fn GetMetadata(ValueRef Val, uint KindID) -> ValueRef; - fn SetMetadata(ValueRef Val, uint KindID, ValueRef Node); - - /* Operations on Uses */ - fn GetFirstUse(ValueRef Val) -> UseRef; - fn GetNextUse(UseRef U) -> UseRef; - fn GetUser(UseRef U) -> ValueRef; - fn GetUsedValue(UseRef U) -> ValueRef; - - /* Operations on Users */ - fn GetOperand(ValueRef Val, uint Index) -> ValueRef; - - /* Operations on constants of any type */ - fn ConstNull(TypeRef Ty) -> ValueRef; /* all zeroes */ - fn ConstAllOnes(TypeRef Ty) -> ValueRef; /* only for int/vector */ - fn GetUndef(TypeRef Ty) -> ValueRef; - fn IsConstant(ValueRef Val) -> Bool; - fn IsNull(ValueRef Val) -> Bool; - fn IsUndef(ValueRef Val) -> Bool; - fn ConstPointerNull(TypeRef Ty) -> ValueRef; - - /* Operations on metadata */ - fn MDStringInContext(ContextRef C, sbuf Str, uint SLen) -> ValueRef; - fn MDString(sbuf Str, uint SLen) -> ValueRef; - fn MDNodeInContext(ContextRef C, vbuf Vals, uint Count) -> ValueRef; - fn MDNode(vbuf Vals, uint Count) -> ValueRef; - - /* Operations on scalar constants */ - fn ConstInt(TypeRef IntTy, ULongLong N, Bool SignExtend) -> ValueRef; - fn ConstIntOfString(TypeRef IntTy, sbuf Text, u8 Radix) -> ValueRef; - fn ConstIntOfStringAndSize(TypeRef IntTy, sbuf Text, - uint SLen, u8 Radix) -> ValueRef; - fn ConstReal(TypeRef RealTy, f64 N) -> ValueRef; - fn ConstRealOfString(TypeRef RealTy, sbuf Text) -> ValueRef; - fn ConstRealOfStringAndSize(TypeRef RealTy, sbuf Text, - uint SLen) -> ValueRef; - fn ConstIntGetZExtValue(ValueRef ConstantVal) -> ULongLong; - fn ConstIntGetSExtValue(ValueRef ConstantVal) -> LongLong; - - - /* Operations on composite constants */ - fn ConstStringInContext(ContextRef C, sbuf Str, - uint Length, Bool DontNullTerminate) -> ValueRef; - fn ConstStructInContext(ContextRef C, vbuf ConstantVals, - uint Count, Bool Packed) -> ValueRef; - - fn ConstString(sbuf Str, uint Length, Bool DontNullTerminate) -> ValueRef; - fn ConstArray(TypeRef ElementTy, - vbuf ConstantVals, uint Length) -> ValueRef; - fn ConstStruct(vbuf ConstantVals, uint Count, Bool Packed) -> ValueRef; - fn ConstVector(vbuf ScalarConstantVals, uint Size) -> ValueRef; - fn ConstUnion(TypeRef Ty, ValueRef Val) -> ValueRef; - - /* Constant expressions */ - fn AlignOf(TypeRef Ty) -> ValueRef; - fn SizeOf(TypeRef Ty) -> ValueRef; - fn ConstNeg(ValueRef ConstantVal) -> ValueRef; - fn ConstNSWNeg(ValueRef ConstantVal) -> ValueRef; - fn ConstNUWNeg(ValueRef ConstantVal) -> ValueRef; - fn ConstFNeg(ValueRef ConstantVal) -> ValueRef; - fn ConstNot(ValueRef ConstantVal) -> ValueRef; - fn ConstAdd(ValueRef LHSConstant, ValueRef RHSConstant) -> ValueRef; - fn ConstNSWAdd(ValueRef LHSConstant, ValueRef RHSConstant) -> ValueRef; - fn ConstNUWAdd(ValueRef LHSConstant, ValueRef RHSConstant) -> ValueRef; - fn ConstFAdd(ValueRef LHSConstant, ValueRef RHSConstant) -> ValueRef; - fn ConstSub(ValueRef LHSConstant, ValueRef RHSConstant) -> ValueRef; - fn ConstNSWSub(ValueRef LHSConstant, ValueRef RHSConstant) -> ValueRef; - fn ConstNUWSub(ValueRef LHSConstant, ValueRef RHSConstant) -> ValueRef; - fn ConstFSub(ValueRef LHSConstant, ValueRef RHSConstant) -> ValueRef; - fn ConstMul(ValueRef LHSConstant, ValueRef RHSConstant) -> ValueRef; - fn ConstNSWMul(ValueRef LHSConstant, ValueRef RHSConstant) -> ValueRef; - fn ConstNUWMul(ValueRef LHSConstant, ValueRef RHSConstant) -> ValueRef; - fn ConstFMul(ValueRef LHSConstant, ValueRef RHSConstant) -> ValueRef; - fn ConstUDiv(ValueRef LHSConstant, ValueRef RHSConstant) -> ValueRef; - fn ConstSDiv(ValueRef LHSConstant, ValueRef RHSConstant) -> ValueRef; - fn ConstExactSDiv(ValueRef LHSConstant, ValueRef RHSConstant) -> ValueRef; - fn ConstFDiv(ValueRef LHSConstant, ValueRef RHSConstant) -> ValueRef; - fn ConstURem(ValueRef LHSConstant, ValueRef RHSConstant) -> ValueRef; - fn ConstSRem(ValueRef LHSConstant, ValueRef RHSConstant) -> ValueRef; - fn ConstFRem(ValueRef LHSConstant, ValueRef RHSConstant) -> ValueRef; - fn ConstAnd(ValueRef LHSConstant, ValueRef RHSConstant) -> ValueRef; - fn ConstOr(ValueRef LHSConstant, ValueRef RHSConstant) -> ValueRef; - fn ConstXor(ValueRef LHSConstant, ValueRef RHSConstant) -> ValueRef; - fn ConstShl(ValueRef LHSConstant, ValueRef RHSConstant) -> ValueRef; - fn ConstLShr(ValueRef LHSConstant, ValueRef RHSConstant) -> ValueRef; - fn ConstAShr(ValueRef LHSConstant, ValueRef RHSConstant) -> ValueRef; - fn ConstGEP(ValueRef ConstantVal, - vbuf ConstantIndices, uint NumIndices) -> ValueRef; - fn ConstInBoundsGEP(ValueRef ConstantVal, - vbuf ConstantIndices, - uint NumIndices) -> ValueRef; - fn ConstTrunc(ValueRef ConstantVal, TypeRef ToType) -> ValueRef; - fn ConstSExt(ValueRef ConstantVal, TypeRef ToType) -> ValueRef; - fn ConstZExt(ValueRef ConstantVal, TypeRef ToType) -> ValueRef; - fn ConstFPTrunc(ValueRef ConstantVal, TypeRef ToType) -> ValueRef; - fn ConstFPExt(ValueRef ConstantVal, TypeRef ToType) -> ValueRef; - fn ConstUIToFP(ValueRef ConstantVal, TypeRef ToType) -> ValueRef; - fn ConstSIToFP(ValueRef ConstantVal, TypeRef ToType) -> ValueRef; - fn ConstFPToUI(ValueRef ConstantVal, TypeRef ToType) -> ValueRef; - fn ConstFPToSI(ValueRef ConstantVal, TypeRef ToType) -> ValueRef; - fn ConstPtrToInt(ValueRef ConstantVal, TypeRef ToType) -> ValueRef; - fn ConstIntToPtr(ValueRef ConstantVal, TypeRef ToType) -> ValueRef; - fn ConstBitCast(ValueRef ConstantVal, TypeRef ToType) -> ValueRef; - fn ConstZExtOrBitCast(ValueRef ConstantVal, - TypeRef ToType) -> ValueRef; - fn ConstSExtOrBitCast(ValueRef ConstantVal, - TypeRef ToType) -> ValueRef; - fn ConstTruncOrBitCast(ValueRef ConstantVal, - TypeRef ToType) -> ValueRef; - fn ConstPointerCast(ValueRef ConstantVal, - TypeRef ToType) -> ValueRef; - fn ConstIntCast(ValueRef ConstantVal, TypeRef ToType, - Bool isSigned) -> ValueRef; - fn ConstFPCast(ValueRef ConstantVal, TypeRef ToType) -> ValueRef; - fn ConstSelect(ValueRef ConstantCondition, - ValueRef ConstantIfTrue, - ValueRef ConstantIfFalse) -> ValueRef; - fn ConstExtractElement(ValueRef VectorConstant, - ValueRef IndexConstant) -> ValueRef; - fn ConstInsertElement(ValueRef VectorConstant, - ValueRef ElementValueConstant, - ValueRef IndexConstant) -> ValueRef; - fn ConstShuffleVector(ValueRef VectorAConstant, - ValueRef VectorBConstant, - ValueRef MaskConstant) -> ValueRef; - fn ConstExtractValue(ValueRef AggConstant, vbuf IdxList, - uint NumIdx) -> ValueRef; - fn ConstInsertValue(ValueRef AggConstant, - ValueRef ElementValueConstant, - vbuf IdxList, uint NumIdx) -> ValueRef; - fn ConstInlineAsm(TypeRef Ty, - sbuf AsmString, sbuf Constraints, - Bool HasSideEffects, Bool IsAlignStack) -> ValueRef; - fn BlockAddress(ValueRef F, BasicBlockRef BB) -> ValueRef; - -}
\ No newline at end of file +native mod llvm = llvm_lib { + + type ModuleRef; + type ContextRef; + type TypeRef; + type TypeHandleRef; + type ValueRef; + type BasicBlockRef; + type BuilderRef; + type ModuleProviderRef; + type MemoryBufferRef; + type PassManagerRef; + type UseRef; + + /* FIXME: These are enums in the C header. Represent them how, in rust? */ + type Linkage; + type Attribute; + type Visibility; + type CallConv; + type IntPredicate; + type RealPredicate; + type Opcode; + + /* Create and destroy contexts. */ + fn LLVMContextCreate() -> ContextRef; + fn LLVMGetGlobalContext() -> ContextRef; + fn LLVMContextDispose(ContextRef C); + fn LLVMGetMDKindIDInContext(ContextRef C, sbuf Name, uint SLen) -> uint; + fn LLVMGetMDKindID(sbuf Name, uint SLen) -> uint; + + /* Create and destroy modules. */ + fn LLVMModuleCreateWithNameInContext(sbuf ModuleID, + ContextRef C)-> ModuleRef; + fn LLVMDisposeModule(ModuleRef M); + + /** Data layout. See Module::getDataLayout. */ + fn LLVMGetDataLayout(ModuleRef M) -> sbuf; + fn LLVMSetDataLayout(ModuleRef M, sbuf Triple); + + /** Target triple. See Module::getTargetTriple. */ + fn LLVMGetTarget(ModuleRef M) -> sbuf; + fn LLVMSetTarget(ModuleRef M, sbuf Triple); + + /** See Module::addTypeName. */ + fn LLVMAddTypeName(ModuleRef M, sbuf Name, TypeRef Ty) -> Bool; + fn LLVMDeleteTypeName(ModuleRef M, sbuf Name); + fn LLVMGetTypeByName(ModuleRef M, sbuf Name) -> TypeRef; + + /** See Module::dump. */ + fn LLVMDumpModule(ModuleRef M); + + /** See Module::setModuleInlineAsm. */ + fn LLVMSetModuleInlineAsm(ModuleRef M, sbuf Asm); + + /** See llvm::LLVMType::getContext. */ + fn LLVMGetTypeContext(TypeRef Ty) -> ContextRef; + + /* Operations on integer types */ + fn LLVMInt1TypeInContext(ContextRef C) -> TypeRef; + fn LLVMInt8TypeInContext(ContextRef C) -> TypeRef; + fn LLVMInt16TypeInContext(ContextRef C) -> TypeRef; + fn LLVMInt32TypeInContext(ContextRef C) -> TypeRef; + fn LLVMInt64TypeInContext(ContextRef C) -> TypeRef; + fn LLVMIntTypeInContext(ContextRef C, uint NumBits) -> TypeRef; + + fn LLVMInt1Type() -> TypeRef; + fn LLVMInt8Type() -> TypeRef; + fn LLVMInt16Type() -> TypeRef; + fn LLVMInt32Type() -> TypeRef; + fn LLVMInt64Type() -> TypeRef; + fn LLVMIntType(uint NumBits) -> TypeRef; + fn LLVMGetIntTypeWidth(TypeRef IntegerTy) -> uint; + + /* Operations on real types */ + fn LLVMFloatTypeInContext(ContextRef C) -> TypeRef; + fn LLVMDoubleTypeInContext(ContextRef C) -> TypeRef; + fn LLVMX86FP80TypeInContext(ContextRef C) -> TypeRef; + fn LLVMFP128TypeInContext(ContextRef C) -> TypeRef; + fn LLVMPPCFP128TypeInContext(ContextRef C) -> TypeRef; + + fn LLVMFloatType() -> TypeRef; + fn LLVMDoubleType() -> TypeRef; + fn LLVMX86FP80Type() -> TypeRef; + fn LLVMFP128Type() -> TypeRef; + fn LLVMPPCFP128Type() -> TypeRef; + + /* Operations on function types */ + fn LLVMFunctionType(TypeRef ReturnType, vbuf ParamTypes, + uint ParamCount, Bool IsVarArg) -> TypeRef; + fn LLVMIsFunctionVarArg(TypeRef FunctionTy) -> Bool; + fn LLVMGetReturnType(TypeRef FunctionTy) -> TypeRef; + fn LLVMCountParamTypes(TypeRef FunctionTy) -> uint; + fn LLVMGetParamTypes(TypeRef FunctionTy, vbuf Dest); + + /* Operations on struct types */ + fn LLVMStructTypeInContext(ContextRef C, vbuf ElementTypes, + uint ElementCount, Bool Packed) -> TypeRef; + fn LLVMStructType(vbuf ElementTypes, uint ElementCount, + Bool Packed) -> TypeRef; + fn LLVMCountStructElementTypes(TypeRef StructTy) -> uint; + fn LLVMGetStructElementTypes(TypeRef StructTy, vbuf Dest); + fn LLVMIsPackedStruct(TypeRef StructTy) -> Bool; + + /* Operations on union types */ + fn LLVMUnionTypeInContext(ContextRef C, vbuf ElementTypes, + uint ElementCount) -> TypeRef; + fn LLVMUnionType(vbuf ElementTypes, uint ElementCount) -> TypeRef; + fn LLVMCountUnionElementTypes(TypeRef UnionTy) -> uint; + fn LLVMGetUnionElementTypes(TypeRef UnionTy, vbuf Dest); + + /* Operations on array, pointer, and vector types (sequence types) */ + fn LLVMArrayType(TypeRef ElementType, uint ElementCount) -> TypeRef; + fn LLVMPointerType(TypeRef ElementType, uint AddressSpace) -> TypeRef; + fn LLVMVectorType(TypeRef ElementType, uint ElementCount) -> TypeRef; + + fn LLVMGetElementType(TypeRef Ty) -> TypeRef; + fn LLVMGetArrayLength(TypeRef ArrayTy) -> uint; + fn LLVMGetPointerAddressSpace(TypeRef PointerTy) -> uint; + fn LLVMGetVectorSize(TypeRef VectorTy) -> uint; + + /* Operations on other types */ + fn LLVMVoidTypeInContext(ContextRef C) -> TypeRef; + fn LLVMLabelTypeInContext(ContextRef C) -> TypeRef; + fn LLVMOpaqueTypeInContext(ContextRef C) -> TypeRef; + + fn LLVMVoidType() -> TypeRef; + fn LLVMLabelType() -> TypeRef; + fn LLVMOpaqueType() -> TypeRef; + + /* Operations on type handles */ + fn LLVMCreateTypeHandle(TypeRef PotentiallyAbstractTy) -> TypeHandleRef; + fn LLVMRefineType(TypeRef AbstractTy, TypeRef ConcreteTy); + fn LLVMResolveTypeHandle(TypeHandleRef TypeHandle) -> TypeRef; + fn LLVMDisposeTypeHandle(TypeHandleRef TypeHandle); + + /* Operations on all values */ + fn LLVMTypeOf(ValueRef Val) -> TypeRef; + fn LLVMGetValueName(ValueRef Val) -> sbuf; + fn LLVMSetValueName(ValueRef Val, sbuf Name); + fn LLVMDumpValue(ValueRef Val); + fn LLVMReplaceAllUsesWith(ValueRef OldVal, ValueRef NewVal); + fn LLVMHasMetadata(ValueRef Val) -> int; + fn LLVMGetMetadata(ValueRef Val, uint KindID) -> ValueRef; + fn LLVMSetMetadata(ValueRef Val, uint KindID, ValueRef Node); + + /* Operations on Uses */ + fn LLVMGetFirstUse(ValueRef Val) -> UseRef; + fn LLVMGetNextUse(UseRef U) -> UseRef; + fn LLVMGetUser(UseRef U) -> ValueRef; + fn LLVMGetUsedValue(UseRef U) -> ValueRef; + + /* Operations on Users */ + fn LLVMGetOperand(ValueRef Val, uint Index) -> ValueRef; + + /* Operations on constants of any type */ + fn LLVMConstNull(TypeRef Ty) -> ValueRef; /* all zeroes */ + fn LLVMConstAllOnes(TypeRef Ty) -> ValueRef; /* only for int/vector */ + fn LLVMGetUndef(TypeRef Ty) -> ValueRef; + fn LLVMIsConstant(ValueRef Val) -> Bool; + fn LLVMIsNull(ValueRef Val) -> Bool; + fn LLVMIsUndef(ValueRef Val) -> Bool; + fn LLVMConstPointerNull(TypeRef Ty) -> ValueRef; + + /* Operations on metadata */ + fn LLVMMDStringInContext(ContextRef C, sbuf Str, uint SLen) -> ValueRef; + fn LLVMMDString(sbuf Str, uint SLen) -> ValueRef; + fn LLVMMDNodeInContext(ContextRef C, vbuf Vals, uint Count) -> ValueRef; + fn LLVMMDNode(vbuf Vals, uint Count) -> ValueRef; + + /* Operations on scalar constants */ + fn LLVMConstInt(TypeRef IntTy, ULongLong N, Bool SignExtend) -> ValueRef; + fn LLVMConstIntOfString(TypeRef IntTy, sbuf Text, u8 Radix) -> ValueRef; + fn LLVMConstIntOfStringAndSize(TypeRef IntTy, sbuf Text, + uint SLen, u8 Radix) -> ValueRef; + fn LLVMConstReal(TypeRef RealTy, f64 N) -> ValueRef; + fn LLVMConstRealOfString(TypeRef RealTy, sbuf Text) -> ValueRef; + fn LLVMConstRealOfStringAndSize(TypeRef RealTy, sbuf Text, + uint SLen) -> ValueRef; + fn LLVMConstIntGetZExtValue(ValueRef ConstantVal) -> ULongLong; + fn LLVMConstIntGetSExtValue(ValueRef ConstantVal) -> LongLong; + + + /* Operations on composite constants */ + fn LLVMConstStringInContext(ContextRef C, sbuf Str, uint Length, + Bool DontNullTerminate) -> ValueRef; + fn LLVMConstStructInContext(ContextRef C, vbuf ConstantVals, + uint Count, Bool Packed) -> ValueRef; + + fn LLVMConstString(sbuf Str, uint Length, + Bool DontNullTerminate) -> ValueRef; + fn LLVMConstArray(TypeRef ElementTy, + vbuf ConstantVals, uint Length) -> ValueRef; + fn LLVMConstStruct(vbuf ConstantVals, uint Count, + Bool Packed) -> ValueRef; + fn LLVMConstVector(vbuf ScalarConstantVals, uint Size) -> ValueRef; + fn LLVMConstUnion(TypeRef Ty, ValueRef Val) -> ValueRef; + + /* Constant expressions */ + fn LLVMAlignOf(TypeRef Ty) -> ValueRef; + fn LLVMSizeOf(TypeRef Ty) -> ValueRef; + fn LLVMConstNeg(ValueRef ConstantVal) -> ValueRef; + fn LLVMConstNSWNeg(ValueRef ConstantVal) -> ValueRef; + fn LLVMConstNUWNeg(ValueRef ConstantVal) -> ValueRef; + fn LLVMConstFNeg(ValueRef ConstantVal) -> ValueRef; + fn LLVMConstNot(ValueRef ConstantVal) -> ValueRef; + fn LLVMConstAdd(ValueRef LHSConstant, ValueRef RHSConstant) -> ValueRef; + fn LLVMConstNSWAdd(ValueRef LHSConstant, + ValueRef RHSConstant) -> ValueRef; + fn LLVMConstNUWAdd(ValueRef LHSConstant, + ValueRef RHSConstant) -> ValueRef; + fn LLVMConstFAdd(ValueRef LHSConstant, ValueRef RHSConstant) -> ValueRef; + fn LLVMConstSub(ValueRef LHSConstant, ValueRef RHSConstant) -> ValueRef; + fn LLVMConstNSWSub(ValueRef LHSConstant, + ValueRef RHSConstant) -> ValueRef; + fn LLVMConstNUWSub(ValueRef LHSConstant, + ValueRef RHSConstant) -> ValueRef; + fn LLVMConstFSub(ValueRef LHSConstant, ValueRef RHSConstant) -> ValueRef; + fn LLVMConstMul(ValueRef LHSConstant, ValueRef RHSConstant) -> ValueRef; + fn LLVMConstNSWMul(ValueRef LHSConstant, + ValueRef RHSConstant) -> ValueRef; + fn LLVMConstNUWMul(ValueRef LHSConstant, + ValueRef RHSConstant) -> ValueRef; + fn LLVMConstFMul(ValueRef LHSConstant, ValueRef RHSConstant) -> ValueRef; + fn LLVMConstUDiv(ValueRef LHSConstant, ValueRef RHSConstant) -> ValueRef; + fn LLVMConstSDiv(ValueRef LHSConstant, ValueRef RHSConstant) -> ValueRef; + fn LLVMConstExactSDiv(ValueRef LHSConstant, + ValueRef RHSConstant) -> ValueRef; + fn LLVMConstFDiv(ValueRef LHSConstant, ValueRef RHSConstant) -> ValueRef; + fn LLVMConstURem(ValueRef LHSConstant, ValueRef RHSConstant) -> ValueRef; + fn LLVMConstSRem(ValueRef LHSConstant, ValueRef RHSConstant) -> ValueRef; + fn LLVMConstFRem(ValueRef LHSConstant, ValueRef RHSConstant) -> ValueRef; + fn LLVMConstAnd(ValueRef LHSConstant, ValueRef RHSConstant) -> ValueRef; + fn LLVMConstOr(ValueRef LHSConstant, ValueRef RHSConstant) -> ValueRef; + fn LLVMConstXor(ValueRef LHSConstant, ValueRef RHSConstant) -> ValueRef; + fn LLVMConstShl(ValueRef LHSConstant, ValueRef RHSConstant) -> ValueRef; + fn LLVMConstLShr(ValueRef LHSConstant, ValueRef RHSConstant) -> ValueRef; + fn LLVMConstAShr(ValueRef LHSConstant, ValueRef RHSConstant) -> ValueRef; + fn LLVMConstGEP(ValueRef ConstantVal, + vbuf ConstantIndices, uint NumIndices) -> ValueRef; + fn LLVMConstInBoundsGEP(ValueRef ConstantVal, + vbuf ConstantIndices, + uint NumIndices) -> ValueRef; + fn LLVMConstTrunc(ValueRef ConstantVal, TypeRef ToType) -> ValueRef; + fn LLVMConstSExt(ValueRef ConstantVal, TypeRef ToType) -> ValueRef; + fn LLVMConstZExt(ValueRef ConstantVal, TypeRef ToType) -> ValueRef; + fn LLVMConstFPTrunc(ValueRef ConstantVal, TypeRef ToType) -> ValueRef; + fn LLVMConstFPExt(ValueRef ConstantVal, TypeRef ToType) -> ValueRef; + fn LLVMConstUIToFP(ValueRef ConstantVal, TypeRef ToType) -> ValueRef; + fn LLVMConstSIToFP(ValueRef ConstantVal, TypeRef ToType) -> ValueRef; + fn LLVMConstFPToUI(ValueRef ConstantVal, TypeRef ToType) -> ValueRef; + fn LLVMConstFPToSI(ValueRef ConstantVal, TypeRef ToType) -> ValueRef; + fn LLVMConstPtrToInt(ValueRef ConstantVal, TypeRef ToType) -> ValueRef; + fn LLVMConstIntToPtr(ValueRef ConstantVal, TypeRef ToType) -> ValueRef; + fn LLVMConstBitCast(ValueRef ConstantVal, TypeRef ToType) -> ValueRef; + fn LLVMConstZExtOrBitCast(ValueRef ConstantVal, + TypeRef ToType) -> ValueRef; + fn LLVMConstSExtOrBitCast(ValueRef ConstantVal, + TypeRef ToType) -> ValueRef; + fn LLVMConstTruncOrBitCast(ValueRef ConstantVal, + TypeRef ToType) -> ValueRef; + fn LLVMConstPointerCast(ValueRef ConstantVal, + TypeRef ToType) -> ValueRef; + fn LLVMConstIntCast(ValueRef ConstantVal, TypeRef ToType, + Bool isSigned) -> ValueRef; + fn LLVMConstFPCast(ValueRef ConstantVal, TypeRef ToType) -> ValueRef; + fn LLVMConstSelect(ValueRef ConstantCondition, + ValueRef ConstantIfTrue, + ValueRef ConstantIfFalse) -> ValueRef; + fn LLVMConstExtractElement(ValueRef VectorConstant, + ValueRef IndexConstant) -> ValueRef; + fn LLVMConstInsertElement(ValueRef VectorConstant, + ValueRef ElementValueConstant, + ValueRef IndexConstant) -> ValueRef; + fn LLVMConstShuffleVector(ValueRef VectorAConstant, + ValueRef VectorBConstant, + ValueRef MaskConstant) -> ValueRef; + fn LLVMConstExtractValue(ValueRef AggConstant, vbuf IdxList, + uint NumIdx) -> ValueRef; + fn LLVMConstInsertValue(ValueRef AggConstant, + ValueRef ElementValueConstant, + vbuf IdxList, uint NumIdx) -> ValueRef; + fn LLVMConstInlineAsm(TypeRef Ty, + sbuf AsmString, sbuf Constraints, + Bool HasSideEffects, Bool IsAlignStack) -> ValueRef; + fn LLVMBlockAddress(ValueRef F, BasicBlockRef BB) -> ValueRef; + + + + /* Operations on global variables, functions, and aliases (globals) */ + fn LLVMGetGlobalParent(ValueRef Global) -> ModuleRef; + fn LLVMIsDeclaration(ValueRef Global) -> Bool; + fn LLVMGetLinkage(ValueRef Global) -> Linkage; + fn LLVMSetLinkage(ValueRef Global, Linkage Link); + fn LLVMGetSection(ValueRef Global) -> sbuf; + fn LLVMSetSection(ValueRef Global, sbuf Section); + fn LLVMGetVisibility(ValueRef Global) -> Visibility; + fn LLVMSetVisibility(ValueRef Global, Visibility Viz); + fn LLVMGetAlignment(ValueRef Global) -> uint; + fn LLVMSetAlignment(ValueRef Global, uint Bytes); + + + /* Operations on global variables */ + fn LLVMAddGlobal(ModuleRef M, TypeRef Ty, sbuf Name) -> ValueRef; + fn LLVMAddGlobalInAddressSpace(ModuleRef M, TypeRef Ty, + sbuf Name, + uint AddressSpace) -> ValueRef; + fn LLVMGetNamedGlobal(ModuleRef M, sbuf Name) -> ValueRef; + fn LLVMGetFirstGlobal(ModuleRef M) -> ValueRef; + fn LLVMGetLastGlobal(ModuleRef M) -> ValueRef; + fn LLVMGetNextGlobal(ValueRef GlobalVar) -> ValueRef; + fn LLVMGetPreviousGlobal(ValueRef GlobalVar) -> ValueRef; + fn LLVMDeleteGlobal(ValueRef GlobalVar); + fn LLVMGetInitializer(ValueRef GlobalVar) -> ValueRef; + fn LLVMSetInitializer(ValueRef GlobalVar, ValueRef ConstantVal); + fn LLVMIsThreadLocal(ValueRef GlobalVar) -> Bool; + fn LLVMSetThreadLocal(ValueRef GlobalVar, Bool IsThreadLocal); + fn LLVMIsGlobalConstant(ValueRef GlobalVar) -> Bool; + fn LLVMSetGlobalConstant(ValueRef GlobalVar, Bool IsConstant); + + /* Operations on aliases */ + fn LLVMAddAlias(ModuleRef M, TypeRef Ty, ValueRef Aliasee, + sbuf Name) -> ValueRef; + + /* Operations on functions */ + fn LLVMAddFunction(ModuleRef M, sbuf Name, + TypeRef FunctionTy) -> ValueRef; + fn LLVMGetNamedFunction(ModuleRef M, sbuf Name) -> ValueRef; + fn LLVMGetFirstFunction(ModuleRef M) -> ValueRef; + fn LLVMGetLastFunction(ModuleRef M) -> ValueRef; + fn LLVMGetNextFunction(ValueRef Fn) -> ValueRef; + fn LLVMGetPreviousFunction(ValueRef Fn) -> ValueRef; + fn LLVMDeleteFunction(ValueRef Fn); + fn LLVMGetIntrinsicID(ValueRef Fn) -> uint; + fn LLVMGetFunctionCallConv(ValueRef Fn) -> uint; + fn LLVMSetFunctionCallConv(ValueRef Fn, uint CC); + fn LLVMGetGC(ValueRef Fn) -> sbuf; + fn LLVMSetGC(ValueRef Fn, sbuf Name); + fn LLVMAddFunctionAttr(ValueRef Fn, Attribute PA); + fn LLVMGetFunctionAttr(ValueRef Fn) -> Attribute; + fn LLVMRemoveFunctionAttr(ValueRef Fn, Attribute PA); + + /* Operations on parameters */ + fn LLVMCountParams(ValueRef Fn) -> uint; + fn LLVMGetParams(ValueRef Fn, vbuf Params); + fn LLVMGetParam(ValueRef Fn, uint Index) -> ValueRef; + fn LLVMGetParamParent(ValueRef Inst) -> ValueRef; + fn LLVMGetFirstParam(ValueRef Fn) -> ValueRef; + fn LLVMGetLastParam(ValueRef Fn) -> ValueRef; + fn LLVMGetNextParam(ValueRef Arg) -> ValueRef; + fn LLVMGetPreviousParam(ValueRef Arg) -> ValueRef; + fn LLVMAddAttribute(ValueRef Arg, Attribute PA); + fn LLVMRemoveAttribute(ValueRef Arg, Attribute PA); + fn LLVMGetAttribute(ValueRef Arg) -> Attribute; + fn LLVMSetParamAlignment(ValueRef Arg, uint align); + + /* Operations on basic blocks */ + fn LLVMBasicBlockAsValue(BasicBlockRef BB) -> ValueRef; + fn LLVMValueIsBasicBlock(ValueRef Val) -> Bool; + fn LLVMValueAsBasicBlock(ValueRef Val) -> BasicBlockRef; + fn LLVMGetBasicBlockParent(BasicBlockRef BB) -> ValueRef; + fn LLVMCountBasicBlocks(ValueRef Fn) -> uint; + fn LLVMGetBasicBlocks(ValueRef Fn, vbuf BasicBlocks); + fn LLVMGetFirstBasicBlock(ValueRef Fn) -> BasicBlockRef; + fn LLVMGetLastBasicBlock(ValueRef Fn) -> BasicBlockRef; + fn LLVMGetNextBasicBlock(BasicBlockRef BB) -> BasicBlockRef; + fn LLVMGetPreviousBasicBlock(BasicBlockRef BB) -> BasicBlockRef; + fn LLVMGetEntryBasicBlock(ValueRef Fn) -> BasicBlockRef; + + fn LLVMAppendBasicBlockInContext(ContextRef C, ValueRef Fn, + sbuf Name) -> BasicBlockRef; + fn LLVMInsertBasicBlockInContext(ContextRef C, BasicBlockRef BB, + sbuf Name) -> BasicBlockRef; + + fn LLVMAppendBasicBlock(ValueRef Fn, sbuf Name) -> BasicBlockRef; + fn LLVMInsertBasicBlock(BasicBlockRef InsertBeforeBB, + sbuf Name) -> BasicBlockRef; + fn LLVMDeleteBasicBlock(BasicBlockRef BB); + + /* Operations on instructions */ + fn LLVMGetInstructionParent(ValueRef Inst) -> BasicBlockRef; + fn LLVMGetFirstInstruction(BasicBlockRef BB) -> ValueRef; + fn LLVMGetLastInstruction(BasicBlockRef BB) -> ValueRef; + fn LLVMGetNextInstruction(ValueRef Inst) -> ValueRef; + fn LLVMGetPreviousInstruction(ValueRef Inst) -> ValueRef; + + /* Operations on call sites */ + fn LLVMSetInstructionCallConv(ValueRef Instr, uint CC); + fn LLVMGetInstructionCallConv(ValueRef Instr) -> uint; + fn LLVMAddInstrAttribute(ValueRef Instr, uint index, Attribute IA); + fn LLVMRemoveInstrAttribute(ValueRef Instr, uint index, Attribute IA); + fn LLVMSetInstrParamAlignment(ValueRef Instr, uint index, uint align); + + /* Operations on call instructions (only) */ + fn LLVMIsTailCall(ValueRef CallInst) -> Bool; + fn LLVMSetTailCall(ValueRef CallInst, Bool IsTailCall); + + /* Operations on phi nodes */ + fn LLVMAddIncoming(ValueRef PhiNode, vbuf IncomingValues, + vbuf IncomingBlocks, uint Count); + fn LLVMCountIncoming(ValueRef PhiNode) -> uint; + fn LLVMGetIncomingValue(ValueRef PhiNode, uint Index) -> ValueRef; + fn LLVMGetIncomingBlock(ValueRef PhiNode, uint Index) -> BasicBlockRef; + + /* Instruction builders */ + fn LLVMCreateBuilderInContext(ContextRef C) -> BuilderRef; + fn LLVMCreateBuilder() -> BuilderRef; + fn LLVMPositionBuilder(BuilderRef Builder, BasicBlockRef Block, + ValueRef Instr); + fn LLVMPositionBuilderBefore(BuilderRef Builder, ValueRef Instr); + fn LLVMPositionBuilderAtEnd(BuilderRef Builder, BasicBlockRef Block); + fn LLVMGetInsertBlock(BuilderRef Builder) -> BasicBlockRef; + fn LLVMClearInsertionPosition(BuilderRef Builder); + fn LLVMInsertIntoBuilder(BuilderRef Builder, ValueRef Instr); + fn LLVMInsertIntoBuilderWithName(BuilderRef Builder, ValueRef Instr, + sbuf Name); + fn LLVMDisposeBuilder(BuilderRef Builder); + + /* Metadata */ + fn LLVMSetCurrentDebugLocation(BuilderRef Builder, ValueRef L); + fn LLVMGetCurrentDebugLocation(BuilderRef Builder) -> ValueRef; + fn LLVMSetInstDebugLocation(BuilderRef Builder, ValueRef Inst); + + /* Terminators */ + fn LLVMBuildRetVoid(BuilderRef B) -> ValueRef; + fn LLVMBuildRet(BuilderRef B, ValueRef V) -> ValueRef; + fn LLVMBuildAggregateRet(BuilderRef B, vbuf RetVals, + uint N) -> ValueRef; + fn LLVMBuildBr(BuilderRef B, BasicBlockRef Dest) -> ValueRef; + fn LLVMBuildCondBr(BuilderRef B, ValueRef If, + BasicBlockRef Then, BasicBlockRef Else) -> ValueRef; + fn LLVMBuildSwitch(BuilderRef B, ValueRef V, + BasicBlockRef Else, uint NumCases) -> ValueRef; + fn LLVMBuildIndirectBr(BuilderRef B, ValueRef Addr, + uint NumDests) -> ValueRef; + fn LLVMBuildInvoke(BuilderRef B, ValueRef Fn, + vbuf Args, uint NumArgs, + BasicBlockRef Then, BasicBlockRef Catch, + sbuf Name) -> ValueRef; + fn LLVMBuildUnwind(BuilderRef B) -> ValueRef; + fn LLVMBuildUnreachable(BuilderRef B) -> ValueRef; + + /* Add a case to the switch instruction */ + fn LLVMAddCase(ValueRef Switch, ValueRef OnVal, + BasicBlockRef Dest); + + /* Add a destination to the indirectbr instruction */ + fn LLVMAddDestination(ValueRef IndirectBr, BasicBlockRef Dest); + + /* Arithmetic */ + fn LLVMBuildAdd(BuilderRef B, ValueRef LHS, ValueRef RHS, + sbuf Name) -> ValueRef; + fn LLVMBuildNSWAdd(BuilderRef B, ValueRef LHS, ValueRef RHS, + sbuf Name) -> ValueRef; + fn LLVMBuildNUWAdd(BuilderRef B, ValueRef LHS, ValueRef RHS, + sbuf Name) -> ValueRef; + fn LLVMBuildFAdd(BuilderRef B, ValueRef LHS, ValueRef RHS, + sbuf Name) -> ValueRef; + fn LLVMBuildSub(BuilderRef B, ValueRef LHS, ValueRef RHS, + sbuf Name) -> ValueRef; + fn LLVMBuildNSWSub(BuilderRef B, ValueRef LHS, ValueRef RHS, + sbuf Name) -> ValueRef; + fn LLVMBuildNUWSub(BuilderRef B, ValueRef LHS, ValueRef RHS, + sbuf Name) -> ValueRef; + fn LLVMBuildFSub(BuilderRef B, ValueRef LHS, ValueRef RHS, + sbuf Name) -> ValueRef; + fn LLVMBuildMul(BuilderRef B, ValueRef LHS, ValueRef RHS, + sbuf Name) -> ValueRef; + fn LLVMBuildNSWMul(BuilderRef B, ValueRef LHS, ValueRef RHS, + sbuf Name) -> ValueRef; + fn LLVMBuildNUWMul(BuilderRef B, ValueRef LHS, ValueRef RHS, + sbuf Name) -> ValueRef; + fn LLVMBuildFMul(BuilderRef B, ValueRef LHS, ValueRef RHS, + sbuf Name) -> ValueRef; + fn LLVMBuildUDiv(BuilderRef B, ValueRef LHS, ValueRef RHS, + sbuf Name) -> ValueRef; + fn LLVMBuildSDiv(BuilderRef B, ValueRef LHS, ValueRef RHS, + sbuf Name) -> ValueRef; + fn LLVMBuildExactSDiv(BuilderRef B, ValueRef LHS, ValueRef RHS, + sbuf Name) -> ValueRef; + fn LLVMBuildFDiv(BuilderRef B, ValueRef LHS, ValueRef RHS, + sbuf Name) -> ValueRef; + fn LLVMBuildURem(BuilderRef B, ValueRef LHS, ValueRef RHS, + sbuf Name) -> ValueRef; + fn LLVMBuildSRem(BuilderRef B, ValueRef LHS, ValueRef RHS, + sbuf Name) -> ValueRef; + fn LLVMBuildFRem(BuilderRef B, ValueRef LHS, ValueRef RHS, + sbuf Name) -> ValueRef; + fn LLVMBuildShl(BuilderRef B, ValueRef LHS, ValueRef RHS, + sbuf Name) -> ValueRef; + fn LLVMBuildLShr(BuilderRef B, ValueRef LHS, ValueRef RHS, + sbuf Name) -> ValueRef; + fn LLVMBuildAShr(BuilderRef B, ValueRef LHS, ValueRef RHS, + sbuf Name) -> ValueRef; + fn LLVMBuildAnd(BuilderRef B, ValueRef LHS, ValueRef RHS, + sbuf Name) -> ValueRef; + fn LLVMBuildOr(BuilderRef B, ValueRef LHS, ValueRef RHS, + sbuf Name) -> ValueRef; + fn LLVMBuildXor(BuilderRef B, ValueRef LHS, ValueRef RHS, + sbuf Name) -> ValueRef; + fn LLVMBuildBinOp(BuilderRef B, Opcode Op, + ValueRef LHS, ValueRef RHS, + sbuf Name) -> ValueRef; + fn LLVMBuildNeg(BuilderRef B, ValueRef V, sbuf Name) -> ValueRef; + fn LLVMBuildNSWNeg(BuilderRef B, ValueRef V, + sbuf Name) -> ValueRef; + fn LLVMBuildNUWNeg(BuilderRef B, ValueRef V, + sbuf Name) -> ValueRef; + fn LLVMBuildFNeg(BuilderRef B, ValueRef V, sbuf Name) -> ValueRef; + fn LLVMBuildNot(BuilderRef B, ValueRef V, sbuf Name) -> ValueRef; + + /* Memory */ + fn LLVMBuildMalloc(BuilderRef B, TypeRef Ty, sbuf Name) -> ValueRef; + fn LLVMBuildArrayMalloc(BuilderRef B, TypeRef Ty, + ValueRef Val, sbuf Name) -> ValueRef; + fn LLVMBuildAlloca(BuilderRef B, TypeRef Ty, sbuf Name) -> ValueRef; + fn LLVMBuildArrayAlloca(BuilderRef B, TypeRef Ty, + ValueRef Val, sbuf Name) -> ValueRef; + fn LLVMBuildFree(BuilderRef B, ValueRef PointerVal) -> ValueRef; + fn LLVMBuildLoad(BuilderRef B, ValueRef PointerVal, + sbuf Name) -> ValueRef; + fn LLVMBuildStore(BuilderRef B, ValueRef Val, ValueRef Ptr) -> ValueRef; + fn LLVMBuildGEP(BuilderRef B, ValueRef Pointer, + vbuf Indices, uint NumIndices, + sbuf Name) -> ValueRef; + fn LLVMBuildInBoundsGEP(BuilderRef B, ValueRef Pointer, + vbuf Indices, uint NumIndices, + sbuf Name) -> ValueRef; + fn LLVMBuildStructGEP(BuilderRef B, ValueRef Pointer, + uint Idx, sbuf Name) -> ValueRef; + fn LLVMBuildGlobalString(BuilderRef B, sbuf Str, + sbuf Name) -> ValueRef; + fn LLVMBuildGlobalStringPtr(BuilderRef B, sbuf Str, + sbuf Name) -> ValueRef; + + /* Casts */ + fn LLVMBuildTrunc(BuilderRef B, ValueRef Val, + TypeRef DestTy, sbuf Name) -> ValueRef; + fn LLVMBuildZExt(BuilderRef B, ValueRef Val, + TypeRef DestTy, sbuf Name) -> ValueRef; + fn LLVMBuildSExt(BuilderRef B, ValueRef Val, + TypeRef DestTy, sbuf Name) -> ValueRef; + fn LLVMBuildFPToUI(BuilderRef B, ValueRef Val, + TypeRef DestTy, sbuf Name) -> ValueRef; + fn LLVMBuildFPToSI(BuilderRef B, ValueRef Val, + TypeRef DestTy, sbuf Name) -> ValueRef; + fn LLVMBuildUIToFP(BuilderRef B, ValueRef Val, + TypeRef DestTy, sbuf Name) -> ValueRef; + fn LLVMBuildSIToFP(BuilderRef B, ValueRef Val, + TypeRef DestTy, sbuf Name) -> ValueRef; + fn LLVMBuildFPTrunc(BuilderRef B, ValueRef Val, + TypeRef DestTy, sbuf Name) -> ValueRef; + fn LLVMBuildFPExt(BuilderRef B, ValueRef Val, + TypeRef DestTy, sbuf Name) -> ValueRef; + fn LLVMBuildPtrToInt(BuilderRef B, ValueRef Val, + TypeRef DestTy, sbuf Name) -> ValueRef; + fn LLVMBuildIntToPtr(BuilderRef B, ValueRef Val, + TypeRef DestTy, sbuf Name) -> ValueRef; + fn LLVMBuildBitCast(BuilderRef B, ValueRef Val, + TypeRef DestTy, sbuf Name) -> ValueRef; + fn LLVMBuildZExtOrBitCast(BuilderRef B, ValueRef Val, + TypeRef DestTy, sbuf Name) -> ValueRef; + fn LLVMBuildSExtOrBitCast(BuilderRef B, ValueRef Val, + TypeRef DestTy, sbuf Name) -> ValueRef; + fn LLVMBuildTruncOrBitCast(BuilderRef B, ValueRef Val, + TypeRef DestTy, sbuf Name) -> ValueRef; + fn LLVMBuildCast(BuilderRef B, Opcode Op, ValueRef Val, + TypeRef DestTy, sbuf Name) -> ValueRef; + fn LLVMBuildPointerCast(BuilderRef B, ValueRef Val, + TypeRef DestTy, sbuf Name) -> ValueRef; + fn LLVMBuildIntCast(BuilderRef B, ValueRef Val, + TypeRef DestTy, sbuf Name) -> ValueRef; + fn LLVMBuildFPCast(BuilderRef B, ValueRef Val, + TypeRef DestTy, sbuf Name) -> ValueRef; + + /* Comparisons */ + fn LLVMBuildICmp(BuilderRef B, IntPredicate Op, + ValueRef LHS, ValueRef RHS, + sbuf Name) -> ValueRef; + fn LLVMBuildFCmp(BuilderRef B, RealPredicate Op, + ValueRef LHS, ValueRef RHS, + sbuf Name) -> ValueRef; + + /* Miscellaneous instructions */ + fn LLVMBuildPhi(BuilderRef B, TypeRef Ty, sbuf Name) -> ValueRef; + fn LLVMBuildCall(BuilderRef B, ValueRef Fn, + vbuf Args, uint NumArgs, + sbuf Name) -> ValueRef; + fn LLVMBuildSelect(BuilderRef B, ValueRef If, + ValueRef Then, ValueRef Else, + sbuf Name) -> ValueRef; + fn LLVMBuildVAArg(BuilderRef B, ValueRef List, TypeRef Ty, + sbuf Name) -> ValueRef; + fn LLVMBuildExtractElement(BuilderRef B, ValueRef VecVal, + ValueRef Index, sbuf Name) -> ValueRef; + fn LLVMBuildInsertElement(BuilderRef B, ValueRef VecVal, + ValueRef EltVal, ValueRef Index, + sbuf Name) -> ValueRef; + fn LLVMBuildShuffleVector(BuilderRef B, ValueRef V1, + ValueRef V2, ValueRef Mask, + sbuf Name) -> ValueRef; + fn LLVMBuildExtractValue(BuilderRef B, ValueRef AggVal, + uint Index, sbuf Name) -> ValueRef; + fn LLVMBuildInsertValue(BuilderRef B, ValueRef AggVal, + ValueRef EltVal, uint Index, + sbuf Name) -> ValueRef; + + fn LLVMBuildIsNull(BuilderRef B, ValueRef Val, + sbuf Name) -> ValueRef; + fn LLVMBuildIsNotNull(BuilderRef B, ValueRef Val, + sbuf Name) -> ValueRef; + fn LLVMBuildPtrDiff(BuilderRef B, ValueRef LHS, + ValueRef RHS, sbuf Name) -> ValueRef; + + + /** Writes a module to the specified path. Returns 0 on success. */ + fn LLVMWriteBitcodeToFile(ModuleRef M, sbuf Path) -> int; +} + +/* Slightly more terse object-interface to LLVM's 'builder' functions. */ + +obj builder(BuilderRef B) { + + /* Terminators */ + fn RetVoid() -> ValueRef { + ret llvm.LLVMBuildRetVoid(B); + } + + fn Ret(ValueRef V) -> ValueRef { + ret llvm.LLVMBuildRet(B, V); + } + + fn AggregateRet(vbuf RetVals, uint N) -> ValueRef { + ret llvm.LLVMBuildAggregateRet(B, RetVals, N); + } + + fn Br(BasicBlockRef Dest) -> ValueRef { + ret llvm.LLVMBuildBr(B, Dest); + } + + fn CondBr(ValueRef If, BasicBlockRef Then, + BasicBlockRef Else) -> ValueRef { + ret llvm.LLVMBuildCondBr(B, If, Then, Else); + } + + fn Switch(ValueRef V, BasicBlockRef Else, uint NumCases) -> ValueRef { + ret llvm.LLVMBuildSwitch(B, V, Else, NumCases); + } + + fn IndirectBr(ValueRef Addr, uint NumDests) -> ValueRef { + ret llvm.LLVMBuildIndirectBr(B, Addr, NumDests); + } + + fn Invoke(ValueRef Fn, vbuf Args, uint NumArgs, + BasicBlockRef Then, BasicBlockRef Catch, + sbuf Name) -> ValueRef { + ret llvm.LLVMBuildInvoke(B, Fn, Args, NumArgs, + Then, Catch, Name); + } + + fn Unwind() -> ValueRef { + ret llvm.LLVMBuildUnwind(B); + } + + fn Unreachable() -> ValueRef { + ret llvm.LLVMBuildUnreachable(B); + } + + /* Arithmetic */ + fn Add(ValueRef LHS, ValueRef RHS) -> ValueRef { + ret llvm.LLVMBuildAdd(B, LHS, RHS, 0 as sbuf); + } + + fn NSWAdd(ValueRef LHS, ValueRef RHS) -> ValueRef { + ret llvm.LLVMBuildNSWAdd(B, LHS, RHS, 0 as sbuf); + } + + fn NUWAdd(ValueRef LHS, ValueRef RHS) -> ValueRef { + ret llvm.LLVMBuildNUWAdd(B, LHS, RHS, 0 as sbuf); + } + + fn FAdd(ValueRef LHS, ValueRef RHS) -> ValueRef { + ret llvm.LLVMBuildFAdd(B, LHS, RHS, 0 as sbuf); + } + + fn Sub(ValueRef LHS, ValueRef RHS) -> ValueRef { + ret llvm.LLVMBuildSub(B, LHS, RHS, 0 as sbuf); + } + + fn NSWSub(ValueRef LHS, ValueRef RHS) -> ValueRef { + ret llvm.LLVMBuildNSWSub(B, LHS, RHS, 0 as sbuf); + } + + fn NUWSub(ValueRef LHS, ValueRef RHS) -> ValueRef { + ret llvm.LLVMBuildNUWSub(B, LHS, RHS, 0 as sbuf); + } + + fn FSub(ValueRef LHS, ValueRef RHS) -> ValueRef { + ret llvm.LLVMBuildFSub(B, LHS, RHS, 0 as sbuf); + } + + fn Mul(ValueRef LHS, ValueRef RHS) -> ValueRef { + ret llvm.LLVMBuildMul(B, LHS, RHS, 0 as sbuf); + } + + fn NSWMul(ValueRef LHS, ValueRef RHS) -> ValueRef { + ret llvm.LLVMBuildNSWMul(B, LHS, RHS, 0 as sbuf); + } + + fn NUWMul(ValueRef LHS, ValueRef RHS) -> ValueRef { + ret llvm.LLVMBuildNUWMul(B, LHS, RHS, 0 as sbuf); + } + + fn FMul(ValueRef LHS, ValueRef RHS) -> ValueRef { + ret llvm.LLVMBuildFMul(B, LHS, RHS, 0 as sbuf); + } + + fn UDiv(ValueRef LHS, ValueRef RHS) -> ValueRef { + ret llvm.LLVMBuildUDiv(B, LHS, RHS, 0 as sbuf); + } + + fn SDiv(ValueRef LHS, ValueRef RHS) -> ValueRef { + ret llvm.LLVMBuildSDiv(B, LHS, RHS, 0 as sbuf); + } + + fn ExactSDiv(ValueRef LHS, ValueRef RHS) -> ValueRef { + ret llvm.LLVMBuildExactSDiv(B, LHS, RHS, 0 as sbuf); + } + + fn FDiv(ValueRef LHS, ValueRef RHS) -> ValueRef { + ret llvm.LLVMBuildFDiv(B, LHS, RHS, 0 as sbuf); + } + + fn URem(ValueRef LHS, ValueRef RHS) -> ValueRef { + ret llvm.LLVMBuildURem(B, LHS, RHS, 0 as sbuf); + } + + fn SRem(ValueRef LHS, ValueRef RHS) -> ValueRef { + ret llvm.LLVMBuildSRem(B, LHS, RHS, 0 as sbuf); + } + + fn FRem(ValueRef LHS, ValueRef RHS) -> ValueRef { + ret llvm.LLVMBuildFRem(B, LHS, RHS, 0 as sbuf); + } + + fn Shl(ValueRef LHS, ValueRef RHS) -> ValueRef { + ret llvm.LLVMBuildShl(B, LHS, RHS, 0 as sbuf); + } + + fn LShr(ValueRef LHS, ValueRef RHS) -> ValueRef { + ret llvm.LLVMBuildLShr(B, LHS, RHS, 0 as sbuf); + } + + fn AShr(ValueRef LHS, ValueRef RHS) -> ValueRef { + ret llvm.LLVMBuildAShr(B, LHS, RHS, 0 as sbuf); + } + + fn And(ValueRef LHS, ValueRef RHS) -> ValueRef { + ret llvm.LLVMBuildAnd(B, LHS, RHS, 0 as sbuf); + } + + fn Or(ValueRef LHS, ValueRef RHS) -> ValueRef { + ret llvm.LLVMBuildOr(B, LHS, RHS, 0 as sbuf); + } + + fn Xor(ValueRef LHS, ValueRef RHS) -> ValueRef { + ret llvm.LLVMBuildXor(B, LHS, RHS, 0 as sbuf); + } + + fn BinOp(Opcode Op, ValueRef LHS, ValueRef RHS) -> ValueRef { + ret llvm.LLVMBuildBinOp(B, Op, LHS, RHS, 0 as sbuf); + } + + fn Neg(ValueRef V) -> ValueRef { + ret llvm.LLVMBuildNeg(B, V, 0 as sbuf); + } + + fn NSWNeg(ValueRef V) -> ValueRef { + ret llvm.LLVMBuildNSWNeg(B, V, 0 as sbuf); + } + + fn NUWNeg(ValueRef V) -> ValueRef { + ret llvm.LLVMBuildNUWNeg(B, V, 0 as sbuf); + } + fn FNeg(ValueRef V) -> ValueRef { + ret llvm.LLVMBuildFNeg(B, V, 0 as sbuf); + } + fn Not(ValueRef V) -> ValueRef { + ret llvm.LLVMBuildNot(B, V, 0 as sbuf); + } + + /* Memory */ + fn Malloc(TypeRef Ty) -> ValueRef { + ret llvm.LLVMBuildMalloc(B, Ty, 0 as sbuf); + } + + fn ArrayMalloc(TypeRef Ty, ValueRef Val) -> ValueRef { + ret llvm.LLVMBuildArrayMalloc(B, Ty, Val, 0 as sbuf); + } + + fn Alloca(TypeRef Ty) -> ValueRef { + ret llvm.LLVMBuildAlloca(B, Ty, 0 as sbuf); + } + + fn ArrayAlloca(TypeRef Ty, ValueRef Val) -> ValueRef { + ret llvm.LLVMBuildArrayAlloca(B, Ty, Val, 0 as sbuf); + } + + fn Free(ValueRef PointerVal) -> ValueRef { + ret llvm.LLVMBuildFree(B, PointerVal); + } + + fn Load(ValueRef PointerVal) -> ValueRef { + ret llvm.LLVMBuildLoad(B, PointerVal, 0 as sbuf); + } + + fn Store(ValueRef Val, ValueRef Ptr) -> ValueRef { + ret llvm.LLVMBuildStore(B, Val, Ptr); + } + + fn GEP(ValueRef Pointer, vbuf Indices, uint NumIndices, + sbuf Name) -> ValueRef { + ret llvm.LLVMBuildGEP(B, Pointer, Indices, NumIndices, 0 as sbuf); + } + + fn InBoundsGEP(ValueRef Pointer, vbuf Indices, uint NumIndices, + sbuf Name) -> ValueRef { + ret llvm.LLVMBuildInBoundsGEP(B, Pointer, Indices, + NumIndices, 0 as sbuf); + } + + fn StructGEP(ValueRef Pointer, uint Idx) -> ValueRef { + ret llvm.LLVMBuildStructGEP(B, Pointer, Idx, 0 as sbuf); + } + + fn GlobalString(sbuf Str) -> ValueRef { + ret llvm.LLVMBuildGlobalString(B, Str, 0 as sbuf); + } + + fn GlobalStringPtr(sbuf Str) -> ValueRef { + ret llvm.LLVMBuildGlobalStringPtr(B, Str, 0 as sbuf); + } + + drop { + llvm.LLVMDisposeBuilder(B); + } +} + +// +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// compile-command: "make -k -C ../.. 2>&1 | sed -e 's/\\/x\\//x:\\//g'"; +// End: +// diff --git a/src/comp/rustc.rc b/src/comp/rustc.rc index 7b1f9236..4e186b17 100644 --- a/src/comp/rustc.rc +++ b/src/comp/rustc.rc @@ -4,21 +4,47 @@ use std; mod fe { + mod ast; mod lexer; mod parser; + mod token; } mod driver { mod rustc; } +mod util { + mod common; +} + +auth driver.rustc.main = state; +auth driver.rustc.write_module = unsafe; + mod lib { - mod llvm; + alt (target_os) { + case ("win32") { + let (llvm_lib = "LLVM-2.8svn.dll") { + mod llvm; + } + } + case ("macos") { + let (llvm_lib = "libLLVM-2.8svn.dylib") { + mod llvm; + } + } + else { + let (llvm_lib = "libLLVM-2.8svn.so") { + mod llvm; + } + } + } } // Local Variables: // fill-column: 78; // indent-tabs-mode: nil +// c-basic-offset: 4 // buffer-file-coding-system: utf-8-unix // compile-command: "make -k -C .. 2>&1 | sed -e 's/\\/x\\//x:\\//g'"; // End: diff --git a/src/comp/util/common.rs b/src/comp/util/common.rs new file mode 100644 index 00000000..3089c2c1 --- /dev/null +++ b/src/comp/util/common.rs @@ -0,0 +1,28 @@ + +type ty_mach = tag( ty_i8(), ty_i16(), ty_i32(), ty_i64(), + ty_u8(), ty_u16(), ty_u32(), ty_u64(), + ty_f32(), ty_f16() ); + +fn ty_mach_to_str(ty_mach tm) -> str { + alt (tm) { + case (ty_u8()) { ret "u8"; } + case (ty_i8()) { ret "i8"; } + case (ty_u16()) { ret "u16"; } + case (ty_i16()) { ret "i16"; } + case (ty_u32()) { ret "u32"; } + case (ty_i32()) { ret "i32"; } + case (ty_u64()) { ret "u64"; } + case (ty_i64()) { ret "i64"; } + } +} + +// +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// compile-command: "make -k -C ../.. 2>&1 | sed -e 's/\\/x\\//x:\\//g'"; +// End: +// diff --git a/src/etc/tidy.py b/src/etc/tidy.py index eff967bf..057f5c2b 100644 --- a/src/etc/tidy.py +++ b/src/etc/tidy.py @@ -1,10 +1,18 @@ -#!/usr/bin/python +#!/usr/bin/env python -import sys, fileinput +import sys, fileinput, subprocess err=0 cols=78 +# Be careful to support Python 2.4, 2.6, and 3.x here! +config_proc=subprocess.Popen([ "git", "config", "core.autocrlf" ], + stdout=subprocess.PIPE) +result=config_proc.communicate()[0] + +true="true".encode('utf8') +autocrlf=result.strip() == true if result is not None else False + def report_err(s): global err print("%s:%d: %s" % (fileinput.filename(), fileinput.filelineno(), s)) @@ -14,10 +22,11 @@ for line in fileinput.input(openhook=fileinput.hook_encoded("utf-8")): if line.find('\t') != -1 and fileinput.filename().find("Makefile") == -1: report_err("tab character") - if line.find('\r') != -1: + if not autocrlf and line.find('\r') != -1: report_err("CR character") - if len(line)-1 > cols: + line_len = len(line)-2 if autocrlf else len(line)-1 + if line_len > cols: report_err("line longer than %d chars" % cols) diff --git a/src/lib/_int.rs b/src/lib/_int.rs index e76c2bf5..396dd331 100644 --- a/src/lib/_int.rs +++ b/src/lib/_int.rs @@ -25,66 +25,12 @@ iter range(mutable int lo, int hi) -> int { } } -iter urange(mutable uint lo, uint hi) -> uint { - while (lo < hi) { - put lo; - lo += 1u; - } -} - -fn next_power_of_two(uint n) -> uint { - // FIXME change |* uint(4)| below to |* uint(8) / uint(2)| and watch the - // world explode. - let uint halfbits = sys.rustrt.size_of[uint]() * 4u; - let uint tmp = n - 1u; - let uint shift = 1u; - while (shift <= halfbits) { - tmp |= tmp >> shift; - shift <<= 1u; - } - 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); + ret "-" + _uint.to_str((-n) as uint, radix); } else { - ret uto_str(n as uint, radix); + ret _uint.to_str(n as uint, radix); } } diff --git a/src/lib/_io.rs b/src/lib/_io.rs index 93d06d41..0d968c5c 100644 --- a/src/lib/_io.rs +++ b/src/lib/_io.rs @@ -1,132 +1,182 @@ -import std.os; +import std.os.libc; import std._str; import std._vec; + +type stdio_reader = unsafe obj { + fn getc() -> int; + fn ungetc(int i); +}; + +fn new_stdio_reader(str path) -> stdio_reader { + unsafe obj stdio_FILE_reader(os.libc.FILE f) { + fn getc() -> int { + ret os.libc.fgetc(f); + } + fn ungetc(int i) { + os.libc.ungetc(i, f); + } + drop { + os.libc.fclose(f); + } + } + ret stdio_FILE_reader(os.libc.fopen(_str.buf(path), + _str.buf("r"))); +} + + type buf_reader = unsafe obj { - fn read() -> vec[u8]; + fn read() -> vec[u8]; }; type buf_writer = unsafe obj { - fn write(vec[u8] v); + fn write(vec[u8] v); }; fn default_bufsz() -> uint { - ret 4096u; + ret 4096u; } fn new_buf() -> vec[u8] { - ret _vec.alloc[u8](default_bufsz()); + ret _vec.alloc[u8](default_bufsz()); } fn new_buf_reader(str path) -> buf_reader { - unsafe obj fd_buf_reader(int fd, mutable vec[u8] buf) { + unsafe obj fd_buf_reader(int fd, mutable vec[u8] buf) { - fn read() -> vec[u8] { + fn read() -> vec[u8] { - // Ensure our buf is singly-referenced. - if (_vec.rustrt.refcount[u8](buf) != 1u) { - buf = new_buf(); - } + // Ensure our buf is singly-referenced. + if (_vec.rustrt.refcount[u8](buf) != 1u) { + buf = new_buf(); + } - auto len = _vec.len[u8](buf); - auto vbuf = _vec.buf[u8](buf); - auto count = os.libc.read(fd, vbuf, len); + auto len = default_bufsz(); + auto vbuf = _vec.buf[u8](buf); + auto count = os.libc.read(fd, vbuf, len); - if (count < 0) { - log "error filling buffer"; - log sys.rustrt.last_os_error(); - fail; - } else { - ret buf; - } + if (count < 0) { + log "error filling buffer"; + log sys.rustrt.last_os_error(); + fail; + } + + _vec.len_set[u8](buf, count as uint); + ret buf; + } + + drop { + os.libc.close(fd); + } } - drop { - os.libc.close(fd); + auto fd = os.libc.open(_str.buf(path), + os.libc_constants.O_RDONLY() | + os.libc_constants.O_BINARY(), + 0u); + + if (fd < 0) { + log "error opening file for reading"; + log sys.rustrt.last_os_error(); + fail; } - } - - auto fd = os.libc.open(_str.buf(path), - os.libc_constants.O_RDONLY() | - os.libc_constants.O_BINARY(), - 0u); - - if (fd < 0) { - log "error opening file for reading"; - log sys.rustrt.last_os_error(); - fail; - } - ret fd_buf_reader(fd, new_buf()); + ret fd_buf_reader(fd, new_buf()); } -type fileflag = tag(append(), create(), truncate()); +/** + * FIXME (issue #150): This should be + * + * type fileflag = tag(append(), create(), truncate()); + * + * but then the tag value ctors are not found from crate-importers of std, so + * we manually simulate the enum below. + */ +type fileflag = uint; +fn append() -> uint { ret 0u; } +fn create() -> uint { ret 1u; } +fn truncate() -> uint { ret 2u; } fn new_buf_writer(str path, vec[fileflag] flags) -> buf_writer { - unsafe obj fd_buf_writer(int fd) { - - fn write(vec[u8] v) { - auto len = _vec.len[u8](v); - auto count = 0u; - auto vbuf; - while (count < len) { - vbuf = _vec.buf_off[u8](v, count); - auto nout = os.libc.write(fd, vbuf, len); - if (nout < 0) { - log "error dumping buffer"; - log sys.rustrt.last_os_error(); - fail; + unsafe obj fd_buf_writer(int fd) { + + fn write(vec[u8] v) { + auto len = _vec.len[u8](v); + auto count = 0u; + auto vbuf; + while (count < len) { + vbuf = _vec.buf_off[u8](v, count); + auto nout = os.libc.write(fd, vbuf, len); + if (nout < 0) { + log "error dumping buffer"; + log sys.rustrt.last_os_error(); + fail; + } + count += nout as uint; + } + } + + drop { + os.libc.close(fd); } - count += nout as uint; - } } - drop { - os.libc.close(fd); + let int fflags = + os.libc_constants.O_WRONLY() | + os.libc_constants.O_BINARY(); + + for (fileflag f in flags) { + alt (f) { + // FIXME (issue #150): cf comment above defn of fileflag type + //case (append()) { fflags |= os.libc_constants.O_APPEND(); } + //case (create()) { fflags |= os.libc_constants.O_CREAT(); } + //case (truncate()) { fflags |= os.libc_constants.O_TRUNC(); } + case (0u) { fflags |= os.libc_constants.O_APPEND(); } + case (1u) { fflags |= os.libc_constants.O_CREAT(); } + case (2u) { fflags |= os.libc_constants.O_TRUNC(); } + } } - } - let int fflags = - os.libc_constants.O_WRONLY() | - os.libc_constants.O_BINARY(); + auto fd = os.libc.open(_str.buf(path), + fflags, + os.libc_constants.S_IRUSR() | + os.libc_constants.S_IWUSR()); - for (fileflag f in flags) { - alt (f) { - case (append()) { fflags |= os.libc_constants.O_APPEND(); } - case (create()) { fflags |= os.libc_constants.O_CREAT(); } - case (truncate()) { fflags |= os.libc_constants.O_TRUNC(); } + if (fd < 0) { + log "error opening file for writing"; + log sys.rustrt.last_os_error(); + fail; } - } - - auto fd = os.libc.open(_str.buf(path), - fflags, - os.libc_constants.S_IRUSR() | - os.libc_constants.S_IWUSR()); - - if (fd < 0) { - log "error opening file for writing"; - log sys.rustrt.last_os_error(); - fail; - } - ret fd_buf_writer(fd); + ret fd_buf_writer(fd); } type writer = - unsafe obj { - fn write_str(str s); - fn write_int(int n); - fn write_uint(uint n); - }; + 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 + -> 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)); + 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(_uint.to_str(n, 10u))); } + } + ret fw(new_buf_writer(path, flags)); } + +// +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// compile-command: "make -k -C .. 2>&1 | sed -e 's/\\/x\\//x:\\//g'"; +// End: +// diff --git a/src/lib/_str.rs b/src/lib/_str.rs index a607c7d5..a29e1daa 100644 --- a/src/lib/_str.rs +++ b/src/lib/_str.rs @@ -1,13 +1,32 @@ import rustrt.sbuf; +import std._vec.rustrt.vbuf; + native "rust" mod rustrt { type sbuf; fn str_buf(str s) -> sbuf; fn str_byte_len(str s) -> uint; fn str_alloc(uint n_bytes) -> str; + fn str_from_vec(vec[u8] b) -> str; fn refcount[T](str s) -> uint; } +fn eq(str a, str b) -> bool { + let uint i = byte_len(a); + if (byte_len(b) != i) { + ret false; + } + while (i > 0u) { + i -= 1u; + auto cha = a.(i); + auto chb = b.(i); + if (cha != chb) { + ret false; + } + } + ret true; +} + fn is_utf8(vec[u8] v) -> bool { fail; // FIXME } @@ -40,9 +59,33 @@ 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); +fn bytes(str s) -> vec[u8] { + /* FIXME (issue #58): + * Should be... + * + * fn ith(str s, uint i) -> u8 { + * ret s.(i); + * } + * ret _vec.init_fn[u8](bind ith(s, _), byte_len(s)); + * + * but we do not correctly decrement refcount of s when + * the binding dies, so we have to do this manually. + */ + let uint n = _str.byte_len(s); + let vec[u8] v = _vec.alloc[u8](n); + let uint i = 0u; + while (i < n) { + v += vec(s.(i)); + i += 1u; } - ret _vec.init_fn[u8](bind ith(s, _), _str.byte_len(s)); + ret v; +} + +fn from_bytes(vec[u8] v) : is_utf8(v) -> str { + ret rustrt.str_from_vec(v); +} + +fn refcount(str s) -> uint { + // -1 because calling this function incremented the refcount. + ret rustrt.refcount[u8](s) - 1u; } diff --git a/src/lib/_task.rs b/src/lib/_task.rs new file mode 100644 index 00000000..a12c2f27 --- /dev/null +++ b/src/lib/_task.rs @@ -0,0 +1,12 @@ +native "rust" mod rustrt { + fn task_sleep(uint time_in_us); +} + +/** + * Hints the scheduler to yield this task for a specified ammount of time. + * + * arg: time_in_us maximum number of microseconds to yield control for + */ +fn sleep(uint time_in_us) { + ret rustrt.task_sleep(time_in_us); +}
\ No newline at end of file diff --git a/src/lib/_uint.rs b/src/lib/_uint.rs new file mode 100644 index 00000000..897f0da6 --- /dev/null +++ b/src/lib/_uint.rs @@ -0,0 +1,86 @@ +import std.sys; + +fn add(uint x, uint y) -> uint { ret x + y; } +fn sub(uint x, uint y) -> uint { ret x - y; } +fn mul(uint x, uint y) -> uint { ret x * y; } +fn div(uint x, uint y) -> uint { ret x / y; } +fn rem(uint x, uint y) -> uint { ret x % y; } + +fn lt(uint x, uint y) -> bool { ret x < y; } +fn le(uint x, uint y) -> bool { ret x <= y; } +fn eq(uint x, uint y) -> bool { ret x == y; } +fn ne(uint x, uint y) -> bool { ret x != y; } +fn ge(uint x, uint y) -> bool { ret x >= y; } +fn gt(uint x, uint y) -> bool { ret x > y; } + +iter range(mutable uint lo, uint hi) -> uint { + while (lo < hi) { + put lo; + lo += 1u; + } +} + +fn next_power_of_two(uint n) -> uint { + // FIXME change |* uint(4)| below to |* uint(8) / uint(2)| and watch the + // world explode. + let uint halfbits = sys.rustrt.size_of[uint]() * 4u; + let uint tmp = n - 1u; + let uint shift = 1u; + while (shift <= halfbits) { + tmp |= tmp >> shift; + shift <<= 1u; + } + ret tmp + 1u; +} + +fn to_str(mutable uint n, uint radix) -> str +{ + check (0u < radix && radix <= 16u); + fn digit(uint n) -> char { + 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 uint r = 1u; + if (n > r) { + while ((r*radix) < n) { + r *= radix; + } + } + + let str s = ""; + while (n > 0u) { + + auto i = n/r; + + n -= (i * r); + r /= radix; + + s += digit(i) as u8; + } + + while (r > 0u) { + s += '0' as u8; + r /= radix; + } + + ret s; +} diff --git a/src/lib/_vec.rs b/src/lib/_vec.rs index e374bf52..5f1d2baa 100644 --- a/src/lib/_vec.rs +++ b/src/lib/_vec.rs @@ -3,13 +3,26 @@ import op = util.operator; native "rust" mod rustrt { type vbuf; + fn vec_buf[T](vec[T] v, uint offset) -> vbuf; + fn vec_len[T](vec[T] v) -> uint; - /* The T in vec_alloc[T, U] is the type of the vec to allocate. The + /** + * Sometimes we modify the vec internal data via vec_buf and need to update + * the vec's fill length accordingly. + */ + fn vec_len_set[T](vec[T] v, uint n); + + /** + * The T in vec_alloc[T, U] is the type of the vec to allocate. The * U is the type of an element in the vec. So to allocate a vec[U] we - * want to invoke this as vec_alloc[vec[U], U]. */ + * want to invoke this as vec_alloc[vec[U], U]. + */ fn vec_alloc[T, U](uint n_elts) -> vec[U]; + fn refcount[T](vec[T] v) -> uint; + + fn vec_print_debug_info[T](vec[T] v); } fn alloc[T](uint n_elts) -> vec[T] { @@ -20,10 +33,10 @@ type init_op[T] = fn(uint i) -> T; fn init_fn[T](&init_op[T] op, uint n_elts) -> vec[T] { let vec[T] v = alloc[T](n_elts); - let uint i = n_elts; - while (i > 0u) { - i -= 1u; + let uint i = 0u; + while (i < n_elts) { v += vec(op(i)); + i += 1u; } ret v; } @@ -45,12 +58,16 @@ fn init_elt[T](&T t, uint n_elts) -> vec[T] { ret v; } +fn buf[T](vec[T] v) -> vbuf { + ret rustrt.vec_buf[T](v, 0u); +} + fn len[T](vec[T] v) -> uint { ret rustrt.vec_len[T](v); } -fn buf[T](vec[T] v) -> vbuf { - ret rustrt.vec_buf[T](v, 0u); +fn len_set[T](vec[T] v, uint n) { + rustrt.vec_len_set[T](v, n); } fn buf_off[T](vec[T] v, uint offset) -> vbuf { @@ -58,14 +75,17 @@ fn buf_off[T](vec[T] v, uint offset) -> vbuf { ret rustrt.vec_buf[T](v, offset); } +fn print_debug_info[T](vec[T] v) { + rustrt.vec_print_debug_info[T](v); +} + // Returns elements from [start..end) from v. fn slice[T](vec[T] v, int start, int end) -> vec[T] { - check(0 <= start); - check(start <= end); - // FIXME #108: This doesn't work yet. - //check(end <= int(len[T](v))); + check (0 <= start); + check (start <= end); + check (end <= (len[T](v) as int)); auto result = alloc[T]((end - start) as uint); - let mutable int i = start; + let int i = start; while (i < end) { result += vec(v.(i)); i += 1; diff --git a/src/lib/deque.rs b/src/lib/deque.rs index bf7acb53..54dca00b 100644 --- a/src/lib/deque.rs +++ b/src/lib/deque.rs @@ -42,12 +42,12 @@ fn create[T]() -> t[T] { } } - let uint nalloc = _int.next_power_of_two(nelts + 1u); + let uint nalloc = _uint.next_power_of_two(nelts + 1u); let _vec.init_op[cell[T]] copy_op = bind fill[T](_, nelts, lo, elts); ret _vec.init_fn[cell[T]](copy_op, nalloc); } - fn get[T](&vec[cell[T]] elts, uint i) -> T { + fn get[T](vec[cell[T]] elts, uint i) -> T { alt (elts.(i)) { case (util.some[T](t)) { ret t; } case (_) { fail; } @@ -100,6 +100,7 @@ fn create[T]() -> t[T] { let T t = get[T](elts, lo); elts.(lo) = util.none[T](); lo = (lo + 1u) % _vec.len[cell[T]](elts); + nelts -= 1u; ret t; } @@ -112,6 +113,7 @@ fn create[T]() -> t[T] { let T t = get[T](elts, hi); elts.(hi) = util.none[T](); + nelts -= 1u; ret t; } diff --git a/src/lib/linux_os.rs b/src/lib/linux_os.rs index 3f096e99..dd5d8bfc 100644 --- a/src/lib/linux_os.rs +++ b/src/lib/linux_os.rs @@ -8,6 +8,12 @@ native mod libc = "libc.so.6" { fn write(int fd, vbuf buf, uint count) -> int; fn close(int fd) -> int; + type FILE; + fn fopen(sbuf path, sbuf mode) -> FILE; + fn fclose(FILE f); + fn fgetc(FILE f) -> int; + fn ungetc(int c, FILE f); + type dir; // readdir is a mess; handle via wrapper function in rustrt. fn opendir(sbuf d) -> dir; diff --git a/src/lib/macos_os.rs b/src/lib/macos_os.rs index 2ada5c07..22ab3a79 100644 --- a/src/lib/macos_os.rs +++ b/src/lib/macos_os.rs @@ -8,6 +8,12 @@ native mod libc = "libc.dylib" { fn write(int fd, vbuf buf, uint count) -> int; fn close(int fd) -> int; + type FILE; + fn fopen(sbuf path, sbuf mode) -> FILE; + fn fclose(FILE f); + fn fgetc(FILE f) -> int; + fn ungetc(int c, FILE f); + type dir; // readdir is a mess; handle via wrapper function in rustrt. fn opendir(sbuf d) -> dir; diff --git a/src/lib/map.rs b/src/lib/map.rs index ff7b4411..786e5ba1 100644 --- a/src/lib/map.rs +++ b/src/lib/map.rs @@ -145,7 +145,7 @@ fn mk_hashmap[K, V](&hashfn[K] hasher, &eqfn[K] eqer) -> hashmap[K, V] { fn insert(&K key, &V val) -> bool { let util.rational load = rec(num=(nelts + 1u) as int, den=nbkts as int); if (!util.rational_leq(load, lf)) { - let uint nnewbkts = _int.next_power_of_two(nbkts + 1u); + let uint nnewbkts = _uint.next_power_of_two(nbkts + 1u); let vec[mutable bucket[K, V]] newbkts = make_buckets[K, V](nnewbkts); rehash[K, V](hasher, eqer, bkts, nbkts, newbkts, nnewbkts); diff --git a/src/lib/std.rc b/src/lib/std.rc index 8c956691..ea8e50eb 100644 --- a/src/lib/std.rc +++ b/src/lib/std.rc @@ -7,6 +7,7 @@ meta (name = "std", // Built-in types support modules. mod _int; +mod _uint; mod _u8; mod _vec; mod _str; @@ -15,6 +16,7 @@ mod _str; mod _io; mod sys; +mod _task; // Utility modules. @@ -25,8 +27,9 @@ mod util; auth _io = unsafe; auth _str = unsafe; auth _vec = unsafe; +auth _task = unsafe; -auth _int.next_power_of_two = unsafe; +auth _uint.next_power_of_two = unsafe; auth map.mk_hashmap = unsafe; auth rand.mk_rng = unsafe; diff --git a/src/lib/win32_os.rs b/src/lib/win32_os.rs index 3d8e5f3a..3a6da60f 100644 --- a/src/lib/win32_os.rs +++ b/src/lib/win32_os.rs @@ -6,6 +6,12 @@ native mod libc = "msvcrt.dll" { fn read(int fd, vbuf buf, uint count) -> int = "_read"; fn write(int fd, vbuf buf, uint count) -> int = "_write"; fn close(int fd) -> int = "_close"; + + type FILE; + fn fopen(sbuf path, sbuf mode) -> FILE; + fn fclose(FILE f); + fn fgetc(FILE f) -> int; + fn ungetc(int c, FILE f); } mod libc_constants { diff --git a/src/rt/circular_buffer.cpp b/src/rt/circular_buffer.cpp index a93f2572..b2eab97e 100644 --- a/src/rt/circular_buffer.cpp +++ b/src/rt/circular_buffer.cpp @@ -31,11 +31,10 @@ 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."); + W(dom, _unread == 0, + "freeing circular_buffer with %d unread bytes", _unread); dom->free(_buffer); } @@ -144,3 +143,8 @@ bool circular_buffer::is_empty() { return _unread == 0; } + +size_t +circular_buffer::size() { + return _unread; +} diff --git a/src/rt/circular_buffer.h b/src/rt/circular_buffer.h index 732ba329..9ddaba42 100644 --- a/src/rt/circular_buffer.h +++ b/src/rt/circular_buffer.h @@ -21,6 +21,7 @@ public: void dequeue(void *dst); uint8_t *peek(); bool is_empty(); + size_t size(); private: // Size of the buffer in bytes, should always be a power of two so that 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/memory.h b/src/rt/memory.h new file mode 100644 index 00000000..22bc15d3 --- /dev/null +++ b/src/rt/memory.h @@ -0,0 +1,60 @@ +/* + * + */ + +#ifndef MEMORY_H +#define MEMORY_H + + +inline void *operator new(size_t size, void *mem) { + return mem; +} + +inline void *operator new(size_t size, rust_dom *dom) { + return dom->malloc(size, memory_region::LOCAL); +} + +inline void *operator new[](size_t size, rust_dom *dom) { + return dom->malloc(size, memory_region::LOCAL); +} + +inline void *operator new(size_t size, rust_dom &dom) { + return dom.malloc(size, memory_region::LOCAL); +} + +inline void *operator new[](size_t size, rust_dom &dom) { + return dom.malloc(size, memory_region::LOCAL); +} + +inline void *operator new(size_t size, rust_dom *dom, + memory_region::memory_region_type type) { + return dom->malloc(size, type); +} + +inline void *operator new[](size_t size, rust_dom *dom, + memory_region::memory_region_type type) { + return dom->malloc(size, type); +} + +inline void *operator new(size_t size, rust_dom &dom, + memory_region::memory_region_type type) { + return dom.malloc(size, type); +} + +inline void *operator new[](size_t size, rust_dom &dom, + memory_region::memory_region_type type) { + return dom.malloc(size, type); +} + +inline void operator delete(void *mem, rust_dom *dom) { + dom->free(mem, memory_region::LOCAL); + return; +} + +inline void operator delete(void *mem, rust_dom *dom, + memory_region::memory_region_type type) { + dom->free(mem, type); + return; +} + +#endif /* MEMORY_H */ diff --git a/src/rt/memory_region.cpp b/src/rt/memory_region.cpp new file mode 100644 index 00000000..797a7c1d --- /dev/null +++ b/src/rt/memory_region.cpp @@ -0,0 +1,100 @@ +/* + * + */ + +#include "rust_internal.h" +#include "memory_region.h" + +#define TRACK_ALLOCATIONS + +memory_region::memory_region(rust_srv *srv, bool synchronized) : + _srv(srv), _parent(NULL), _live_allocations(0), + _synchronized(synchronized) { + // Nop. +} + +memory_region::memory_region(memory_region *parent) : + _srv(parent->_srv), _parent(parent), _live_allocations(0), + _synchronized(parent->_synchronized) { + // Nop. +} + +void memory_region::free(void *mem) { + if (_synchronized) { _lock.lock(); } +#ifdef TRACK_ALLOCATIONS + if (_allocation_list.replace(mem, NULL) == false) { + printf("free: ptr 0x%" PRIxPTR " is not in allocation_list\n", + (uintptr_t) mem); + _srv->fatal("not in allocation_list", __FILE__, __LINE__, ""); + } +#endif + if (_live_allocations < 1) { + _srv->fatal("live_allocs < 1", __FILE__, __LINE__, ""); + } + _live_allocations--; + _srv->free(mem); + if (_synchronized) { _lock.unlock(); } + +} + +void * +memory_region::realloc(void *mem, size_t size) { + if (_synchronized) { _lock.lock(); } + if (!mem) { + _live_allocations++; + } + void *newMem = _srv->realloc(mem, size); +#ifdef TRACK_ALLOCATIONS + if (_allocation_list.replace(mem, newMem) == false) { + printf("realloc: ptr 0x%" PRIxPTR " is not in allocation_list\n", + (uintptr_t) mem); + _srv->fatal("not in allocation_list", __FILE__, __LINE__, ""); + } +#endif + if (_synchronized) { _lock.unlock(); } + return newMem; +} + +void * +memory_region::malloc(size_t size) { + if (_synchronized) { _lock.lock(); } + _live_allocations++; + void *mem = _srv->malloc(size); +#ifdef TRACK_ALLOCATIONS + _allocation_list.append(mem); +#endif + if (_synchronized) { _lock.unlock(); } + return mem; +} + +void * +memory_region::calloc(size_t size) { + if (_synchronized) { _lock.lock(); } + _live_allocations++; + void *mem = _srv->malloc(size); + memset(mem, 0, size); +#ifdef TRACK_ALLOCATIONS + _allocation_list.append(mem); +#endif + if (_synchronized) { _lock.unlock(); } + return mem; +} + +memory_region::~memory_region() { + if (_live_allocations == 0) { + return; + } + char msg[128]; + snprintf(msg, sizeof(msg), + "leaked memory in rust main loop (%" PRIuPTR " objects)", + _live_allocations); +#ifdef TRACK_ALLOCATIONS + for (size_t i = 0; i < _allocation_list.size(); i++) { + if (_allocation_list[i] != NULL) { + printf("allocation 0x%" PRIxPTR " was not freed\n", + (uintptr_t) _allocation_list[i]); + } + } +#endif + _srv->fatal(msg, __FILE__, __LINE__, "%d objects", _live_allocations); +} diff --git a/src/rt/memory_region.h b/src/rt/memory_region.h new file mode 100644 index 00000000..3411d867 --- /dev/null +++ b/src/rt/memory_region.h @@ -0,0 +1,37 @@ +/* + * The Rust runtime uses memory regions to provide a primitive level of + * memory management and isolation between tasks, and domains. + * + * TODO: Implement a custom lock-free malloc / free instead of relying solely + * on the standard malloc / free. + */ + +#ifndef MEMORY_REGION_H +#define MEMORY_REGION_H + +#include "sync/spin_lock.h" + +class rust_srv; + +class memory_region { +private: + rust_srv *_srv; + memory_region *_parent; + size_t _live_allocations; + array_list<void *> _allocation_list; + const bool _synchronized; + spin_lock _lock; +public: + enum memory_region_type { + LOCAL = 0x1, SYNCHRONIZED = 0x2 + }; + memory_region(rust_srv *srv, bool synchronized); + memory_region(memory_region *parent); + void *malloc(size_t size); + void *calloc(size_t size); + void *realloc(void *mem, size_t size); + void free(void *mem); + virtual ~memory_region(); +}; + +#endif /* MEMORY_REGION_H */ diff --git a/src/rt/rust.cpp b/src/rt/rust.cpp index 9cc2fe41..905b0c8a 100644 --- a/src/rt/rust.cpp +++ b/src/rt/rust.cpp @@ -1,108 +1,5 @@ #include "rust_internal.h" -#define TRACK_ALLOCATIONS - -rust_srv::rust_srv() : - live_allocs(0) -{ -} - -rust_srv::~rust_srv() -{ - if (live_allocs != 0) { - char msg[128]; - snprintf(msg, sizeof(msg), - "leaked memory in rust main loop (%" PRIuPTR " objects)", - live_allocs); -#ifdef TRACK_ALLOCATIONS - for (size_t i = 0; i < allocation_list.size(); i++) { - if (allocation_list[i] != NULL) { - printf("allocation 0x%" PRIxPTR " was not freed\n", - (uintptr_t) allocation_list[i]); - } - } -#endif - fatal(msg, __FILE__, __LINE__); - } -} - -void -rust_srv::log(char const *str) -{ - printf("rt: %s\n", str); -} - - - -void * -rust_srv::malloc(size_t bytes) -{ - ++live_allocs; - void * val = ::malloc(bytes); -#ifdef TRACK_ALLOCATIONS - allocation_list.append(val); -#endif - return val; -} - -void * -rust_srv::realloc(void *p, size_t bytes) -{ - if (!p) { - live_allocs++; - } - void * val = ::realloc(p, bytes); -#ifdef TRACK_ALLOCATIONS - if (allocation_list.replace(p, val) == NULL) { - fatal("not in allocation_list", __FILE__, __LINE__); - } -#endif - return val; -} - -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", - (uintptr_t) p); - fatal("not in allocation_list", __FILE__, __LINE__); - } -#endif - if (live_allocs < 1) { - fatal("live_allocs < 1", __FILE__, __LINE__); - } - live_allocs--; - ::free(p); -} - -void -rust_srv::fatal(char const *expr, char const *file, size_t line) -{ - char buf[1024]; - snprintf(buf, sizeof(buf), - "fatal, '%s' failed, %s:%d", - expr, file, (int)line); - log(buf); - exit(1); -} - -void -rust_srv::warning(char const *expr, char const *file, size_t line) -{ - char buf[1024]; - snprintf(buf, sizeof(buf), - "warning: '%s', at: %s:%d", - expr, file, (int)line); - log(buf); -} - -rust_srv * -rust_srv::clone() -{ - return new rust_srv(); -} struct command_line_args @@ -182,7 +79,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.h b/src/rt/rust.h index 9a61dca7..3c534c94 100644 --- a/src/rt/rust.h +++ b/src/rt/rust.h @@ -19,26 +19,7 @@ #include "util/array_list.h" -struct rust_srv { - size_t live_allocs; - array_list<void *> allocation_list; - - virtual void log(char const *); - virtual void fatal(char const *, char const *, size_t); - virtual void warning(char const *, char const *, size_t); - virtual void *malloc(size_t); - virtual void *realloc(void *, size_t); - virtual void free(void *); - virtual rust_srv *clone(); - - rust_srv(); - virtual ~rust_srv(); -}; - -inline void *operator new(size_t size, rust_srv *srv) -{ - return srv->malloc(size); -} +#include "rust_srv.h" /* * Local Variables: diff --git a/src/rt/rust_builtin.cpp b/src/rt/rust_builtin.cpp index d8d9b8d6..d2bad054 100644 --- a/src/rt/rust_builtin.cpp +++ b/src/rt/rust_builtin.cpp @@ -2,19 +2,6 @@ #include "rust_internal.h" /* Native builtins. */ -extern "C" CDECL rust_str* -str_alloc(rust_task *task, size_t n_bytes) -{ - rust_dom *dom = task->dom; - size_t alloc = next_power_of_two(sizeof(rust_str) + n_bytes); - void *mem = dom->malloc(alloc); - if (!mem) { - task->fail(2); - return NULL; - } - rust_str *st = new (mem) rust_str(dom, alloc, 1, (uint8_t const *)""); - return st; -} extern "C" CDECL rust_str* last_os_error(rust_task *task) { @@ -51,7 +38,7 @@ last_os_error(rust_task *task) { #endif size_t fill = strlen(buf) + 1; size_t alloc = next_power_of_two(sizeof(rust_str) + fill); - void *mem = dom->malloc(alloc); + void *mem = dom->malloc(alloc, memory_region::LOCAL); if (!mem) { task->fail(1); return NULL; @@ -95,7 +82,7 @@ extern "C" CDECL rust_vec* vec_alloc(rust_task *task, type_desc *t, type_desc *elem_t, size_t n_elts) { rust_dom *dom = task->dom; - task->log(rust_log::MEM, + task->log(rust_log::MEM | rust_log::STDLIB, "vec_alloc %" PRIdPTR " elements of size %" PRIdPTR, n_elts, elem_t->size); size_t fill = n_elts * elem_t->size; @@ -109,6 +96,85 @@ vec_alloc(rust_task *task, type_desc *t, type_desc *elem_t, size_t n_elts) return vec; } +extern "C" CDECL void * +vec_buf(rust_task *task, type_desc *ty, rust_vec *v, size_t offset) +{ + return (void *)&v->data[ty->size * offset]; +} + +extern "C" CDECL size_t +vec_len(rust_task *task, type_desc *ty, rust_vec *v) +{ + return v->fill / ty->size; +} + +extern "C" CDECL void +vec_len_set(rust_task *task, type_desc *ty, rust_vec *v, size_t len) +{ + task->log(rust_log::STDLIB, + "vec_len_set(0x%" PRIxPTR ", %" PRIdPTR ") on vec with " + "alloc = %" PRIdPTR + ", fill = %" PRIdPTR + ", len = %" PRIdPTR + ". New fill is %" PRIdPTR, + v, len, v->alloc, v->fill, v->fill / ty->size, len * ty->size); + v->fill = len * ty->size; +} + +extern "C" CDECL void +vec_print_debug_info(rust_task *task, type_desc *ty, rust_vec *v) +{ + task->log(rust_log::STDLIB, + "vec_print_debug_info(0x%" PRIxPTR ")" + " with tydesc 0x%" PRIxPTR + " (size = %" PRIdPTR ", align = %" PRIdPTR ")" + " alloc = %" PRIdPTR ", fill = %" PRIdPTR ", len = %" PRIdPTR + " , data = ...", + v, + ty, + ty->size, + ty->align, + v->alloc, + v->fill, + v->fill / ty->size); + + for (size_t i = 0; i < v->fill; ++i) { + task->log(rust_log::STDLIB, + " %" PRIdPTR ": 0x%" PRIxPTR, + i, v->data[i]); + } +} + +/* Helper for str_alloc and str_from_vec. Returns NULL as failure. */ +static rust_str * +str_alloc_with_data(rust_task *task, + size_t n_bytes, + size_t fill, + uint8_t const *d) +{ + rust_dom *dom = task->dom; + size_t alloc = next_power_of_two(sizeof(rust_str) + n_bytes); + void *mem = dom->malloc(alloc, memory_region::LOCAL); + if (!mem) + return NULL; + rust_str *st = new (mem) rust_str(dom, alloc, fill, d); + return st; +} + +extern "C" CDECL rust_str* +str_alloc(rust_task *task, size_t n_bytes) +{ + rust_str *st = str_alloc_with_data(task, + n_bytes + 1, // +1 to fit at least "" + 1, + (uint8_t const *)""); + if (!st) { + task->fail(2); + return NULL; + } + return st; +} + extern "C" CDECL char const * str_buf(rust_task *task, rust_str *s) { @@ -121,16 +187,20 @@ 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) -{ - return (void *)&v->data[ty->size * offset]; -} - -extern "C" CDECL size_t -vec_len(rust_task *task, type_desc *ty, rust_vec *v) +extern "C" CDECL rust_str * +str_from_vec(rust_task *task, rust_vec *v) { - return v->fill / ty->size; + rust_str *st = + str_alloc_with_data(task, + v->fill + 1, // +1 to fit at least '\0' + v->fill, + v->fill ? (uint8_t const *)v->data : NULL); + if (!st) { + task->fail(2); + return NULL; + } + st->data[st->fill++] = '\0'; + return st; } extern "C" CDECL void * @@ -158,6 +228,13 @@ rand_free(rust_task *task, randctx *rctx) task->free(rctx); } +extern "C" CDECL void upcall_sleep(rust_task *task, size_t time_in_us); + +extern "C" CDECL void +task_sleep(rust_task *task, size_t time_in_us) { + upcall_sleep(task, time_in_us); +} + // // Local Variables: // mode: C++ 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..ce6bebf3 100644 --- a/src/rt/rust_dom.cpp +++ b/src/rt/rust_dom.cpp @@ -4,12 +4,18 @@ 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), + local_region(&srv->local_region), + synchronized_region(&srv->synchronized_region), + name(name), running_tasks(this), blocked_tasks(this), dead_tasks(this), @@ -25,14 +31,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 +53,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 +86,8 @@ rust_dom::~rust_dom() { #endif while (caches.length()) delete caches.pop(); + + _live_domains.replace(this, NULL); } void @@ -124,41 +139,72 @@ 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; } 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); - return p; +rust_dom::malloc(size_t size) { + return malloc(size, memory_region::LOCAL); } void * -rust_dom::calloc(size_t sz) { - void *p = this->malloc(sz); - memset(p, 0, sz); - return p; +rust_dom::malloc(size_t size, memory_region::memory_region_type type) { + if (type == memory_region::LOCAL) { + return local_region.malloc(size); + } else if (type == memory_region::SYNCHRONIZED) { + return synchronized_region.malloc(size); + } + return NULL; } void * -rust_dom::realloc(void *p, size_t sz) { - void *p1 = srv->realloc(p, sz); - I(this, p1); - log(rust_log::MEM, "rust_dom::realloc(0x%" PRIxPTR ", %d) -> 0x%" PRIxPTR, - p, sz, p1); - return p1; +rust_dom::calloc(size_t size) { + return calloc(size, memory_region::LOCAL); +} + +void * +rust_dom::calloc(size_t size, memory_region::memory_region_type type) { + if (type == memory_region::LOCAL) { + return local_region.calloc(size); + } else if (type == memory_region::SYNCHRONIZED) { + return synchronized_region.calloc(size); + } + return NULL; +} + +void * +rust_dom::realloc(void *mem, size_t size) { + return realloc(mem, size, memory_region::LOCAL); +} + +void * +rust_dom::realloc(void *mem, size_t size, + memory_region::memory_region_type type) { + if (type == memory_region::LOCAL) { + return local_region.realloc(mem, size); + } else if (type == memory_region::SYNCHRONIZED) { + return synchronized_region.realloc(mem, size); + } + return NULL; +} + +void +rust_dom::free(void *mem) { + free(mem, memory_region::LOCAL); } void -rust_dom::free(void *p) { - log(rust_log::MEM, "rust_dom::free(0x%" PRIxPTR ")", p); - I(this, p); - srv->free(p); +rust_dom::free(void *mem, memory_region::memory_region_type type) { + log(rust_log::MEM, "rust_dom::free(0x%" PRIxPTR ")", mem); + if (type == memory_region::LOCAL) { + local_region.free(mem); + } else if (type == memory_region::SYNCHRONIZED) { + synchronized_region.free(mem); + } + return; } #ifdef __WIN32__ @@ -190,8 +236,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 +246,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 +272,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 +289,13 @@ 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); - A(this, message->dom == this, "Message owned by non-local domain."); + &_incoming_message_queue, + this); _incoming_message_queue.enqueue(message); - _incoming_message_pending.signal(); - _progress.signal(); } /** @@ -262,7 +307,8 @@ void rust_dom::drain_incoming_message_queue() { log(rust_log::COMM, "<== processing incoming message \"%s\" 0x%" PRIxPTR, message->label, message); message->process(); - delete message; + message->~rust_message(); + this->synchronized_region.free(message); } } @@ -272,7 +318,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; @@ -307,27 +353,64 @@ rust_dom::get_port_proxy_synchronized(rust_port *port) { * Returns NULL if no tasks can be scheduled. */ rust_task * -rust_dom::schedule_task() -{ +rust_dom::schedule_task() { I(this, this); // FIXME: in the face of failing tasks, this is not always right. // I(this, n_live_tasks() > 0); if (running_tasks.length() > 0) { size_t i = rand(&rctx); i %= running_tasks.length(); - return (rust_task *)running_tasks[i]; + if (running_tasks[i]->yield_timer.has_timed_out()) { + return (rust_task *)running_tasks[i]; + } } // log(rust_log::DOM|rust_log::TASK, "no schedulable tasks"); return NULL; } +/** + * Checks for simple deadlocks. + */ +bool +rust_dom::is_deadlocked() { + if (_live_domains.size() != 1) { + // We cannot tell if we are deadlocked if other domains exists. + return false; + } + + if (running_tasks.length() != 0) { + // We are making progress and therefore we are not deadlocked. + return false; + } + + if (_incoming_message_queue.is_empty() && blocked_tasks.length() > 0) { + // We have no messages to process, no running tasks to schedule + // and some blocked tasks therefore we are likely in a deadlock. + log_state(); + return true; + } + + return false; +} + +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 + " timeout: %d", + running_tasks[i]->name, + running_tasks[i], + running_tasks[i]->yield_timer.get_timeout()); } } @@ -335,15 +418,19 @@ 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 + " '%s'", + blocked_tasks[i]->name, blocked_tasks[i], + blocked_tasks[i]->cond, blocked_tasks[i]->cond_name); } } 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]->name, dead_tasks[i], + dead_tasks[i]->ref_count); } } } @@ -360,42 +447,54 @@ 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) { + A(this, is_deadlocked() == false, "deadlock"); + drain_incoming_message_queue(); rust_task *scheduled_task = schedule_task(); - // If we cannot schedule a task because all other live tasks - // are blocked, wait on a condition variable which is signaled - // if progress is made in other domains. + // The scheduler busy waits until a task is available for scheduling. + // Eventually we'll want a smarter way to do this, perhaps sleep + // for a minimum amount of time. 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 +509,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..34d8c694 100644 --- a/src/rt/rust_dom.h +++ b/src/rt/rust_dom.h @@ -25,6 +25,9 @@ struct rust_dom rust_crate const *root_crate; rust_log _log; rust_srv *srv; + memory_region local_region; + memory_region synchronized_region; + const char *const name; ptr_vec<rust_task> running_tasks; ptr_vec<rust_task> blocked_tasks; ptr_vec<rust_task> dead_tasks; @@ -34,20 +37,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); @@ -58,10 +60,15 @@ struct rust_dom template<typename T> void logptr(char const *msg, T* ptrval); void fail(); - void *malloc(size_t sz); - void *calloc(size_t sz); - void *realloc(void *data, size_t sz); - void free(void *p); + void *malloc(size_t size); + void *malloc(size_t size, memory_region::memory_region_type type); + void *calloc(size_t size); + void *calloc(size_t size, memory_region::memory_region_type type); + void *realloc(void *mem, size_t size); + void *realloc(void *mem, size_t size, + memory_region::memory_region_type type); + void free(void *mem); + void free(void *mem, memory_region::memory_region_type type); void send_message(rust_message *message); void drain_incoming_message_queue(); @@ -81,9 +88,11 @@ struct rust_dom void reap_dead_tasks(); rust_task *schedule_task(); + bool is_deadlocked(); 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..d6618873 100644 --- a/src/rt/rust_internal.h +++ b/src/rt/rust_internal.h @@ -10,6 +10,7 @@ #include <stdlib.h> #include <stdint.h> #include <inttypes.h> +#include <stdarg.h> #include <stdio.h> #include <string.h> @@ -38,19 +39,22 @@ extern "C" { #error "Platform not supported." #endif +#include "sync/sync.h" +#include "sync/timer.h" #include "sync/condition_variable.h" #ifndef __i386__ #error "Target CPU not supported." #endif -#define I(dom, e) ((e) ? (void)0 : \ - (dom)->srv->fatal(#e, __FILE__, __LINE__)) -#define W(dom, e, s) ((e) ? (void)0 : \ - (dom)->srv->warning(#e " : " #s, __FILE__, __LINE__)) +#define I(dom, e) ((e) ? (void)0 : \ + (dom)->srv->fatal(#e, __FILE__, __LINE__, "")) -#define A(dom, e, s) ((e) ? (void)0 : \ - (dom)->srv->fatal(#e " : " #s, __FILE__, __LINE__)) +#define W(dom, e, s, ...) ((e) ? (void)0 : \ + (dom)->srv->warning(#e, __FILE__, __LINE__, s, ## __VA_ARGS__)) + +#define A(dom, e, s, ...) ((e) ? (void)0 : \ + (dom)->srv->fatal(#e, __FILE__, __LINE__, s, ## __VA_ARGS__)) struct rust_task; struct rust_port; @@ -75,7 +79,7 @@ template <typename T> struct rc_base { - size_t ref_count; + intptr_t ref_count; void ref() { ++ref_count; @@ -151,6 +155,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); }; @@ -161,32 +166,14 @@ template <typename T> inline T check_null(rust_dom *dom, T value, char const *expr, char const *file, size_t line) { if (value == NULL) { - dom->srv->fatal(expr, file, line); + dom->srv->fatal(expr, file, line, "is null"); } return value; } #define CHECK_NULL(dom, e) (check_null(dom, e, #e, __FILE__, __LINE__)) -inline void *operator new(size_t sz, void *mem) { - return mem; -} - -inline void *operator new(size_t sz, rust_dom *dom) { - return dom->malloc(sz); -} - -inline void *operator new[](size_t sz, rust_dom *dom) { - return dom->malloc(sz); -} - -inline void *operator new(size_t sz, rust_dom &dom) { - return dom.malloc(sz); -} - -inline void *operator new[](size_t sz, rust_dom &dom) { - return dom.malloc(sz); -} +#include "memory.h" struct rust_timer diff --git a/src/rt/rust_log.cpp b/src/rt/rust_log.cpp index 48fcd0f1..e72a62fa 100644 --- a/src/rt/rust_log.cpp +++ b/src/rt/rust_log.cpp @@ -26,7 +26,10 @@ read_type_bit_mask() { bits |= strstr(env_str, "cache") ? rust_log::CACHE : 0; bits |= strstr(env_str, "timer") ? rust_log::TIMER : 0; bits |= strstr(env_str, "gc") ? rust_log::GC : 0; + bits |= strstr(env_str, "stdlib") ? rust_log::STDLIB : 0; + bits |= strstr(env_str, "special") ? rust_log::SPECIAL : 0; bits |= strstr(env_str, "all") ? rust_log::ALL : 0; + bits = strstr(env_str, "none") ? 0 : bits; } return bits; } @@ -144,13 +147,22 @@ rust_log::trace_ln(rust_task *task, char *message) { #if defined(__WIN32__) uint32_t thread_id = 0; #else - uint32_t thread_id = (uint32_t) pthread_self(); + uint32_t thread_id = hash((uint32_t) pthread_self()); #endif char prefix[1024] = ""; - append_string(prefix, "0x%08" PRIxPTR ":0x%08" PRIxPTR ":", - thread_id, (uintptr_t) _dom); + if (_dom->name) { + append_string(prefix, "%04" PRIxPTR ":%.10s:", + thread_id, _dom->name); + } else { + append_string(prefix, "%04" PRIxPTR ":0x%08" PRIxPTR ":", + thread_id, (uintptr_t) _dom); + } if (task) { - append_string(prefix, "0x%08" PRIxPTR ":", (uintptr_t) task); + if (task->name) { + append_string(prefix, "%.10s:", task->name); + } else { + append_string(prefix, "0x%08" PRIxPTR ":", (uintptr_t) task); + } } trace_ln(thread_id, prefix, message); } diff --git a/src/rt/rust_log.h b/src/rt/rust_log.h index 29c85e70..5155cb48 100644 --- a/src/rt/rust_log.h +++ b/src/rt/rust_log.h @@ -41,6 +41,8 @@ public: UPCALL = 0x200, TIMER = 0x400, GC = 0x800, + STDLIB = 0x1000, + SPECIAL = 0x2000, ALL = 0xffffffff }; diff --git a/src/rt/rust_message.cpp b/src/rt/rust_message.cpp index 8b396b4d..b6b7fbf0 100644 --- a/src/rt/rust_message.cpp +++ b/src/rt/rust_message.cpp @@ -3,7 +3,8 @@ rust_message:: rust_message(const char* label, rust_task *source, rust_task *target) : - dom(target->dom), label(label), + label(label), + _dom(target->dom), _source(source), _target(target) { } @@ -12,12 +13,12 @@ rust_message::~rust_message() { } void rust_message::process() { - I(dom, false); + I(_dom, false); } rust_proxy<rust_task> * rust_message::get_source_proxy() { - return dom->get_task_proxy(_source); + return _dom->get_task_proxy(_source); } notify_message:: @@ -50,8 +51,9 @@ send(notification_type type, const char* label, rust_task *source, rust_proxy<rust_task> *target) { rust_task *target_task = target->delegate(); rust_dom *target_domain = target_task->dom; - notify_message *message = new (target_domain) - notify_message(type, label, source, target_task); + notify_message *message = + new (target_domain, memory_region::SYNCHRONIZED) notify_message(type, + label, source, target_task); target_domain->send_message(message); } @@ -83,15 +85,15 @@ send(uint8_t *buffer, size_t buffer_sz, const char* label, rust_task *source, rust_task *target_task = target->delegate(); rust_port *target_port = port->delegate(); rust_dom *target_domain = target_task->dom; - data_message *message = new (target_domain) - data_message(buffer, buffer_sz, label, source, - target_task, target_port); + data_message *message = + new (target_domain, memory_region::SYNCHRONIZED) + data_message(buffer, buffer_sz, label, source, + target_task, target_port); target_domain->send_message(message); } 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_message.h b/src/rt/rust_message.h index b7b8568a..6d986acf 100644 --- a/src/rt/rust_message.h +++ b/src/rt/rust_message.h @@ -9,12 +9,11 @@ /** * Abstract base class for all message types. */ -class rust_message : public lock_free_queue_node, - public dom_owned<rust_message> { +class rust_message : public lock_free_queue_node { public: - rust_dom *dom; const char* label; private: + rust_dom *_dom; rust_task *_source; protected: rust_task *_target; @@ -70,7 +69,7 @@ public: data_message(uint8_t *buffer, size_t buffer_sz, const char* label, rust_task *source, rust_task *target, rust_port *port); - ~data_message(); + virtual ~data_message(); void process(); /** diff --git a/src/rt/rust_port.cpp b/src/rt/rust_port.cpp index 0a5b7ee7..458283fb 100644 --- a/src/rt/rust_port.cpp +++ b/src/rt/rust_port.cpp @@ -17,16 +17,49 @@ 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(); + + if (chan->ref_count == 0) { + task->log(rust_log::COMM, + "chan: 0x%" PRIxPTR " is dormant, freeing", chan); + delete chan; + } } - // 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); + 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 ", size: %d, remote: %s", + chan, + chan->buffer.size(), + 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_srv.cpp b/src/rt/rust_srv.cpp new file mode 100644 index 00000000..f2dfef63 --- /dev/null +++ b/src/rt/rust_srv.cpp @@ -0,0 +1,80 @@ +/* + * + */ + +#include "rust_internal.h" +#include "rust_srv.h" + +rust_srv::rust_srv() : + local_region(this, false), + synchronized_region(this, true) { + // Nop. +} + +rust_srv::~rust_srv() { + // Nop. +} + +void +rust_srv::free(void *p) { + ::free(p); +} + +void * +rust_srv::malloc(size_t bytes) { + return ::malloc(bytes); +} + +void * +rust_srv::realloc(void *p, size_t bytes) { + return ::realloc(p, bytes); +} + +void +rust_srv::log(char const *msg) { + printf("rt: %s\n", msg); +} + +void +rust_srv::fatal(const char *expression, + const char *file, + size_t line, + const char *format, + ...) { + char buf[1024]; + va_list args; + va_start(args, format); + vsnprintf(buf, sizeof(buf), format, args); + va_end(args); + + char msg[1024]; + snprintf(msg, sizeof(msg), + "fatal, '%s' failed, %s:%d %s", + expression, file, (int)line, buf); + log(msg); + exit(1); +} + +void +rust_srv::warning(char const *expression, + char const *file, + size_t line, + const char *format, + ...) { + char buf[1024]; + va_list args; + va_start(args, format); + vsnprintf(buf, sizeof(buf), format, args); + va_end(args); + + char msg[1024]; + snprintf(msg, sizeof(msg), + "warning: '%s', at: %s:%d %s", + expression, file, (int)line, buf); + log(msg); +} + +rust_srv * +rust_srv::clone() { + return new rust_srv(); +} diff --git a/src/rt/rust_srv.h b/src/rt/rust_srv.h new file mode 100644 index 00000000..e617c002 --- /dev/null +++ b/src/rt/rust_srv.h @@ -0,0 +1,34 @@ +/* + * + */ + +#ifndef RUST_SRV_H +#define RUST_SRV_H + +#include "sync/spin_lock.h" +#include "memory_region.h" + +class rust_srv { +public: + memory_region local_region; + memory_region synchronized_region; + virtual void log(char const *msg); + virtual void fatal(char const *expression, + char const *file, + size_t line, + char const *format, + ...); + virtual void warning(char const *expression, + char const *file, + size_t line, + char const *format, + ...); + virtual void free(void *); + virtual void *malloc(size_t); + virtual void *realloc(void *, size_t); + rust_srv(); + virtual ~rust_srv(); + virtual rust_srv *clone(); +}; + +#endif /* RUST_SRV_H */ diff --git a/src/rt/rust_task.cpp b/src/rt/rust_task.cpp index 9f4fa611..9497645b 100644 --- a/src/rt/rust_task.cpp +++ b/src/rt/rust_task.cpp @@ -6,7 +6,9 @@ // Stacks -static size_t const min_stk_bytes = 0x300; +// FIXME (issue #151): This should be 0x300; the change here is for +// practicality's sake until stack growth is working. +static size_t const min_stk_bytes = 0x300000; // Task stack segments. Heap allocated and chained together. @@ -52,7 +54,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,8 +62,10 @@ 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), + cond_name("none"), supervisor(spawner), idx(0), rendezvous_ptr(0), @@ -77,8 +81,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)) { @@ -198,6 +202,12 @@ rust_task::start(uintptr_t exit_task_glue, void rust_task::grow(size_t n_frame_bytes) { + // FIXME (issue #151): Just fail rather than almost certainly crashing + // mysteriously later. The commented-out logic below won't work at all in + // the presence of non-word-aligned pointers. + abort(); + +#if 0 stk_seg *old_stk = this->stk; uintptr_t old_top = (uintptr_t) old_stk->limit; uintptr_t old_bottom = (uintptr_t) &old_stk->data[0]; @@ -252,6 +262,7 @@ rust_task::grow(size_t n_frame_bytes) "processed %d relocations", n_relocs); del_stk(dom, old_stk); dom->logptr("grown stk limit", new_top); +#endif } void @@ -308,10 +319,16 @@ rust_task::run_on_resume(uintptr_t glue) } void -rust_task::yield(size_t nargs) -{ +rust_task::yield(size_t nargs) { + yield(nargs, 0); +} + +void +rust_task::yield(size_t nargs, size_t time_in_us) { log(rust_log::TASK, - "task 0x%" PRIxPTR " yielding", this); + "task %s @0x%" PRIxPTR " yielding for %d us", + name, this, time_in_us); + yield_timer.reset(time_in_us); run_after_return(nargs, dom->root_crate->get_yield_glue()); } @@ -323,23 +340,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 +370,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 +381,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 +389,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 +492,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 +513,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 +533,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)); @@ -531,7 +562,7 @@ rust_task::transition(ptr_vec<rust_task> *src, ptr_vec<rust_task> *dst) } void -rust_task::block(rust_cond *on) +rust_task::block(rust_cond *on, const char* name) { log(rust_log::TASK, "Blocking on 0x%" PRIxPTR ", cond: 0x%" PRIxPTR, (uintptr_t) on, (uintptr_t) cond); @@ -540,6 +571,7 @@ rust_task::block(rust_cond *on) transition(&dom->running_tasks, &dom->blocked_tasks); cond = on; + cond_name = name; } void @@ -551,11 +583,9 @@ 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; + cond_name = "none"; } void diff --git a/src/rt/rust_task.h b/src/rt/rust_task.h index b657592a..9a4e0c7b 100644 --- a/src/rt/rust_task.h +++ b/src/rt/rust_task.h @@ -21,13 +21,18 @@ 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; + const char *cond_name; rust_task *supervisor; // Parent-link for failure propagation. size_t idx; size_t gc_alloc_thresh; size_t gc_alloc_accum; + // Keeps track of the last time this task yielded. + timer yield_timer; + // Rendezvous pointer for receiving data when blocked on a port. If we're // trying to read data and no data is available on any incoming channel, // we block on the port, and yield control to the scheduler. Since, we @@ -41,8 +46,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, @@ -64,7 +71,7 @@ rust_task : public maybe_proxy<rust_task>, const char *state_str(); void transition(ptr_vec<rust_task> *svec, ptr_vec<rust_task> *dvec); - void block(rust_cond *on); + void block(rust_cond *on, const char* name); void wakeup(rust_cond *from); void die(); void unblock(); @@ -85,6 +92,9 @@ rust_task : public maybe_proxy<rust_task>, // Save callee-saved registers and return to the main loop. void yield(size_t nargs); + // Yields for a specified duration of time. + void yield(size_t nargs, size_t time_in_ms); + // Fail this task (assuming caller-on-stack is different task). void kill(); diff --git a/src/rt/rust_upcall.cpp b/src/rt/rust_upcall.cpp index 574cb703..a3373870 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,55 @@ 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); + // Nop. +} + +/** * 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()) { + // We're trying to delete a channel that another task may be reading + // from. We have two options: + // + // 1. We can flush the channel by blocking in upcall_flush_chan() + // and resuming only when the channel is flushed. The problem + // here is that we can get ourselves in a deadlock if the parent + // task tries to join us. + // + // 2. We can leave the channel in a "dormnat" state by not freeing + // it and letting the receiver task delete it for us instead. + if (chan->buffer.is_empty() == false) { + return; + } + chan->disassociate(); + } delete chan; } @@ -133,24 +172,32 @@ extern "C" CDECL void upcall_yield(rust_task *task) { task->yield(1); } +extern "C" CDECL void upcall_sleep(rust_task *task, size_t time_in_us) { + LOG_UPCALL_ENTRY(task); + task->log(rust_log::UPCALL | rust_log::TASK, "elapsed %d", + task->yield_timer.get_elapsed_time()); + task->log(rust_log::UPCALL | rust_log::TASK, "sleep %d us", time_in_us); + task->yield(2, time_in_us); +} + 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()); - task->block(target_task); + task->block(target_task, "joining remote task"); task->yield(2); } else { // If the other task is already dying, we don't have to wait for it. if (target_task->dead() == false) { target_task->tasks_waiting_to_join.push(task); - task->block(target_task); + task->block(target_task, "joining local task"); task->yield(2); } } @@ -168,8 +215,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 +228,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 @@ -201,7 +238,7 @@ upcall_recv(rust_task *task, uintptr_t *dptr, rust_port *port) { task->log(rust_log::COMM, "<=== waiting for rendezvous data ==="); task->rendezvous_ptr = dptr; - task->block(port); + task->block(port, "waiting for rendezvous data"); task->yield(3); } @@ -219,11 +256,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 +282,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 +538,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 +556,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/lock_free_queue.cpp b/src/rt/sync/lock_free_queue.cpp index 69241ece..3f859862 100644 --- a/src/rt/sync/lock_free_queue.cpp +++ b/src/rt/sync/lock_free_queue.cpp @@ -18,6 +18,7 @@ lock_free_queue::lock_free_queue() : _tail(this) { void lock_free_queue::enqueue(lock_free_queue_node *item) { + lock.lock(); item->next = (lock_free_queue_node *) NULL; lock_free_queue_node *last = _tail; _tail = item; @@ -25,10 +26,12 @@ lock_free_queue::enqueue(lock_free_queue_node *item) { last = last->next; } last->next = item; + lock.unlock(); } lock_free_queue_node * lock_free_queue::dequeue() { + lock.lock(); lock_free_queue_node *item = next; if (item && !(next = item->next)) { _tail = (lock_free_queue_node *) this; @@ -41,6 +44,7 @@ lock_free_queue::dequeue() { } while ((lost = help) != (lock_free_queue_node *) NULL); } } + lock.unlock(); return item; } diff --git a/src/rt/sync/lock_free_queue.h b/src/rt/sync/lock_free_queue.h index 1f09ec52..c1a309b8 100644 --- a/src/rt/sync/lock_free_queue.h +++ b/src/rt/sync/lock_free_queue.h @@ -1,6 +1,8 @@ #ifndef LOCK_FREE_QUEUE_H #define LOCK_FREE_QUEUE_H +#include "spin_lock.h" + class lock_free_queue_node { public: lock_free_queue_node *next; @@ -8,6 +10,7 @@ public: }; class lock_free_queue : lock_free_queue_node { + spin_lock lock; lock_free_queue_node *_tail; public: lock_free_queue(); diff --git a/src/rt/sync/sync.cpp b/src/rt/sync/sync.cpp new file mode 100644 index 00000000..09a6f291 --- /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__ + Sleep(1); +#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/sync/timer.cpp b/src/rt/sync/timer.cpp new file mode 100644 index 00000000..0487b397 --- /dev/null +++ b/src/rt/sync/timer.cpp @@ -0,0 +1,61 @@ +#include "../globals.h" +#include "timer.h" + +#if defined(__APPLE__) +#include <mach/mach_time.h> +#endif + +timer::timer() { +#if __WIN32__ + uint64_t ticks_per_second; + QueryPerformanceFrequency((LARGE_INTEGER *)&ticks_per_second); + _ticks_per_us = ticks_per_second / 1000000; +#endif + reset(0); +} + +void +timer::reset(uint64_t timeout) { + _start = get_time(); + _timeout = timeout; +} + +uint64_t +timer::get_elapsed_time() { + return get_time() - _start; +} + +int64_t +timer::get_timeout() { + return _timeout - get_elapsed_time(); +} + +bool +timer::has_timed_out() { + return get_timeout() <= 0; +} + +uint64_t +timer::get_time() { +#ifdef __APPLE__ + uint64_t time = mach_absolute_time(); + mach_timebase_info_data_t info = {0, 0}; + if (info.denom == 0) { + mach_timebase_info(&info); + } + uint64_t time_nano = time * (info.numer / info.denom); + return time_nano / 1000; +#elif __WIN32__ + uint64_t ticks; + QueryPerformanceCounter((LARGE_INTEGER *)&ticks); + return ticks / _ticks_per_us; +#else + timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return (ts.tv_sec * 1000000000LL + ts.tv_nsec) / 1000; +#endif +} + +timer::~timer() { + // Nop. +} diff --git a/src/rt/sync/timer.h b/src/rt/sync/timer.h new file mode 100644 index 00000000..509fd22c --- /dev/null +++ b/src/rt/sync/timer.h @@ -0,0 +1,25 @@ +/* + * Utility class to measure time in a platform independent way. + */ + +#ifndef TIMER_H +#define TIMER_H + +class timer { +private: + uint64_t _start; + uint64_t _timeout; + uint64_t get_time(); +#if __WIN32__ + uint64_t _ticks_per_us; +#endif +public: + timer(); + void reset(uint64_t timeout); + uint64_t get_elapsed_time(); + int64_t get_timeout(); + bool has_timed_out(); + virtual ~timer(); +}; + +#endif /* TIMER_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 100755 index 00000000..47a46ce9 --- /dev/null +++ b/src/run.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python + +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("-l", dest="log", default="", + help="rust log") +parser.add_option("-v", action="store_true", dest="valgrind", default=False, + help="runs under valgrind") +parser.add_option("-t", action="store_true", dest="terminate", default=False, + help="terminate on first failure") +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.log != ""): + os.putenv("RUST_LOG", options.log); + +if (options.quiet): + os.putenv("RUST_LOG", "none"); + +# Rut +totalPassed = 0; +repetitions = 1; +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)); + command = rustProgram.replace(".rs", ".x86"); + if (options.valgrind): + command = "valgrind --leak-check=full " + \ + "--quiet --vex-iropt-level=0 " + \ + "--suppressions=etc/x86.supp " + \ + command; + print "Running Command: " + command; + result = os.system(command); + exitStatus = result >> 8; + signalNumber = result & 0xF; + if (result == 0): + passed += 1; + elif (options.terminate): + sys.exit(1); + print "Result for: " + rustProgram + " " + str(passed) + \ + " of " + str(repetitions) + " passed."; + totalPassed += passed; +print "Total: " + str(totalPassed) + " of " + \ + str(len(tests) * repetitions) + " passed." diff --git a/src/test/run-pass/acyclic-unwind.rs b/src/test/run-pass/acyclic-unwind.rs index c9ed2ae3..38de9082 100644 --- a/src/test/run-pass/acyclic-unwind.rs +++ b/src/test/run-pass/acyclic-unwind.rs @@ -13,6 +13,11 @@ io fn f(chan[int] c) while (true) { // spin waiting for the parent to kill us. log "child waiting to die..."; + + // while waiting to die, the messages we are + // sending to the channel are never received + // by the parent, therefore this test cases drops + // messages on the floor c <| 1; } } diff --git a/src/test/run-pass/append-units.rs b/src/test/run-pass/append-units.rs new file mode 100644 index 00000000..05b3478b --- /dev/null +++ b/src/test/run-pass/append-units.rs @@ -0,0 +1,15 @@ +fn main() { + auto v = vec(1,2,3); + v += 4; + v += 5; + check (v.(3) == 4); + check (v.(4) == 5); + + auto s = "hello"; + log s; + s += 'z' as u8; + s += 'y' as u8; + log s; + check (s.(5) == 'z' as u8); + check (s.(6) == 'y' as u8); +} 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/arithmetic-interference.rs b/src/test/run-pass/arithmetic-interference.rs deleted file mode 100644 index 588148bb..00000000 --- a/src/test/run-pass/arithmetic-interference.rs +++ /dev/null @@ -1,7 +0,0 @@ -// Testcase for issue #131. - -fn main() -> () { - let int a = 10; - log a; - check (a * (a - 1) == 90); -} 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/lib-deque.rs b/src/test/run-pass/lib-deque.rs index c7516e3d..ecd8bc44 100644 --- a/src/test/run-pass/lib-deque.rs +++ b/src/test/run-pass/lib-deque.rs @@ -35,8 +35,25 @@ fn test_simple() { log i; check (i == 17); - /* FIXME (issue #138): Test d.get() once it no longer causes - * segfault. */ + check (d.size() == 0u); + d.add_back(3); + check (d.size() == 1u); + d.add_front(2); + check (d.size() == 2u); + d.add_back(4); + check (d.size() == 3u); + d.add_front(1); + check (d.size() == 4u); + + log d.get(0); + log d.get(1); + log d.get(2); + log d.get(3); + + check (d.get(0) == 1); + check (d.get(1) == 2); + check (d.get(2) == 3); + check (d.get(3) == 4); } fn test_boxes(@int a, @int b, @int c, @int d) { @@ -57,13 +74,25 @@ fn test_boxes(@int a, @int b, @int c, @int d) { check (deq.pop_back() == c); check (deq.pop_back() == a); - /* FIXME (issue #138): Test d.get() once it no longer causes - * segfault. */ + check (deq.size() == 0u); + deq.add_back(c); + check (deq.size() == 1u); + deq.add_front(b); + check (deq.size() == 2u); + deq.add_back(d); + check (deq.size() == 3u); + deq.add_front(a); + check (deq.size() == 4u); + + check (deq.get(0) == a); + check (deq.get(1) == b); + check (deq.get(2) == c); + check (deq.get(3) == d); } -type eqfn[T] = fn(&T a, &T b) -> bool; +type eqfn[T] = fn(T a, T b) -> bool; -fn test_parameterized[T](eqfn[T] e, &T a, &T b, &T c, &T d) { +fn test_parameterized[T](eqfn[T] e, T a, T b, T c, T d) { let deque.t[T] deq = deque.create[T](); check (deq.size() == 0u); deq.add_front(a); @@ -81,8 +110,20 @@ fn test_parameterized[T](eqfn[T] e, &T a, &T b, &T c, &T d) { check (e(deq.pop_back(), c)); check (e(deq.pop_back(), a)); - /* FIXME (issue #138): Test d.get() once it no longer causes - * segfault. */ + check (deq.size() == 0u); + deq.add_back(c); + check (deq.size() == 1u); + deq.add_front(b); + check (deq.size() == 2u); + deq.add_back(d); + check (deq.size() == 3u); + deq.add_front(a); + check (deq.size() == 4u); + + check (e(deq.get(0), a)); + check (e(deq.get(1), b)); + check (e(deq.get(2), c)); + check (e(deq.get(3), d)); } type taggy = tag(one(int), two(int, int), three(int, int, int)); @@ -94,15 +135,15 @@ type taggypar[T] = tag(onepar(int), type reccy = rec(int x, int y, taggy t); fn main() { - fn inteq(&int a, &int b) -> bool { + fn inteq(int a, int b) -> bool { ret a == b; } - fn intboxeq(&@int a, &@int b) -> bool { + fn intboxeq(@int a, @int b) -> bool { ret a == b; } - fn taggyeq(&taggy a, &taggy b) -> bool { + fn taggyeq(taggy a, taggy b) -> bool { alt (a) { case (one(a1)) { alt (b) { @@ -125,7 +166,7 @@ fn main() { } } - fn taggypareq[T](&taggypar[T] a, &taggypar[T] b) -> bool { + fn taggypareq[T](taggypar[T] a, taggypar[T] b) -> bool { alt (a) { case (onepar[T](a1)) { alt (b) { @@ -150,65 +191,68 @@ fn main() { } } - fn reccyeq(&reccy a, &reccy b) -> bool { + fn reccyeq(reccy a, reccy b) -> bool { ret (a.x == b.x && a.y == b.y && taggyeq(a.t, b.t)); } - log "test simple"; + log "*** starting"; + + log "*** test simple"; test_simple(); + log "*** end test simple"; /* * FIXME: Causes "Invalid read of size 4" under valgrind. - log "test boxes"; + log "*** test boxes"; test_boxes(@5, @72, @64, @175); + log "*** end test boxes"; */ log "test parameterized: int"; - let eqfn[int] eq1 = bind inteq(_, _); + let eqfn[int] eq1 = inteq; test_parameterized[int](eq1, 5, 72, 64, 175); /* * FIXME: Appears to segfault after an upcall_grow_task - log "test parameterized: @int"; - let eqfn[@int] eq2 = bind intboxeq(_, _); + log "*** test parameterized: @int"; + let eqfn[@int] eq2 = intboxeq; test_parameterized[@int](eq2, @5, @72, @64, @175); + log "*** end test parameterized @int"; - */ + */ log "test parameterized: taggy"; - let eqfn[taggy] eq3 = bind taggyeq(_, _); + let eqfn[taggy] eq3 = taggyeq; test_parameterized[taggy](eq3, one(1), two(1, 2), three(1, 2, 3), two(17, 42)); /* - * FIXME: Segfault. + * FIXME: Segfault. Also appears to be caused only after upcall_grow_task - log "test parameterized: taggypar[int]"; - let eqfn[taggypar[int]] eq4 = bind taggypareq[int](_, _); + log "*** test parameterized: taggypar[int]"; + let eqfn[taggypar[int]] eq4 = taggypareq[int]; test_parameterized[taggypar[int]](eq4, onepar[int](1), twopar[int](1, 2), threepar[int](1, 2, 3), twopar[int](17, 42)); + log "*** end test parameterized: taggypar[int]"; */ - /* - * FIXME: Segfault. - - log "test parameterized: reccy"; + log "*** test parameterized: reccy"; let reccy reccy1 = rec(x=1, y=2, t=one(1)); let reccy reccy2 = rec(x=345, y=2, t=two(1, 2)); let reccy reccy3 = rec(x=1, y=777, t=three(1, 2, 3)); let reccy reccy4 = rec(x=19, y=252, t=two(17, 42)); - let eqfn[reccy] eq5 = bind reccyeq(_, _); + let eqfn[reccy] eq5 = reccyeq; test_parameterized[reccy](eq5, reccy1, reccy2, reccy3, reccy4); + log "*** end test parameterized: reccy"; - */ - log "done"; + log "*** done"; } diff --git a/src/test/run-pass/lib-io.rs b/src/test/run-pass/lib-io.rs new file mode 100644 index 00000000..66394435 --- /dev/null +++ b/src/test/run-pass/lib-io.rs @@ -0,0 +1,26 @@ +// -*- rust -*- + +use std; +import std._io; +import std._str; + +fn test_simple(str tmpfilebase) { + let str tmpfile = tmpfilebase + ".tmp"; + log tmpfile; + let str frood = "A hoopy frood who really knows where his towel is."; + log frood; + + { + let _io.buf_writer out = _io.new_buf_writer(tmpfile, vec(_io.create())); + out.write(_str.bytes(frood)); + } + + let _io.buf_reader inp = _io.new_buf_reader(tmpfile); + let str frood2 = _str.from_bytes(inp.read()); + log frood2; + check (_str.eq(frood, frood2)); +} + +fn main(vec[str] argv) { + test_simple(argv.(0)); +} diff --git a/src/test/run-pass/lib-vec-str-conversions.rs b/src/test/run-pass/lib-vec-str-conversions.rs new file mode 100644 index 00000000..1d6b61a1 --- /dev/null +++ b/src/test/run-pass/lib-vec-str-conversions.rs @@ -0,0 +1,41 @@ +// -*- rust -*- + +use std; +import std._str; +import std._vec; + +fn test_simple() { + let str s1 = "All mimsy were the borogoves"; + + /* + * FIXME from_bytes(vec[u8] v) has constraint is_utf(v), which is + * unimplemented and thereby just fails. This doesn't stop us from + * using from_bytes for now since the constraint system isn't fully + * working, but we should implement is_utf8 before that happens. + */ + + let vec[u8] v = _str.bytes(s1); + let str s2 = _str.from_bytes(v); + + let uint i = 0u; + let uint n1 = _str.byte_len(s1); + let uint n2 = _vec.len[u8](v); + + check (n1 == n2); + + while (i < n1) { + let u8 a = s1.(i); + let u8 b = s2.(i); + log a; + log b; + check (a == b); + i += 1u; + } + + log "refcnt is"; + log _str.refcount(s1); +} + +fn main() { + test_simple(); +} diff --git a/src/test/run-pass/size-and-align.rs b/src/test/run-pass/size-and-align.rs new file mode 100644 index 00000000..4da22558 --- /dev/null +++ b/src/test/run-pass/size-and-align.rs @@ -0,0 +1,15 @@ +// -*- rust -*- + +type clam[T] = tag(a(T, int), b()); + +fn uhoh[T](vec[clam[T]] v) { + alt (v.(1)) { + case (a[T](t, u)) { log "incorrect"; log u; fail; } + case (b[T]()) { log "correct"; } + } +} + +fn main() { + let vec[clam[int]] v = vec(b[int](), b[int](), a[int](42, 17)); + uhoh[int](v); +} 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-10.rs b/src/test/run-pass/task-comm-10.rs new file mode 100644 index 00000000..4cdcf18b --- /dev/null +++ b/src/test/run-pass/task-comm-10.rs @@ -0,0 +1,15 @@ +io fn start(chan[chan[str]] c) { + let port[str] p = port(); + c <| chan(p); + auto a <- p; + // auto b <- p; // Never read the second string. +} + +io fn main() { + let port[chan[str]] p = port(); + auto child = spawn "start" start(chan(p)); + auto c <- p; + c <| "A"; + c <| "B"; + yield; +}
\ No newline at end of file diff --git a/src/test/run-pass/task-comm-11.rs b/src/test/run-pass/task-comm-11.rs new file mode 100644 index 00000000..519eb699 --- /dev/null +++ b/src/test/run-pass/task-comm-11.rs @@ -0,0 +1,10 @@ +io fn start(chan[chan[str]] c) { + let port[str] p = port(); + c <| chan(p); +} + +io fn main() { + let port[chan[str]] p = port(); + auto child = spawn "child" start(chan(p)); + auto c <- p; +}
\ No newline at end of file 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; diff --git a/src/test/run-pass/task-lib.rs b/src/test/run-pass/task-lib.rs new file mode 100644 index 00000000..18499f4e --- /dev/null +++ b/src/test/run-pass/task-lib.rs @@ -0,0 +1,6 @@ +use std; +import std._task; + +fn main() { + _task.sleep(1000000u); +}
\ No newline at end of file diff --git a/src/test/run-pass/task-life-0.rs b/src/test/run-pass/task-life-0.rs new file mode 100644 index 00000000..f15792fb --- /dev/null +++ b/src/test/run-pass/task-life-0.rs @@ -0,0 +1,7 @@ +fn main() -> () { + spawn child("Hello"); +} + +fn child(str s) { + +}
\ No newline at end of file diff --git a/src/test/run-pass/while-flow-graph.rs b/src/test/run-pass/while-flow-graph.rs new file mode 100644 index 00000000..49e7810a --- /dev/null +++ b/src/test/run-pass/while-flow-graph.rs @@ -0,0 +1,4 @@ +fn main() { + let int x = 10; + while (x == 10 && x == 11) { auto y = 0xf00; } +}
\ No newline at end of file |