aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGraydon Hoare <[email protected]>2010-06-23 21:03:09 -0700
committerGraydon Hoare <[email protected]>2010-06-23 21:03:09 -0700
commitd6b7c96c3eb29b9244ece0c046d3f372ff432d04 (patch)
treeb425187e232966063ffc2f0d14c04a55d8f004ef /src
parentInitial git commit. (diff)
downloadrust-d6b7c96c3eb29b9244ece0c046d3f372ff432d04.tar.xz
rust-d6b7c96c3eb29b9244ece0c046d3f372ff432d04.zip
Populate tree.
Diffstat (limited to 'src')
-rw-r--r--src/Makefile391
-rw-r--r--src/README28
-rw-r--r--src/boot/be/abi.ml207
-rw-r--r--src/boot/be/asm.ml755
-rw-r--r--src/boot/be/elf.ml1760
-rw-r--r--src/boot/be/il.ml1135
-rw-r--r--src/boot/be/macho.ml1184
-rw-r--r--src/boot/be/pe.ml1149
-rw-r--r--src/boot/be/ra.ml664
-rw-r--r--src/boot/be/x86.ml2205
-rw-r--r--src/boot/driver/lib.ml232
-rw-r--r--src/boot/driver/llvm/glue.ml37
-rw-r--r--src/boot/driver/main.ml421
-rw-r--r--src/boot/driver/session.ml111
-rw-r--r--src/boot/driver/x86/glue.ml16
-rw-r--r--src/boot/fe/ast.ml1360
-rw-r--r--src/boot/fe/cexp.ml762
-rw-r--r--src/boot/fe/item.ml1139
-rw-r--r--src/boot/fe/lexer.mll362
-rw-r--r--src/boot/fe/parser.ml374
-rw-r--r--src/boot/fe/pexp.ml1354
-rw-r--r--src/boot/fe/token.ml308
-rw-r--r--src/boot/llvm/llabi.ml69
-rw-r--r--src/boot/llvm/llasm.ml192
-rw-r--r--src/boot/llvm/llemit.ml36
-rw-r--r--src/boot/llvm/llfinal.ml96
-rw-r--r--src/boot/llvm/lltrans.ml938
-rw-r--r--src/boot/me/alias.ml134
-rw-r--r--src/boot/me/dead.ml121
-rw-r--r--src/boot/me/dwarf.ml3019
-rw-r--r--src/boot/me/effect.ml313
-rw-r--r--src/boot/me/layout.ml470
-rw-r--r--src/boot/me/loop.ml163
-rw-r--r--src/boot/me/resolve.ml959
-rw-r--r--src/boot/me/semant.ml1969
-rw-r--r--src/boot/me/trans.ml5031
-rw-r--r--src/boot/me/transutil.ml238
-rw-r--r--src/boot/me/type.ml1294
-rw-r--r--src/boot/me/typestate.ml1089
-rw-r--r--src/boot/me/walk.ml687
-rw-r--r--src/boot/util/bits.ml107
-rw-r--r--src/boot/util/common.ml709
-rw-r--r--src/comp/driver/rustc.rs12
-rw-r--r--src/comp/fe/lexer.rs0
-rw-r--r--src/comp/fe/parser.rs0
-rw-r--r--src/comp/rustc.rc20
-rw-r--r--src/etc/tidy.py25
-rw-r--r--src/etc/x86.supp14
-rw-r--r--src/lib/_int.rs20
-rw-r--r--src/lib/_io.rs36
-rw-r--r--src/lib/_str.rs23
-rw-r--r--src/lib/_u8.rs20
-rw-r--r--src/lib/_vec.rs30
-rw-r--r--src/lib/linux_os.rs19
-rw-r--r--src/lib/macos_os.rs19
-rw-r--r--src/lib/std.rc35
-rw-r--r--src/lib/sys.rs7
-rw-r--r--src/lib/win32_os.rs9
-rw-r--r--src/rt/bigint/bigint.h294
-rw-r--r--src/rt/bigint/bigint_ext.cpp553
-rw-r--r--src/rt/bigint/bigint_int.cpp1428
-rw-r--r--src/rt/bigint/low_primes.h1069
-rw-r--r--src/rt/isaac/rand.h56
-rw-r--r--src/rt/isaac/randport.cpp134
-rw-r--r--src/rt/isaac/standard.h57
-rw-r--r--src/rt/memcheck.h309
-rw-r--r--src/rt/rust.cpp267
-rw-r--r--src/rt/rust.h49
-rw-r--r--src/rt/rust_builtin.cpp129
-rw-r--r--src/rt/rust_chan.cpp34
-rw-r--r--src/rt/rust_chan.h22
-rw-r--r--src/rt/rust_comm.cpp199
-rw-r--r--src/rt/rust_crate.cpp63
-rw-r--r--src/rt/rust_crate_cache.cpp306
-rw-r--r--src/rt/rust_crate_reader.cpp578
-rw-r--r--src/rt/rust_dom.cpp271
-rw-r--r--src/rt/rust_dwarf.h198
-rw-r--r--src/rt/rust_internal.h730
-rw-r--r--src/rt/rust_log.cpp117
-rw-r--r--src/rt/rust_log.h59
-rw-r--r--src/rt/rust_task.cpp474
-rw-r--r--src/rt/rust_timer.cpp97
-rw-r--r--src/rt/rust_upcall.cpp654
-rw-r--r--src/rt/rust_util.h155
-rw-r--r--src/rt/sync/fair_ticket_lock.cpp43
-rw-r--r--src/rt/sync/fair_ticket_lock.h15
-rw-r--r--src/rt/sync/lock_free_queue.cpp37
-rw-r--r--src/rt/sync/lock_free_queue.h15
-rw-r--r--src/rt/sync/spin_lock.cpp47
-rw-r--r--src/rt/sync/spin_lock.h14
-rw-r--r--src/rt/uthash/uthash.h766
-rw-r--r--src/rt/uthash/utlist.h280
-rw-r--r--src/rt/util/array_list.h69
-rw-r--r--src/rt/valgrind.h3926
-rw-r--r--src/test/bench/shootout/ackermann.rs25
-rw-r--r--src/test/bench/shootout/binary-trees.rs15
-rw-r--r--src/test/bench/shootout/fibo.rs22
-rw-r--r--src/test/compile-fail/arg-count-mismatch.rs9
-rw-r--r--src/test/compile-fail/arg-type-mismatch.rs10
-rw-r--r--src/test/compile-fail/bad-env-capture.rs10
-rw-r--r--src/test/compile-fail/bad-main.rs4
-rw-r--r--src/test/compile-fail/bad-name.rs6
-rw-r--r--src/test/compile-fail/bad-type-env-capture.rs3
-rw-r--r--src/test/compile-fail/bogus-tag.rs19
-rw-r--r--src/test/compile-fail/comm-makes-io.rs6
-rw-r--r--src/test/compile-fail/dead-code-be.rs11
-rw-r--r--src/test/compile-fail/dead-code-ret.rs11
-rw-r--r--src/test/compile-fail/direct-obj-fn-call.rs12
-rw-r--r--src/test/compile-fail/export.rs14
-rw-r--r--src/test/compile-fail/fru-extra-field.rs9
-rw-r--r--src/test/compile-fail/fru-typestate.rs10
-rw-r--r--src/test/compile-fail/impure-pred.rs19
-rw-r--r--src/test/compile-fail/infinite-tag-type-recursion.rs9
-rw-r--r--src/test/compile-fail/infinite-vec-type-recursion.rs9
-rw-r--r--src/test/compile-fail/io-infects-caller.rs10
-rw-r--r--src/test/compile-fail/log-type-error.rs6
-rw-r--r--src/test/compile-fail/native-makes-unsafe.rs9
-rw-r--r--src/test/compile-fail/not-a-pred.rs16
-rw-r--r--src/test/compile-fail/output-type-mismatch.rs9
-rw-r--r--src/test/compile-fail/pred-on-wrong-slots.rs20
-rw-r--r--src/test/compile-fail/rec-missing-fields.rs10
-rw-r--r--src/test/compile-fail/return-uninit.rs10
-rw-r--r--src/test/compile-fail/slot-as-pred.rs14
-rw-r--r--src/test/compile-fail/spawn-non-nil-fn.rs9
-rw-r--r--src/test/compile-fail/type-shadow.rs12
-rw-r--r--src/test/compile-fail/unnecessary-io.rs4
-rw-r--r--src/test/compile-fail/unnecessary-unsafe.rs4
-rw-r--r--src/test/compile-fail/unsafe-infects-caller.rs13
-rw-r--r--src/test/compile-fail/while-bypass.rs13
-rw-r--r--src/test/compile-fail/while-expr.rs7
-rw-r--r--src/test/compile-fail/while-type-error.rs7
-rw-r--r--src/test/compile-fail/writing-through-read-alias.rs14
-rw-r--r--src/test/run-fail/explicit-fail.rs5
-rw-r--r--src/test/run-fail/fail.rs5
-rw-r--r--src/test/run-fail/linked-failure.rs14
-rw-r--r--src/test/run-fail/pred.rs17
-rw-r--r--src/test/run-fail/str-overrun.rs16
-rw-r--r--src/test/run-fail/vec-overrun.rs11
-rw-r--r--src/test/run-fail/vec-underrun.rs11
-rw-r--r--src/test/run-pass/acyclic-unwind.rs30
-rw-r--r--src/test/run-pass/alt-tag.rs39
-rw-r--r--src/test/run-pass/argv.rs9
-rw-r--r--src/test/run-pass/basic.rs50
-rw-r--r--src/test/run-pass/bind-obj-ctor.rs17
-rw-r--r--src/test/run-pass/bind-thunk.rs11
-rw-r--r--src/test/run-pass/bind-trivial.rs11
-rw-r--r--src/test/run-pass/bitwise.rs21
-rw-r--r--src/test/run-pass/box-unbox.rs10
-rw-r--r--src/test/run-pass/cast.rs16
-rw-r--r--src/test/run-pass/char.rs12
-rw-r--r--src/test/run-pass/clone-with-exterior.rs10
-rw-r--r--src/test/run-pass/comm.rs16
-rw-r--r--src/test/run-pass/command-line-args.rs3
-rw-r--r--src/test/run-pass/complex.rs32
-rw-r--r--src/test/run-pass/dead-code-one-arm-if.rs8
-rw-r--r--src/test/run-pass/deep.rs14
-rw-r--r--src/test/run-pass/div-mod.rs17
-rw-r--r--src/test/run-pass/drop-on-ret.rs12
-rw-r--r--src/test/run-pass/else-if.rs19
-rw-r--r--src/test/run-pass/export-non-interference.rs6
-rw-r--r--src/test/run-pass/exterior.rs18
-rw-r--r--src/test/run-pass/fact.rs21
-rw-r--r--src/test/run-pass/foreach-put-structured.rs22
-rw-r--r--src/test/run-pass/foreach-simple-outer-slot.rs22
-rw-r--r--src/test/run-pass/foreach-simple.rs17
-rw-r--r--src/test/run-pass/fun-call-variants.rs19
-rw-r--r--src/test/run-pass/fun-indirect-call.rs11
-rw-r--r--src/test/run-pass/generic-derived-type.rs17
-rw-r--r--src/test/run-pass/generic-drop-glue.rs9
-rw-r--r--src/test/run-pass/generic-exterior-box.rs13
-rw-r--r--src/test/run-pass/generic-fn-infer.rs13
-rw-r--r--src/test/run-pass/generic-fn.rs32
-rw-r--r--src/test/run-pass/generic-obj-with-derived-type.rs17
-rw-r--r--src/test/run-pass/generic-obj.rs24
-rw-r--r--src/test/run-pass/generic-recursive-tag.rs5
-rw-r--r--src/test/run-pass/generic-tag-alt.rs9
-rw-r--r--src/test/run-pass/generic-tag.rs6
-rw-r--r--src/test/run-pass/generic-type-synonym.rs4
-rw-r--r--src/test/run-pass/generic-type.rs6
-rw-r--r--src/test/run-pass/hello.rs6
-rw-r--r--src/test/run-pass/i32-sub.rs8
-rw-r--r--src/test/run-pass/i8-incr.rs9
-rw-r--r--src/test/run-pass/import.rs14
-rw-r--r--src/test/run-pass/inner-module.rs17
-rw-r--r--src/test/run-pass/int.rs6
-rw-r--r--src/test/run-pass/large-records.rs14
-rw-r--r--src/test/run-pass/lazy-and-or.rs22
-rw-r--r--src/test/run-pass/lazychan.rs23
-rw-r--r--src/test/run-pass/linear-for-loop.rs38
-rw-r--r--src/test/run-pass/list.rs7
-rw-r--r--src/test/run-pass/many.rs19
-rw-r--r--src/test/run-pass/mlist-cycle.rs10
-rw-r--r--src/test/run-pass/mlist.rs7
-rw-r--r--src/test/run-pass/mutable-vec-drop.rs4
-rw-r--r--src/test/run-pass/mutual-recursion-group.rs11
-rw-r--r--src/test/run-pass/native-mod-src/inner.rs12
-rw-r--r--src/test/run-pass/native-mod.rc16
-rw-r--r--src/test/run-pass/native-opaque-type.rs7
-rw-r--r--src/test/run-pass/native-src/native.rs7
-rw-r--r--src/test/run-pass/native.rc12
-rw-r--r--src/test/run-pass/obj-as.rs21
-rw-r--r--src/test/run-pass/obj-drop.rs6
-rw-r--r--src/test/run-pass/obj-dtor.rs33
-rw-r--r--src/test/run-pass/obj-with-vec.rs11
-rw-r--r--src/test/run-pass/opeq.rs22
-rw-r--r--src/test/run-pass/pred.rs18
-rw-r--r--src/test/run-pass/preempt.rs26
-rw-r--r--src/test/run-pass/readalias.rs12
-rw-r--r--src/test/run-pass/rec-auto.rs9
-rw-r--r--src/test/run-pass/rec-extend.rs19
-rw-r--r--src/test/run-pass/rec-tup.rs25
-rw-r--r--src/test/run-pass/rec.rs23
-rw-r--r--src/test/run-pass/return-nil.rs8
-rw-r--r--src/test/run-pass/simple-obj.rs12
-rw-r--r--src/test/run-pass/spawn-fn.rs18
-rw-r--r--src/test/run-pass/spawn.rs10
-rw-r--r--src/test/run-pass/stateful-obj.rs23
-rw-r--r--src/test/run-pass/str-append.rs8
-rw-r--r--src/test/run-pass/str-concat.rs9
-rw-r--r--src/test/run-pass/str-idx.rs7
-rw-r--r--src/test/run-pass/syntax-extension.rs4
-rw-r--r--src/test/run-pass/tag.rs14
-rw-r--r--src/test/run-pass/tail-cps.rs34
-rw-r--r--src/test/run-pass/tail-direct.rs24
-rw-r--r--src/test/run-pass/task-comm.rs127
-rw-r--r--src/test/run-pass/threads.rs16
-rw-r--r--src/test/run-pass/tup.rs19
-rw-r--r--src/test/run-pass/type-sizes.rs20
-rw-r--r--src/test/run-pass/u32-decr.rs8
-rw-r--r--src/test/run-pass/u8-incr-decr.rs12
-rw-r--r--src/test/run-pass/u8-incr.rs12
-rw-r--r--src/test/run-pass/uint.rs6
-rw-r--r--src/test/run-pass/unit.rs13
-rw-r--r--src/test/run-pass/user.rs14
-rw-r--r--src/test/run-pass/utf8.rs48
-rw-r--r--src/test/run-pass/vec-append.rs10
-rw-r--r--src/test/run-pass/vec-concat.rs11
-rw-r--r--src/test/run-pass/vec-drop.rs4
-rw-r--r--src/test/run-pass/vec-slice.rs6
-rw-r--r--src/test/run-pass/vec.rs13
-rw-r--r--src/test/run-pass/writealias.rs13
-rw-r--r--src/test/run-pass/yield.rs20
-rw-r--r--src/test/run-pass/yield2.rs10
243 files changed, 49419 insertions, 166 deletions
diff --git a/src/Makefile b/src/Makefile
index 95d530df..5d4e6aa0 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -19,27 +19,29 @@ endif
CFG_INFO := $(info cfg: building on $(CFG_OSTYPE) $(CFG_CPUTYPE))
-CFG_GCC_COMPILE_FLAGS :=
+CFG_GCC_CFLAGS :=
CFG_GCC_LINK_FLAGS :=
CFG_VALGRIND :=
CFG_LLVM_CONFIG := llvm-config
-CFG_BOOT_FLAGS :=
+CFG_BOOT_FLAGS := $(FLAGS)
ifeq ($(CFG_OSTYPE), Linux)
CFG_RUNTIME := librustrt.so
CFG_STDLIB := libstd.so
- CFG_GCC_COMPILE_FLAGS += -fPIC
+ CFG_GCC_CFLAGS += -fPIC
CFG_GCC_LINK_FLAGS += -shared -fPIC -ldl -lpthread
ifeq ($(CFG_CPUTYPE), x86_64)
- CFG_GCC_COMPILE_FLAGS += -m32
+ CFG_GCC_CFLAGS += -m32
CFG_GCC_LINK_FLAGS += -m32
endif
CFG_NATIVE := 1
CFG_UNIXY := 1
CFG_VALGRIND := $(shell which valgrind)
ifdef CFG_VALGRIND
- CFG_VALGRIND += --run-libc-freeres=no --leak-check=full --quiet --vex-iropt-level=0
+ CFG_VALGRIND += --leak-check=full \
+ --quiet --vex-iropt-level=0 \
+ --suppressions=etc/x86.supp
endif
endif
@@ -52,7 +54,7 @@ ifeq ($(CFG_OSTYPE), Darwin)
# "on an i386" when the whole userspace is 64-bit and the compiler
# emits 64-bit binaries by default. So we just force -m32 here. Smarter
# approaches welcome!
- CFG_GCC_COMPILE_FLAGS += -m32
+ CFG_GCC_CFLAGS += -m32
CFG_GCC_LINK_FLAGS += -m32
endif
@@ -73,7 +75,7 @@ ifdef CFG_WINDOWSY
CFG_EXE_SUFFIX := .exe
CFG_BOOT := ./rustboot.exe
CFG_COMPILER := ./rustc.exe
- CFG_GCC_COMPILE_FLAGS += -march=i686
+ CFG_GCC_CFLAGS += -march=i686
CFG_GCC_LINK_FLAGS += -shared -fPIC
CFG_RUN_TARG = $(1)
# FIXME: support msvc at some point
@@ -99,10 +101,10 @@ ifdef CFG_UNIXY
endif
CFG_OBJ_SUFFIX := .o
CFG_EXE_SUFFIX := .exe
- CFG_GCC_COMPILE_FLAGS :=
+ CFG_GCC_CFLAGS :=
CFG_GCC_LINK_FLAGS := -shared
ifeq ($(CFG_CPUTYPE), x86_64)
- CFG_GCC_COMPILE_FLAGS += -m32
+ CFG_GCC_CFLAGS += -m32
CFG_GCC_LINK_FLAGS += -m32
endif
endif
@@ -110,11 +112,11 @@ endif
ifdef CFG_GCC
CFG_INFO := $(info cfg: using gcc)
- CFG_GCC_COMPILE_FLAGS += -Wall -Werror -fno-rtti -fno-exceptions -g
+ CFG_GCC_CFLAGS += -Wall -Werror -fno-rtti -fno-exceptions -g
CFG_GCC_LINK_FLAGS += -g
- CFG_COMPILE_C = $(CFG_GCC_CROSS)g++ $(CFG_GCC_COMPILE_FLAGS) -c -o $(1) $(2)
+ CFG_COMPILE_C = $(CFG_GCC_CROSS)g++ $(CFG_GCC_CFLAGS) -c -o $(1) $(2)
CFG_LINK_C = $(CFG_GCC_CROSS)g++ $(CFG_GCC_LINK_FLAGS) -o $(1)
- CFG_DEPEND_C = $(CFG_GCC_CROSS)g++ $(CFG_GCC_COMPILE_FLAGS) -MT "$(1)" -MM $(2)
+ CFG_DEPEND_C = $(CFG_GCC_CROSS)g++ $(CFG_GCC_CFLAGS) -MT "$(1)" -MM $(2)
else
CFG_ERR := $(error please try on a system with gcc)
endif
@@ -153,7 +155,8 @@ ifneq ($(CFG_LLVM_CONFIG),)
$(info cfg: using LLVM version 2.8svn)
else
CFG_LLVM_CONFIG :=
- $(info cfg: incompatible LLVM version $(CFG_LLVM_VERSION), expected 2.8svn)
+ $(info cfg: incompatible LLVM version $(CFG_LLVM_VERSION), \
+ expected 2.8svn)
endif
endif
ifdef CFG_LLVM_CONFIG
@@ -161,11 +164,12 @@ ifdef CFG_LLVM_CONFIG
WHERE := $(shell ocamlc -where)
LLVM_LIBS := llvm.cma llvm_bitwriter.cma
LLVM_NATIVE_LIBS := llvm.cmxa llvm_bitwiter.cmxa
- LLVM_CLIBS := $(shell for c in `$(CFG_LLVM_CONFIG) --ldflags --libs` -lllvm -lllvm_bitwriter; do echo -cclib && echo $$c; done | xargs echo)
+ LLVM_CLIBS := $(shell for c in `$(CFG_LLVM_CONFIG) --ldflags --libs` \
+ -lllvm -lllvm_bitwriter; do echo -cclib && echo $$c; done | xargs echo)
LLVM_INCS := -I boot/llvm -I $(WHERE)
- LLVM_MLS := $(addprefix boot/llvm/, llabi.ml llasm.ml llfinal.ml lltrans.ml \
- llemit.ml)
- CFG_LLC_COMPILE_FLAGS := -march=x86
+ LLVM_MLS := $(addprefix boot/llvm/, llabi.ml llasm.ml llfinal.ml \
+ lltrans.ml llemit.ml)
+ CFG_LLC_CFLAGS := -march=x86
$(info cfg: found llvm-config at $(CFG_LLVM_CONFIG))
else
VARIANT=x86
@@ -190,7 +194,8 @@ ML_INCS := -I boot/fe -I boot/me -I boot/be -I boot/driver/$(VARIANT) \
ML_LIBS := unix.cma nums.cma bigarray.cma
ML_NATIVE_LIBS := unix.cmxa nums.cmxa bigarray.cmxa
OCAMLC_FLAGS := -g $(ML_INCS) -w Ael -warn-error Ael
-OCAMLOPT_FLAGS := $(ML_INCS) -w Ael -warn-error Ael $(CFG_OCAMLOPT_PROFILE_FLAGS)
+OCAMLOPT_FLAGS := $(ML_INCS) -w Ael -warn-error Ael \
+ $(CFG_OCAMLOPT_PROFILE_FLAGS)
ifdef CFG_LLVM_CONFIG
ML_LIBS += $(LLVM_LIBS) -custom -cclib -lstdc++ $(LLVM_CLIBS)
@@ -205,11 +210,12 @@ DRIVER_BOT_MLS := $(addprefix boot/driver/, session.ml)
BE_MLS := $(addprefix boot/be/, x86.ml ra.ml pe.ml elf.ml \
macho.ml)
IL_MLS := $(addprefix boot/be/, asm.ml il.ml abi.ml)
-ME_MLS := $(addprefix boot/me/, walk.ml semant.ml resolve.ml alias.ml type.ml dead.ml \
- typestate.ml mode.ml mutable.ml gctype.ml loop.ml layout.ml transutil.ml \
- trans.ml dwarf.ml)
-FE_MLS := $(addprefix boot/fe/, ast.ml token.ml lexer.ml parser.ml pexp.ml item.ml cexp.ml)
-DRIVER_TOP_MLS := $(addprefix boot/driver/, $(VARIANT)/glue.ml lib.ml main.ml)
+ME_MLS := $(addprefix boot/me/, walk.ml semant.ml resolve.ml alias.ml \
+ type.ml dead.ml effect.ml typestate.ml loop.ml layout.ml \
+ transutil.ml trans.ml dwarf.ml)
+FE_MLS := $(addprefix boot/fe/, ast.ml token.ml lexer.ml parser.ml pexp.ml \
+ item.ml cexp.ml)
+DRIVER_TOP_MLS := $(addprefix boot/driver/, lib.ml $(VARIANT)/glue.ml main.ml)
BOOT_MLS := $(UTIL_BOT_MLS) $(DRIVER_BOT_MLS) $(FE_MLS) $(IL_MLS) $(ME_MLS) \
$(BE_MLS) $(LLVM_MLS) $(DRIVER_TOP_MLS)
@@ -226,8 +232,12 @@ RUNTIME_CS := rt/rust.cpp \
rt/rust_comm.cpp \
rt/rust_dom.cpp \
rt/rust_task.cpp \
+ rt/rust_chan.cpp \
rt/rust_upcall.cpp \
+ rt/rust_log.cpp \
+ rt/rust_timer.cpp \
rt/isaac/randport.cpp
+
RUNTIME_HDR := rt/rust.h \
rt/rust_dwarf.h \
rt/rust_internal.h \
@@ -253,7 +263,8 @@ $(CFG_RUNTIME): $(RUNTIME_OBJS) $(MKFILES) $(RUNTIME_HDR)
$(CFG_STDLIB): $(STDLIB_CRATE) $(CFG_BOOT) $(MKFILES)
@$(call CFG_ECHO, compile: $<)
- $(CFG_QUIET)OCAMLRUNPARAM="b1" $(CFG_BOOT) $(CFG_BOOT_FLAGS) -shared -o $@ $(STDLIB_CRATE)
+ $(CFG_QUIET)OCAMLRUNPARAM="b1" $(CFG_BOOT) $(CFG_BOOT_FLAGS) \
+ -shared -o $@ $(STDLIB_CRATE)
%$(CFG_OBJ_SUFFIX): %.cpp $(MKFILES)
@$(call CFG_ECHO, compile: $<)
@@ -262,7 +273,8 @@ $(CFG_STDLIB): $(STDLIB_CRATE) $(CFG_BOOT) $(MKFILES)
ifdef CFG_NATIVE
$(CFG_BOOT): $(BOOT_CMXS) $(MKFILES)
@$(call CFG_ECHO, compile: $<)
- $(CFG_QUIET)ocamlopt$(OPT) -o $@ $(OCAMLOPT_FLAGS) $(ML_NATIVE_LIBS) $(BOOT_CMXS)
+ $(CFG_QUIET)ocamlopt$(OPT) -o $@ $(OCAMLOPT_FLAGS) $(ML_NATIVE_LIBS) \
+ $(BOOT_CMXS)
else
$(CFG_BOOT): $(BOOT_CMOS) $(MKFILES)
@$(call CFG_ECHO, compile: $<)
@@ -288,7 +300,7 @@ endif
# Main compiler targets and rules
######################################################################
-$(CFG_COMPILER): $(COMPILER_CRATE) $(CFG_BOOT) $(CFG_RUNTIME) $(CFG_STDLIB)
+$(CFG_COMPILER): $(COMPILER_INPUTS) $(CFG_BOOT) $(CFG_RUNTIME) $(CFG_STDLIB)
@$(call CFG_ECHO, compile: $<)
$(CFG_QUIET)OCAMLRUNPARAM="b1" $(CFG_BOOT) $(CFG_BOOT_FLAGS) -o $@ $<
$(CFG_QUIET)chmod 0755 $@
@@ -302,13 +314,17 @@ self: $(CFG_COMPILER)
# Testing
######################################################################
-TEST_XFAILS_X86 := test/run-pass/mlist_cycle.rs \
+TEST_XFAILS_X86 := test/run-pass/mlist-cycle.rs \
test/run-pass/clone-with-exterior.rs \
+ test/run-pass/obj-as.rs \
test/run-pass/rec-auto.rs \
test/run-pass/vec-slice.rs \
test/run-pass/generic-fn-infer.rs \
+ test/run-pass/generic-recursive-tag.rs \
test/run-pass/generic-tag.rs \
+ test/run-pass/generic-tag-alt.rs \
test/run-pass/bind-obj-ctor.rs \
+ test/run-pass/task-comm.rs \
test/compile-fail/rec-missing-fields.rs \
test/compile-fail/infinite-tag-type-recursion.rs \
test/compile-fail/infinite-vec-type-recursion.rs
@@ -316,61 +332,74 @@ TEST_XFAILS_X86 := test/run-pass/mlist_cycle.rs \
TEST_XFAILS_LLVM := $(addprefix test/run-pass/, \
acyclic-unwind.rs \
alt-tag.rs \
+ argv.rs \
basic.rs \
bind-obj-ctor.rs \
bind-thunk.rs \
bind-trivial.rs \
+ bitwise.rs \
+ box-unbox.rs \
cast.rs \
char.rs \
clone-with-exterior.rs \
comm.rs \
+ command-line-args.rs \
complex.rs \
dead-code-one-arm-if.rs \
deep.rs \
div-mod.rs \
drop-on-ret.rs \
+ else-if.rs \
+ export-non-interference.rs \
exterior.rs \
- foreach-simple.rs \
- foreach-simple-outer-slot.rs \
foreach-put-structured.rs \
- vec-slice.rs \
- simple-obj.rs \
- import.rs \
+ foreach-simple-outer-slot.rs \
+ foreach-simple.rs \
fun-call-variants.rs \
fun-indirect-call.rs \
generic-derived-type.rs \
generic-drop-glue.rs \
+ generic-exterior-box.rs \
+ generic-fn-infer.rs \
generic-fn.rs \
- generic-obj.rs \
generic-obj-with-derived-type.rs \
+ generic-obj.rs \
+ generic-recursive-tag.rs \
+ generic-tag-alt.rs \
generic-tag.rs \
+ generic-type-synonym.rs \
generic-type.rs \
- generic-fn-infer.rs \
- vec-append.rs \
- vec-concat.rs \
- vec-drop.rs \
- mutable-vec-drop.rs \
+ i32-sub.rs \
+ i8-incr.rs \
+ import.rs \
inner-module.rs \
large-records.rs \
+ lazy-and-or.rs \
lazychan.rs \
linear-for-loop.rs \
+ list.rs \
many.rs \
+ mlist-cycle.rs \
mlist.rs \
- mlist_cycle.rs \
+ mutable-vec-drop.rs \
mutual-recursion-group.rs \
+ native-mod.rc \
+ native-opaque-type.rs \
native.rc \
- command-line-args.rs \
- native_mod.rc \
+ obj-as.rs \
+ obj-drop.rs \
+ obj-dtor.rs \
+ obj-with-vec.rs \
opeq.rs \
+ preempt.rs \
pred.rs \
readalias.rs \
rec-auto.rs \
rec-extend.rs \
+ rec-tup.rs \
rec.rs \
- rec_tup.rs \
return-nil.rs \
- i32-sub.rs \
- i8-incr.rs \
+ simple-obj.rs \
spawn-fn.rs \
spawn.rs \
stateful-obj.rs \
@@ -383,31 +412,31 @@ TEST_XFAILS_LLVM := $(addprefix test/run-pass/, \
tail-direct.rs \
threads.rs \
tup.rs \
+ type-sizes.rs \
u32-decr.rs \
u8-incr-decr.rs \
u8-incr.rs \
unit.rs \
user.rs \
+ utf8.rs \
+ vec-append.rs \
+ vec-concat.rs \
+ vec-drop.rs \
+ vec-slice.rs \
vec.rs \
writealias.rs \
yield.rs \
yield2.rs \
- native-opaque-type.rs \
- type-sizes.rs \
- obj-drop.rs \
- obj-dtor.rs \
- obj-with-vec.rs \
- else-if.rs \
- lazy-and-or.rs \
+ task-comm.rs \
) \
$(addprefix test/run-fail/, \
explicit-fail.rs \
fail.rs \
linked-failure.rs \
pred.rs \
- vec_overrun.rs \
- str_overrun.rs \
- vec_underrun.rs \
+ vec-overrun.rs \
+ str-overrun.rs \
+ vec-underrun.rs \
) \
$(addprefix test/compile-fail/, \
rec-missing-fields.rs \
@@ -416,93 +445,109 @@ TEST_XFAILS_LLVM := $(addprefix test/run-pass/, \
)
ifdef CFG_WINDOWSY
-TEST_XFAILS_X86 += test/run-pass/native_mod.rc
-TEST_XFAILS_LLVM += test/run-pass/native_mod.rc
+TEST_XFAILS_X86 += test/run-pass/native-mod.rc
+TEST_XFAILS_LLVM += test/run-pass/native-mod.rc
+else
+TEST_XFAILS_X86 += test/run-pass/preempt.rs
+TEST_XFAILS_LLVM += test/run-pass/preempt.rs
endif
-TEST_RUN_PASS_CRATES_X86 := $(filter-out $(TEST_XFAILS_X86), $(wildcard test/run-pass/*.rc))
-TEST_RUN_PASS_CRATES_LLVM := $(filter-out $(TEST_XFAILS_LLVM), $(wildcard test/run-pass/*.rc))
-TEST_RUN_PASS_SOURCES_X86 := $(filter-out $(TEST_XFAILS_X86), $(wildcard test/run-pass/*.rs))
-TEST_RUN_PASS_SOURCES_LLVM := $(filter-out $(TEST_XFAILS_LLVM), $(wildcard test/run-pass/*.rs))
-TEST_RUN_PASS_EXTRAS := $(wildcard test/run-pass/*/*.rs)
-TEST_RUN_PASS_EXES_X86 := \
- $(TEST_RUN_PASS_CRATES_X86:.rc=.x86$(CFG_EXE_SUFFIX)) \
- $(TEST_RUN_PASS_SOURCES_X86:.rs=.x86$(CFG_EXE_SUFFIX))
-TEST_RUN_PASS_EXES_LLVM := \
- $(TEST_RUN_PASS_CRATES_LLVM:.rc=.llvm$(CFG_EXE_SUFFIX)) \
- $(TEST_RUN_PASS_SOURCES_LLVM:.rs=.llvm$(CFG_EXE_SUFFIX))
-TEST_RUN_PASS_OUTS_X86 := \
- $(TEST_RUN_PASS_EXES_X86:.x86$(CFG_EXE_SUFFIX)=.x86.out)
-TEST_RUN_PASS_OUTS_LLVM := \
- $(TEST_RUN_PASS_EXES_LLVM:.llvm$(CFG_EXE_SUFFIX)=.llvm.out)
-
-
-TEST_RUN_FAIL_CRATES_X86 := $(filter-out $(TEST_XFAILS_X86), $(wildcard test/run-fail/*.rc))
-TEST_RUN_FAIL_CRATES_LLVM := $(filter-out $(TEST_XFAILS_LLVM), $(wildcard test/run-fail/*.rc))
-TEST_RUN_FAIL_SOURCES_X86 := $(filter-out $(TEST_XFAILS_X86), $(wildcard test/run-fail/*.rs))
-TEST_RUN_FAIL_SOURCES_LLVM := $(filter-out $(TEST_XFAILS_LLVM), $(wildcard test/run-fail/*.rs))
-TEST_RUN_FAIL_EXTRAS := $(wildcard test/run-fail/*/*.rs)
-TEST_RUN_FAIL_EXES_X86 := \
- $(TEST_RUN_FAIL_CRATES_X86:.rc=.x86$(CFG_EXE_SUFFIX)) \
- $(TEST_RUN_FAIL_SOURCES_X86:.rs=.x86$(CFG_EXE_SUFFIX))
-TEST_RUN_FAIL_EXES_LLVM := \
- $(TEST_RUN_FAIL_CRATES_LLVM:.rc=.llvm$(CFG_EXE_SUFFIX)) \
- $(TEST_RUN_FAIL_SOURCES_LLVM:.rs=.llvm$(CFG_EXE_SUFFIX))
-TEST_RUN_FAIL_OUTS_X86 := \
- $(TEST_RUN_FAIL_EXES_X86:.x86$(CFG_EXE_SUFFIX)=.x86.out)
-TEST_RUN_FAIL_OUTS_LLVM := \
- $(TEST_RUN_FAIL_EXES_LLVM:.llvm$(CFG_EXE_SUFFIX)=.llvm.out)
-
-
-TEST_COMPILE_FAIL_CRATES_X86 := $(filter-out $(TEST_XFAILS_X86), $(wildcard test/compile-fail/*.rc))
-TEST_COMPILE_FAIL_CRATES_LLVM := $(filter-out $(TEST_XFAILS_LLVM), $(wildcard test/compile-fail/*.rc))
-TEST_COMPILE_FAIL_SOURCES_X86 := $(filter-out $(TEST_XFAILS_X86), $(wildcard test/compile-fail/*.rs))
-TEST_COMPILE_FAIL_SOURCES_LLVM := $(filter-out $(TEST_XFAILS_LLVM), $(wildcard test/compile-fail/*.rs))
-TEST_COMPILE_FAIL_EXTRAS := $(wildcard test/compile-fail/*/*.rs)
-TEST_COMPILE_FAIL_EXES_X86 := \
- $(TEST_COMPILE_FAIL_CRATES_X86:.rc=.x86$(CFG_EXE_SUFFIX)) \
- $(TEST_COMPILE_FAIL_SOURCES_X86:.rs=.x86$(CFG_EXE_SUFFIX))
-TEST_COMPILE_FAIL_EXES_LLVM := \
- $(TEST_COMPILE_FAIL_CRATES_LLVM:.rc=.llvm$(CFG_EXE_SUFFIX)) \
- $(TEST_COMPILE_FAIL_SOURCES_LLVM:.rs=.llvm$(CFG_EXE_SUFFIX))
-TEST_COMPILE_FAIL_OUTS_X86 := \
- $(TEST_COMPILE_FAIL_EXES_X86:.x86$(CFG_EXE_SUFFIX)=.x86.out)
-TEST_COMPILE_FAIL_OUTS_LLVM := \
- $(TEST_COMPILE_FAIL_EXES_LLVM:.llvm$(CFG_EXE_SUFFIX)=.llvm.out)
-
-ALL_TEST_CRATES := $(TEST_COMPILE_FAIL_CRATES_X86) \
- $(TEST_RUN_FAIL_CRATES_X86) \
- $(TEST_RUN_PASS_CRATES_X86)
-
-ALL_TEST_SOURCES := $(TEST_COMPILE_FAIL_SOURCES_X86) \
- $(TEST_RUN_FAIL_SOURCES_X86) \
- $(TEST_RUN_PASS_SOURCES_X86)
+RPASS_RC := $(wildcard test/run-pass/*.rc)
+RPASS_RS := $(wildcard test/run-pass/*.rs)
+RFAIL_RC := $(wildcard test/run-fail/*.rc)
+RFAIL_RS := $(wildcard test/run-fail/*.rs)
+CFAIL_RC := $(wildcard test/compile-fail/*.rc)
+CFAIL_RS := $(wildcard test/compile-fail/*.rs)
+
+TEST_RPASS_CRATES_X86 := $(filter-out $(TEST_XFAILS_X86), $(RPASS_RC))
+TEST_RPASS_CRATES_LLVM := $(filter-out $(TEST_XFAILS_LLVM), $(RPASS_RC))
+TEST_RPASS_SOURCES_X86 := $(filter-out $(TEST_XFAILS_X86), $(RPASS_RS))
+TEST_RPASS_SOURCES_LLVM := $(filter-out $(TEST_XFAILS_LLVM), $(RPASS_RS))
+TEST_RPASS_EXTRAS := $(wildcard test/run-pass/*/*.rs)
+TEST_RPASS_EXES_X86 := \
+ $(TEST_RPASS_CRATES_X86:.rc=.x86$(CFG_EXE_SUFFIX)) \
+ $(TEST_RPASS_SOURCES_X86:.rs=.x86$(CFG_EXE_SUFFIX))
+TEST_RPASS_EXES_LLVM := \
+ $(TEST_RPASS_CRATES_LLVM:.rc=.llvm$(CFG_EXE_SUFFIX)) \
+ $(TEST_RPASS_SOURCES_LLVM:.rs=.llvm$(CFG_EXE_SUFFIX))
+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_RFAIL_CRATES_X86 := $(filter-out $(TEST_XFAILS_X86), $(RFAIL_RC))
+TEST_RFAIL_CRATES_LLVM := $(filter-out $(TEST_XFAILS_LLVM), $(RFAIL_RC))
+TEST_RFAIL_SOURCES_X86 := $(filter-out $(TEST_XFAILS_X86), $(RFAIL_RS))
+TEST_RFAIL_SOURCES_LLVM := $(filter-out $(TEST_XFAILS_LLVM), $(RFAIL_RS))
+TEST_RFAIL_EXTRAS := $(wildcard test/run-fail/*/*.rs)
+TEST_RFAIL_EXES_X86 := \
+ $(TEST_RFAIL_CRATES_X86:.rc=.x86$(CFG_EXE_SUFFIX)) \
+ $(TEST_RFAIL_SOURCES_X86:.rs=.x86$(CFG_EXE_SUFFIX))
+TEST_RFAIL_EXES_LLVM := \
+ $(TEST_RFAIL_CRATES_LLVM:.rc=.llvm$(CFG_EXE_SUFFIX)) \
+ $(TEST_RFAIL_SOURCES_LLVM:.rs=.llvm$(CFG_EXE_SUFFIX))
+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_CFAIL_CRATES_X86 := $(filter-out $(TEST_XFAILS_X86), $(CFAIL_RC))
+TEST_CFAIL_CRATES_LLVM := $(filter-out $(TEST_XFAILS_LLVM), $(CFAIL_RC))
+TEST_CFAIL_SOURCES_X86 := $(filter-out $(TEST_XFAILS_X86), $(CFAIL_RS))
+TEST_CFAIL_SOURCES_LLVM := $(filter-out $(TEST_XFAILS_LLVM), $(CFAIL_RS))
+TEST_CFAIL_EXTRAS := $(wildcard test/compile-fail/*/*.rs)
+TEST_CFAIL_EXES_X86 := \
+ $(TEST_CFAIL_CRATES_X86:.rc=.x86$(CFG_EXE_SUFFIX)) \
+ $(TEST_CFAIL_SOURCES_X86:.rs=.x86$(CFG_EXE_SUFFIX))
+TEST_CFAIL_EXES_LLVM := \
+ $(TEST_CFAIL_CRATES_LLVM:.rc=.llvm$(CFG_EXE_SUFFIX)) \
+ $(TEST_CFAIL_SOURCES_LLVM:.rs=.llvm$(CFG_EXE_SUFFIX))
+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)
+
+ALL_TEST_CRATES := $(TEST_CFAIL_CRATES_X86) \
+ $(TEST_RFAIL_CRATES_X86) \
+ $(TEST_RPASS_CRATES_X86)
+
+ALL_TEST_SOURCES := $(TEST_CFAIL_SOURCES_X86) \
+ $(TEST_RFAIL_SOURCES_X86) \
+ $(TEST_RPASS_SOURCES_X86)
ALL_TEST_INPUTS := $(wildcard test/*/*.rs test/*/*/*.rs test/*/*.rc)
-check_nocompile: $(TEST_COMPILE_FAIL_OUTS_X86)
+check_nocompile: $(TEST_CFAIL_OUTS_X86)
+
+check: tidy \
+ $(TEST_RPASS_EXES_X86) $(TEST_RFAIL_EXES_X86) \
+ $(TEST_RPASS_OUTS_X86) $(TEST_RFAIL_OUTS_X86) \
+ $(TEST_CFAIL_OUTS_X86)
-check: $(TEST_RUN_PASS_EXES_X86) $(TEST_RUN_FAIL_EXES_X86) \
- $(TEST_RUN_PASS_OUTS_X86) $(TEST_RUN_FAIL_OUTS_X86) \
- $(TEST_COMPILE_FAIL_OUTS_X86)
ifeq ($(VARIANT),llvm)
-ALL_TEST_CRATES += $(TEST_COMPILE_FAIL_CRATES_LLVM) \
- $(TEST_RUN_FAIL_CRATES_LLVM) \
- $(TEST_RUN_PASS_CRATES_LLVM)
+ALL_TEST_CRATES += $(TEST_CFAIL_CRATES_LLVM) \
+ $(TEST_RFAIL_CRATES_LLVM) \
+ $(TEST_RPASS_CRATES_LLVM)
-ALL_TEST_SOURCES += $(TEST_COMPILE_FAIL_SOURCES_LLVM) \
- $(TEST_RUN_FAIL_SOURCES_LLVM) \
- $(TEST_RUN_PASS_SOURCES_LLVM)
+ALL_TEST_SOURCES += $(TEST_CFAIL_SOURCES_LLVM) \
+ $(TEST_RFAIL_SOURCES_LLVM) \
+ $(TEST_RPASS_SOURCES_LLVM)
-check_nocompile: $(TEST_COMPILE_FAIL_OUTS_LLVM)
+check_nocompile: $(TEST_CFAIL_OUTS_LLVM)
-check: $(TEST_RUN_PASS_EXES_LLVM) $(TEST_RUN_FAIL_EXES_LLVM) \
- $(TEST_RUN_PASS_OUTS_LLVM) $(TEST_RUN_FAIL_OUTS_LLVM) \
- $(TEST_COMPILE_FAIL_OUTS_LLVM)
+check: tidy \
+ $(TEST_RPASS_EXES_LLVM) $(TEST_RFAIL_EXES_LLVM) \
+ $(TEST_RPASS_OUTS_LLVM) $(TEST_RFAIL_OUTS_LLVM) \
+ $(TEST_CFAIL_OUTS_LLVM)
endif
+REQ := $(CFG_BOOT) $(CFG_RUNTIME) $(CFG_STDLIB)
+BOOT := $(CFG_QUIET)OCAMLRUNPARAM="b1" $(CFG_BOOT) $(CFG_BOOT_FLAGS)
+
test/run-pass/%.out: test/run-pass/%$(CFG_EXE_SUFFIX) $(CFG_RUNTIME)
@$(call CFG_ECHO, run: $<)
$(CFG_QUIET)$(call CFG_RUN_TARG, $<) > $@
@@ -510,55 +555,57 @@ test/run-pass/%.out: test/run-pass/%$(CFG_EXE_SUFFIX) $(CFG_RUNTIME)
test/run-fail/%.out: test/run-fail/%$(CFG_EXE_SUFFIX) $(CFG_RUNTIME)
@$(call CFG_ECHO, run: $<)
$(CFG_QUIET)rm -f $@
- $(CFG_QUIET)$(call CFG_RUN_TARG, $<) >$@ 2>&1 ; X=$$? ; if [ $$X -eq 0 ] ; then exit 1 ; else exit 0 ; fi
- $(CFG_QUIET)grep --text --quiet "`awk -F: '/error-pattern/ { print $$2 }' $(basename $(basename $@)).rs | tr -d '\n\r'`" $@
+ $(CFG_QUIET)$(call CFG_RUN_TARG, $<) >$@ 2>&1 ; X=$$? ; \
+ if [ $$X -eq 0 ] ; then exit 1 ; else exit 0 ; fi
+ $(CFG_QUIET)grep --text --quiet \
+ "`awk -F: '/error-pattern/ { print $$2 }' \
+ $(basename $(basename $@)).rs | tr -d '\n\r'`" $@
-test/compile-fail/%.x86.out: test/compile-fail/%.rs $(CFG_BOOT) $(CFG_RUNTIME)
+test/compile-fail/%.x86.out: test/compile-fail/%.rs $(REQ)
@$(call CFG_ECHO, compile [x86]: $<)
$(CFG_QUIET)rm -f $@
- $(CFG_QUIET)OCAMLRUNPARAM="b1" $(CFG_BOOT) -o $(@:.out=$(CFG_EXE_SUFFIX)) $< >$@ 2>&1 || true
- $(CFG_QUIET)grep --text --quiet "`awk -F: '/error-pattern/ { print $$2 }' $< | tr -d '\n\r'`" $@
+ $(BOOT) -o $(@:.out=$(CFG_EXE_SUFFIX)) $< >$@ 2>&1 || true
+ $(CFG_QUIET)grep --text --quiet \
+ "`awk -F: '/error-pattern/ { print $$2 }' $< | tr -d '\n\r'`" $@
-test/compile-fail/%.llvm.out: test/compile-fail/%.rs $(CFG_BOOT) $(CFG_RUNTIME)
+test/compile-fail/%.llvm.out: test/compile-fail/%.rs $(REQ)
@$(call CFG_ECHO, compile [llvm]: $<)
$(CFG_QUIET)rm -f $@
- $(CFG_QUIET)OCAMLRUNPARAM="b1" $(CFG_BOOT) $(CFG_BOOT_FLAGS) -o $(@:.out=$(CFG_EXE_SUFFIX)) $< >$@ 2>&1 || true
- $(CFG_QUIET)grep --text --quiet "`awk -F: '/error-pattern/ { print $$2 }' $< | tr -d '\n\r'`" $@
+ $(BOOT) -o $(@:.out=$(CFG_EXE_SUFFIX)) $< >$@ 2>&1 || true
+ $(CFG_QUIET)grep --text --quiet \
+ "`awk -F: '/error-pattern/ { print $$2 }' $< | tr -d '\n\r'`" $@
-test/run-pass/%.x86$(CFG_EXE_SUFFIX): test/run-pass/%.rc $(CFG_BOOT) $(CFG_RUNTIME) $(CFG_STDLIB)
+test/run-pass/%.x86$(CFG_EXE_SUFFIX): test/run-pass/%.rc $(REQ)
@$(call CFG_ECHO, compile [x86]: $<)
- $(CFG_QUIET)OCAMLRUNPARAM="b1" $(CFG_BOOT) $(CFG_BOOT_FLAGS) -o $@ $<
- $(CFG_QUIET)chmod 0755 $@
+ $(BOOT) -o $@ $<
%.s: %.bc
@$(call CFG_ECHO, compile [llvm]: $<)
- $(CFG_QUIET)llc $(CFG_LLC_COMPILE_FLAGS) -o $@ $<
+ $(CFG_QUIET)llc $(CFG_LLC_CFLAGS) -o $@ $<
%.llvm$(CFG_EXE_SUFFIX): %.s $(CFG_RUNTIME)
@$(call CFG_ECHO, compile [llvm]: $<)
- $(CFG_QUIET)gcc $(CFG_GCC_COMPILE_FLAGS) -o $@ $< -L. -lrustrt
+ $(CFG_QUIET)gcc $(CFG_GCC_CFLAGS) -o $@ $< -L. -lrustrt
-test/run-pass/%.bc: test/run-pass/%.rc $(CFG_BOOT) $(CFG_STDLIB)
+test/run-pass/%.bc: test/run-pass/%.rc $(REQ)
@$(call CFG_ECHO, compile [llvm]: $<)
- $(CFG_QUIET)OCAMLRUNPARAM="b1" $(CFG_BOOT) $(CFG_BOOT_FLAGS) -o $@ -llvm $<
+ $(BOOT) -o $@ -llvm $<
-test/run-pass/%.x86$(CFG_EXE_SUFFIX): test/run-pass/%.rs $(CFG_BOOT) $(CFG_RUNTIME) $(CFG_STDLIB)
+test/run-pass/%.x86$(CFG_EXE_SUFFIX): test/run-pass/%.rs $(REQ)
@$(call CFG_ECHO, compile [x86]: $<)
- $(CFG_QUIET)OCAMLRUNPARAM="b1" $(CFG_BOOT) $(CFG_BOOT_FLAGS) -o $@ $<
- $(CFG_QUIET)chmod 0755 $@
+ $(BOOT) -o $@ $<
-test/run-pass/%.bc: test/run-pass/%.rs $(CFG_BOOT) $(CFG_STDLIB)
+test/run-pass/%.bc: test/run-pass/%.rs $(REQ)
@$(call CFG_ECHO, compile [llvm]: $<)
- $(CFG_QUIET)OCAMLRUNPARAM="b1" $(CFG_BOOT) $(CFG_BOOT_FLAGS) -o $@ -llvm $<
+ $(BOOT) -o $@ -llvm $<
-test/run-fail/%.x86$(CFG_EXE_SUFFIX): test/run-fail/%.rs $(CFG_BOOT) $(CFG_RUNTIME) $(CFG_STDLIB)
+test/run-fail/%.x86$(CFG_EXE_SUFFIX): test/run-fail/%.rs $(REQ)
@$(call CFG_ECHO, compile [x86]: $<)
- $(CFG_QUIET)OCAMLRUNPARAM="b1" $(CFG_BOOT) $(CFG_BOOT_FLAGS) -o $@ $<
- $(CFG_QUIET)chmod 0755 $@
+ $(BOOT) -o $@ $<
-test/run-fail/%.bc: test/run-fail/%.rs $(CFG_BOOT) $(CFG_STDLIB)
+test/run-fail/%.bc: test/run-fail/%.rs $(REQ)
@$(call CFG_ECHO, compile [llvm]: $<)
- $(CFG_QUIET)OCAMLRUNPARAM="b1" $(CFG_BOOT) $(CFG_BOOT_FLAGS) -o $@ -llvm $<
+ $(BOOT) -o $@ -llvm $<
######################################################################
@@ -570,7 +617,9 @@ C_DEPFILES := $(RUNTIME_CS:%.cpp=%.d)
%.d: %.cpp $(MKFILES)
@$(call CFG_ECHO, dep: $<)
- $(CFG_QUIET)$(call CFG_DEPEND_C, $@ $(patsubst %.cpp, %$(CFG_OBJ_SUFFIX), $<), $(RUNTIME_INCS)) $< $(CFG_PATH_MUNGE) >$@
+ $(CFG_QUIET)$(call CFG_DEPEND_C, $@ \
+ $(patsubst %.cpp, %$(CFG_OBJ_SUFFIX), $<), \
+ $(RUNTIME_INCS)) $< $(CFG_PATH_MUNGE) >$@
%.d: %.ml $(MKFILES)
@$(call CFG_ECHO, dep: $<)
@@ -593,15 +642,15 @@ CRATE_DEPFILES := $(ALL_TEST_CRATES:%.rc=%.d) $(STDLIB_DEPFILE)
$(STDLIB_DEPFILE): $(STDLIB_CRATE) $(MKFILES) $(CFG_BOOT)
@$(call CFG_ECHO, dep: $<)
- $(CFG_QUIET)$(CFG_BOOT) $(CFG_BOOT_FLAGS) -shared -rdeps $< $(CFG_PATH_MUNGE) >$@
+ $(BOOT) -shared -rdeps $< $(CFG_PATH_MUNGE) >$@
%.d: %.rc $(MKFILES) $(CFG_BOOT)
@$(call CFG_ECHO, dep: $<)
- $(CFG_QUIET)$(CFG_BOOT) $(CFG_BOOT_FLAGS) -rdeps $< $(CFG_PATH_MUNGE) >$@
+ $(BOOT) -rdeps $< $(CFG_PATH_MUNGE) >$@
%.d: %.rs $(MKFILES) $(CFG_BOOT)
@$(call CFG_ECHO, dep: $<)
- $(CFG_QUIET)$(CFG_BOOT) $(CFG_BOOT_FLAGS) -rdeps $< $(CFG_PATH_MUNGE) >$@
+ $(BOOT) -rdeps $< $(CFG_PATH_MUNGE) >$@
ifneq ($(MAKECMDGOALS),clean)
-include $(CRATE_DEPFILES)
@@ -622,8 +671,9 @@ PKG_3RDPARTY := rt/valgrind.h rt/memcheck.h \
rt/bigint/bigint.h rt/bigint/bigint_int.cpp \
rt/bigint/bigint_ext.cpp rt/bigint/low_primes.h
PKG_FILES := README \
+ $(wildcard etc/*.*) \
$(MKFILES) $(BOOT_MLS) boot/fe/lexer.mll \
- $(COMPILER_CRATE) $(COMPILER_INPUTS) \
+ $(COMPILER_INPUTS) \
$(STDLIB_CRATE) $(STDLIB_INPUTS) \
$(RUNTIME_CS) $(RUNTIME_HDR) $(PKG_3RDPARTY) \
$(ALL_TEST_INPUTS)
@@ -658,20 +708,29 @@ distcheck:
# Cleanup
######################################################################
-.PHONY: clean
+.PHONY: clean tidy
+
+tidy:
+ @$(call CFG_ECHO, check: formatting)
+ $(CFG_QUIET) python etc/tidy.py \
+ $(wildcard ../*.txt) \
+ ../README \
+ $(filter-out boot/fe/lexer.ml $(PKG_3RDPARTY), $(PKG_FILES))
clean:
@$(call CFG_ECHO, cleaning)
- $(CFG_QUIET)rm -f $(RUNTIME_OBJS) $(BOOT_CMOS) $(BOOT_CMIS) $(BOOT_CMXS) $(BOOT_OBJS)
+ $(CFG_QUIET)rm -f $(RUNTIME_OBJS)
+ $(CFG_QUIET)rm -f $(BOOT_CMOS) $(BOOT_CMIS) $(BOOT_CMXS) $(BOOT_OBJS)
$(CFG_QUIET)rm -f $(CFG_COMPILER)
$(CFG_QUIET)rm -f $(ML_DEPFILES) $(C_DEPFILES) $(CRATE_DEPFILES)
$(CFG_QUIET)rm -f boot/fe/lexer.ml
$(CFG_QUIET)rm -f $(CFG_BOOT) $(CFG_RUNTIME) $(CFG_STDLIB)
- $(CFG_QUIET)rm -f $(TEST_RUN_PASS_EXES_X86) $(TEST_RUN_PASS_OUTS_X86)
- $(CFG_QUIET)rm -f $(TEST_RUN_PASS_EXES_LLVM) $(TEST_RUN_PASS_OUTS_LLVM)
- $(CFG_QUIET)rm -f $(TEST_RUN_FAIL_EXES_X86) $(TEST_RUN_FAIL_OUTS_X86)
- $(CFG_QUIET)rm -f $(TEST_RUN_FAIL_EXES_LLVM) $(TEST_RUN_FAIL_OUTS_LLVM)
- $(CFG_QUIET)rm -f $(TEST_COMPILE_FAIL_EXES_X86) $(TEST_COMPILE_FAIL_OUTS_X86)
- $(CFG_QUIET)rm -f $(TEST_COMPILE_FAIL_EXES_LLVM) $(TEST_COMPILE_FAIL_OUTS_LLVM)
+ $(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 -Rf $(PKG_NAME)-*.tar.gz dist
- $(CFG_QUIET)rm -f $(foreach ext,cmx cmi cmo cma o a d exe,$(wildcard boot/*/*.$(ext) boot/*/*/*.$(ext)))
+ $(CFG_QUIET)rm -f $(foreach ext,cmx cmi cmo cma o a d exe,\
+ $(wildcard boot/*/*.$(ext) boot/*/*/*.$(ext)))
diff --git a/src/README b/src/README
new file mode 100644
index 00000000..c51709d0
--- /dev/null
+++ b/src/README
@@ -0,0 +1,28 @@
+This is preliminary version of the Rust compiler.
+
+Source layout:
+
+boot/ The bootstrap compiler
+boot/fe - Front end (lexer, parser, AST)
+boot/me - Middle end (resolve, check, layout, trans)
+boot/be - Back end (IL, RA, insns, asm, objfiles)
+boot/util - Ubiquitous helpers
+boot/llvm - LLVM-based alternative back end
+boot/driver - Compiler driver
+
+comp/ The self-hosted compiler (doesn't exist yet)
+comp/* - Same structure as in boot/
+
+rt/ The runtime system
+rt/rust_*.cpp - The majority of the runtime services
+rt/isaac - The PRNG used for pseudo-random choices in the runtime
+rt/bigint - The bigint library used for the 'big' type
+rt/uthash - Small hashtable-and-list library for C, used in runtime
+rt/{sync,util} - Small utility classes for the runtime.
+
+test/ Testsuite (for both bootstrap and self-hosted)
+test/compile-fail - Tests that should fail to compile
+test/run-fail - Tests that should compile, run and fail
+test/run-pass - Tests that should compile, run and succeed
+
+Please be gentle, it's a work in progress.
diff --git a/src/boot/be/abi.ml b/src/boot/be/abi.ml
new file mode 100644
index 00000000..fd9ca750
--- /dev/null
+++ b/src/boot/be/abi.ml
@@ -0,0 +1,207 @@
+
+(*
+ * The 'abi' structure is pretty much just a grab-bag of machine
+ * dependencies and structure-layout information. Part of the latter
+ * is shared with trans and semant.
+ *
+ * Make some attempt to factor it as time goes by.
+ *)
+
+(* Word offsets for structure fields in rust-internal.h, and elsewhere in
+ compiler. *)
+
+let rc_base_field_refcnt = 0;;
+
+let task_field_refcnt = rc_base_field_refcnt;;
+let task_field_stk = task_field_refcnt + 1;;
+let task_field_runtime_sp = task_field_stk + 1;;
+let task_field_rust_sp = task_field_runtime_sp + 1;;
+let task_field_gc_alloc_chain = task_field_rust_sp + 1;;
+let task_field_dom = task_field_gc_alloc_chain + 1;;
+let n_visible_task_fields = task_field_dom + 1;;
+
+let dom_field_interrupt_flag = 0;;
+
+let frame_glue_fns_field_mark = 0;;
+let frame_glue_fns_field_drop = 1;;
+let frame_glue_fns_field_reloc = 2;;
+
+let exterior_rc_slot_field_refcnt = 0;;
+let exterior_rc_slot_field_body = 1;;
+
+let exterior_gc_slot_field_next = (-2);;
+let exterior_gc_slot_field_ctrl = (-1);;
+let exterior_gc_slot_field_refcnt = 0;;
+let exterior_gc_slot_field_body = 1;;
+
+let exterior_rc_header_size = 1;;
+let exterior_gc_header_size = 3;;
+
+let exterior_gc_malloc_return_adjustment = 2;;
+
+let stk_field_valgrind_id = 0 + 1;;
+let stk_field_limit = stk_field_valgrind_id + 1;;
+let stk_field_data = stk_field_limit + 1;;
+
+let binding_size = 2;;
+let binding_field_item = 0;;
+let binding_field_binding = 1;;
+
+let general_code_alignment = 16;;
+
+let tydesc_field_first_param = 0;;
+let tydesc_field_size = 1;;
+let tydesc_field_align = 2;;
+let tydesc_field_copy_glue = 3;;
+let tydesc_field_drop_glue = 4;;
+let tydesc_field_free_glue = 5;;
+let tydesc_field_mark_glue = 6;;
+let tydesc_field_obj_drop_glue = 7;;
+
+let vec_elt_rc = 0;;
+let vec_elt_alloc = 1;;
+let vec_elt_fill = 2;;
+let vec_elt_data = 3;;
+
+let calltup_elt_out_ptr = 0;;
+let calltup_elt_task_ptr = 1;;
+let calltup_elt_ty_params = 2;;
+let calltup_elt_args = 3;;
+let calltup_elt_iterator_args = 4;;
+let calltup_elt_indirect_args = 5;;
+
+let iterator_args_elt_block_fn = 0;;
+let iterator_args_elt_outer_frame_ptr = 1;;
+
+let indirect_args_elt_closure = 0;;
+
+(* ty_params, src, dst, tydesc, taskptr. *)
+let worst_case_glue_call_args = 5;;
+
+type abi =
+ {
+ abi_word_sz: int64;
+ abi_word_bits: Il.bits;
+ abi_word_ty: Common.ty_mach;
+
+ abi_is_2addr_machine: bool;
+ abi_has_pcrel_data: bool;
+ abi_has_pcrel_code: bool;
+
+ abi_n_hardregs: int;
+ abi_str_of_hardreg: (int -> string);
+
+ abi_prealloc_quad: (Il.quad' -> Il.quad');
+ abi_constrain_vregs: (Il.quad -> Bits.t array -> unit);
+
+ abi_emit_fn_prologue: (Il.emitter
+ -> Common.size (* framesz *)
+ -> Common.size (* callsz *)
+ -> Common.nabi
+ -> Common.fixup (* grow_task *)
+ -> unit);
+
+ abi_emit_fn_epilogue: (Il.emitter -> unit);
+
+ abi_emit_fn_tail_call: (Il.emitter
+ -> int64 (* caller_callsz *)
+ -> int64 (* caller_argsz *)
+ -> Il.code (* callee_code *)
+ -> int64 (* callee_argsz *)
+ -> unit);
+
+ abi_clobbers: (Il.quad -> Il.hreg list);
+
+ abi_emit_native_call: (Il.emitter
+ -> Il.cell (* ret *)
+ -> Common.nabi
+ -> Common.fixup (* callee *)
+ -> Il.operand array (* args *)
+ -> unit);
+
+ abi_emit_native_void_call: (Il.emitter
+ -> Common.nabi
+ -> Common.fixup (* callee *)
+ -> Il.operand array (* args *)
+ -> unit);
+
+ abi_emit_native_call_in_thunk: (Il.emitter
+ -> Il.cell (* ret *)
+ -> Common.nabi
+ -> Il.operand (* callee *)
+ -> Il.operand array (* args *)
+ -> unit);
+ abi_emit_inline_memcpy: (Il.emitter
+ -> int64 (* n_bytes *)
+ -> Il.reg (* dst_ptr *)
+ -> Il.reg (* src_ptr *)
+ -> Il.reg (* tmp_reg *)
+ -> bool (* ascending *)
+ -> unit);
+
+ (* Global glue. *)
+ abi_activate: (Il.emitter -> unit);
+ abi_yield: (Il.emitter -> unit);
+ abi_unwind: (Il.emitter -> Common.nabi -> Common.fixup -> unit);
+ abi_get_next_pc_thunk:
+ ((Il.reg (* output *)
+ * Common.fixup (* thunk in objfile *)
+ * (Il.emitter -> unit)) (* fn to make thunk *)
+ option);
+
+ abi_sp_reg: Il.reg;
+ abi_fp_reg: Il.reg;
+ abi_dwarf_fp_reg: int;
+ abi_tp_cell: Il.cell;
+ abi_implicit_args_sz: int64;
+ abi_frame_base_sz: int64;
+ abi_frame_info_sz: int64;
+ abi_spill_slot: (Il.spill -> Il.mem);
+ }
+;;
+
+let load_fixup_addr
+ (e:Il.emitter)
+ (out_reg:Il.reg)
+ (fix:Common.fixup)
+ (rty:Il.referent_ty)
+ : unit =
+
+ let cell = Il.Reg (out_reg, Il.AddrTy rty) in
+ let op = Il.ImmPtr (fix, rty) in
+ Il.emit e (Il.lea cell op);
+;;
+
+let load_fixup_codeptr
+ (e:Il.emitter)
+ (out_reg:Il.reg)
+ (fixup:Common.fixup)
+ (has_pcrel_code:bool)
+ (indirect:bool)
+ : Il.code =
+ if indirect
+ then
+ begin
+ load_fixup_addr e out_reg fixup (Il.ScalarTy (Il.AddrTy Il.CodeTy));
+ Il.CodePtr (Il.Cell (Il.Mem (Il.RegIn (out_reg, None),
+ Il.ScalarTy (Il.AddrTy Il.CodeTy))))
+ end
+ else
+ if has_pcrel_code
+ then (Il.CodePtr (Il.ImmPtr (fixup, Il.CodeTy)))
+ else
+ begin
+ load_fixup_addr e out_reg fixup Il.CodeTy;
+ Il.CodePtr (Il.Cell (Il.Reg (out_reg, Il.AddrTy Il.CodeTy)))
+ end
+;;
+
+
+(*
+ * 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/be/asm.ml b/src/boot/be/asm.ml
new file mode 100644
index 00000000..10b2142a
--- /dev/null
+++ b/src/boot/be/asm.ml
@@ -0,0 +1,755 @@
+(*
+
+ Our assembler is an all-at-once, buffer-in-memory job, very simple
+ minded. I have 1gb of memory on my laptop: I don't expect to ever
+ emit a program that large with this code.
+
+ It is based on the 'frag' type, which has a variant for every major
+ type of machine-blob we know how to write (bytes, zstrings, BSS
+ blocks, words of various sorts).
+
+ A frag can contain symbolic references between the sub-parts of
+ it. These are accomplished through ref cells we call fixups, and a
+ 2-pass (resolution and writing) process defined recursively over
+ the frag structure.
+
+ Fixups are defined by wrapping a frag in a DEF pseudo-frag with
+ a fixup attached. This will record information about the wrapped
+ frag -- positions and sizes -- in the fixup during resolution.
+
+ We say "positions" and "sizes" there, in plural, because both a
+ file number and a memory number is recorded for each concept.
+
+ File numbers refer to positions and sizes in the file we're
+ generating, and are based on the native int type for the host
+ platform -- usually 31 or 62 bits -- whereas the expressions that
+ *use* position fixups tend to promote them up to 32 or 64 bits
+ somehow. On a 32 bit platform, you can't generate output buffers
+ with 64-bit positions (ocaml limitation!)
+
+ Memory numbers are 64 bit, always, and refer to sizes and positions
+ of frags when they are loaded into memory in the target. When
+ you're generating code for a 32-bit target, or using a memory
+ number in a context that's less than 64 bits, the value is
+ range-checked and truncated. But in all other respects, we imagine
+ a 32-bit address space is just the prefix of the continuing 64-bit
+ address space. If you need to pin an object at a particular place
+ from the point 2^32-1, say, you will need to do arithmetic and use
+ the MEMPOS pseudo-frag, that sets the current memory position as
+ it's being processed.
+
+ Fixups can be *used* anywhere else in the frag tree, as many times
+ as you like. If you try to write an unresolved fixup, the emitter
+ faults. When you specify the use of a fixup, you need to specify
+ whether you want to use its file size, file position, memory size,
+ or memory position.
+
+ Positions, addresses, sizes and such, of course, are in bytes.
+
+ Expressions are evaluated to an int64 (signed), even if the
+ expression is an int32 or less. Depending on how you use the result
+ of the expression, a range check error may fire (for example, if
+ the expression evaluates to -2^24 and you're emitting a word16).
+
+ Word endianness is per-file. At the moment this seems acceptable.
+
+ Because we want to be *very specific* about the time and place
+ arithmetic promotions occur, we define two separate expression-tree
+ types (with the same polymorphic constructors) and two separate
+ evaluation functions, with an explicit operator for marking the
+ promotion-points.
+
+*)
+
+open Common;;
+
+
+let log (sess:Session.sess) =
+ Session.log "asm"
+ sess.Session.sess_log_asm
+ sess.Session.sess_log_out
+;;
+
+let iflog (sess:Session.sess) (thunk:(unit -> unit)) : unit =
+ if sess.Session.sess_log_asm
+ then thunk ()
+ else ()
+;;
+
+exception Bad_fit of string;;
+exception Undef_sym of string;;
+
+type ('a, 'b) expr =
+ IMM of 'a
+ | ADD of (('a, 'b) expr) * (('a, 'b) expr)
+ | SUB of (('a, 'b) expr) * (('a, 'b) expr)
+ | MUL of (('a, 'b) expr) * (('a, 'b) expr)
+ | DIV of (('a, 'b) expr) * (('a, 'b) expr)
+ | REM of (('a, 'b) expr) * (('a, 'b) expr)
+ | MAX of (('a, 'b) expr) * (('a, 'b) expr)
+ | ALIGN of (('a, 'b) expr) * (('a, 'b) expr)
+ | SLL of (('a, 'b) expr) * int
+ | SLR of (('a, 'b) expr) * int
+ | SAR of (('a, 'b) expr) * int
+ | AND of (('a, 'b) expr) * (('a, 'b) expr)
+ | XOR of (('a, 'b) expr) * (('a, 'b) expr)
+ | OR of (('a, 'b) expr) * (('a, 'b) expr)
+ | NOT of (('a, 'b) expr)
+ | NEG of (('a, 'b) expr)
+ | F_POS of fixup
+ | F_SZ of fixup
+ | M_POS of fixup
+ | M_SZ of fixup
+ | EXT of 'b
+
+type expr32 = (int32, int) expr
+;;
+
+type expr64 = (int64, expr32) expr
+;;
+
+
+let rec eval32 (e:expr32)
+ : int32 =
+ let chop64 kind name v =
+ let x = Int64.to_int32 v in
+ if (Int64.compare v (Int64.of_int32 x)) = 0 then
+ x
+ else raise (Bad_fit (kind
+ ^ " fixup "
+ ^ name
+ ^ " overflowed 32 bits in eval32: "
+ ^ Int64.to_string v))
+ in
+ let expandInt _ _ v = Int32.of_int v in
+ let checkdef kind name v inj =
+ match v with
+ None ->
+ raise (Undef_sym (kind ^ " fixup " ^ name
+ ^ " undefined in eval32"))
+ | Some x -> inj kind name x
+ in
+ match e with
+ IMM i -> i
+ | ADD (a, b) -> Int32.add (eval32 a) (eval32 b)
+ | SUB (a, b) -> Int32.sub (eval32 a) (eval32 b)
+ | MUL (a, b) -> Int32.mul (eval32 a) (eval32 b)
+ | DIV (a, b) -> Int32.div (eval32 a) (eval32 b)
+ | REM (a, b) -> Int32.rem (eval32 a) (eval32 b)
+ | MAX (a, b) -> i32_max (eval32 a) (eval32 b)
+ | ALIGN (a, b) -> i32_align (eval32 a) (eval32 b)
+ | SLL (a, b) -> Int32.shift_left (eval32 a) b
+ | SLR (a, b) -> Int32.shift_right_logical (eval32 a) b
+ | SAR (a, b) -> Int32.shift_right (eval32 a) b
+ | AND (a, b) -> Int32.logand (eval32 a) (eval32 b)
+ | XOR (a, b) -> Int32.logxor (eval32 a) (eval32 b)
+ | OR (a, b) -> Int32.logor (eval32 a) (eval32 b)
+ | NOT a -> Int32.lognot (eval32 a)
+ | NEG a -> Int32.neg (eval32 a)
+ | F_POS f ->
+ checkdef "file position"
+ f.fixup_name f.fixup_file_pos expandInt
+ | F_SZ f ->
+ checkdef "file size"
+ f.fixup_name f.fixup_file_sz expandInt
+ | M_POS f ->
+ checkdef "mem position"
+ f.fixup_name f.fixup_mem_pos chop64
+ | M_SZ f ->
+ checkdef "mem size" f.fixup_name f.fixup_mem_sz chop64
+ | EXT i -> Int32.of_int i
+;;
+
+let rec eval64 (e:expr64)
+ : int64 =
+ let checkdef kind name v inj =
+ match v with
+ None ->
+ raise (Undef_sym (kind ^ " fixup '"
+ ^ name ^ "' undefined in eval64"))
+ | Some x -> inj x
+ in
+ match e with
+ IMM i -> i
+ | ADD (a, b) -> Int64.add (eval64 a) (eval64 b)
+ | SUB (a, b) -> Int64.sub (eval64 a) (eval64 b)
+ | MUL (a, b) -> Int64.mul (eval64 a) (eval64 b)
+ | DIV (a, b) -> Int64.div (eval64 a) (eval64 b)
+ | REM (a, b) -> Int64.rem (eval64 a) (eval64 b)
+ | MAX (a, b) -> i64_max (eval64 a) (eval64 b)
+ | ALIGN (a, b) -> i64_align (eval64 a) (eval64 b)
+ | SLL (a, b) -> Int64.shift_left (eval64 a) b
+ | SLR (a, b) -> Int64.shift_right_logical (eval64 a) b
+ | SAR (a, b) -> Int64.shift_right (eval64 a) b
+ | AND (a, b) -> Int64.logand (eval64 a) (eval64 b)
+ | XOR (a, b) -> Int64.logxor (eval64 a) (eval64 b)
+ | OR (a, b) -> Int64.logor (eval64 a) (eval64 b)
+ | NOT a -> Int64.lognot (eval64 a)
+ | NEG a -> Int64.neg (eval64 a)
+ | F_POS f ->
+ checkdef "file position"
+ f.fixup_name f.fixup_file_pos Int64.of_int
+ | F_SZ f ->
+ checkdef "file size"
+ f.fixup_name f.fixup_file_sz Int64.of_int
+ | M_POS f ->
+ checkdef "mem position"
+ f.fixup_name f.fixup_mem_pos (fun x -> x)
+ | M_SZ f ->
+ checkdef "mem size"
+ f.fixup_name f.fixup_mem_sz (fun x -> x)
+ | EXT e -> Int64.of_int32 (eval32 e)
+;;
+
+
+type frag =
+ MARK (* MARK == 'PAD (IMM 0L)' *)
+ | SEQ of frag array
+ | PAD of int
+ | BSS of int64
+ | MEMPOS of int64
+ | BYTE of int
+ | BYTES of int array
+ | CHAR of char
+ | STRING of string
+ | ZSTRING of string
+ | ULEB128 of expr64
+ | SLEB128 of expr64
+ | WORD of (ty_mach * expr64)
+ | ALIGN_FILE of (int * frag)
+ | ALIGN_MEM of (int * frag)
+ | DEF of (fixup * frag)
+ | RELAX of relaxation
+
+and relaxation =
+ { relax_options: frag array;
+ relax_choice: int ref; }
+;;
+
+exception Relax_more of relaxation;;
+
+let new_relaxation (frags:frag array) =
+ RELAX { relax_options = frags;
+ relax_choice = ref ((Array.length frags) - 1); }
+;;
+
+
+let rec write_frag
+ ~(sess:Session.sess)
+ ~(lsb0:bool)
+ ~(buf:Buffer.t)
+ ~(frag:frag)
+ : unit =
+ let relax = Queue.create () in
+ let bump_relax r =
+ iflog sess (fun _ ->
+ log sess "bumping relaxation to position %d"
+ ((!(r.relax_choice)) - 1));
+ r.relax_choice := (!(r.relax_choice)) - 1;
+ if !(r.relax_choice) < 0
+ then bug () "relaxation ran out of options"
+ in
+ let rec loop _ =
+ Queue.clear relax;
+ Buffer.clear buf;
+ resolve_frag_full relax frag;
+ lower_frag ~sess ~lsb0 ~buf ~relax ~frag;
+ if Queue.is_empty relax
+ then ()
+ else
+ begin
+ iflog sess (fun _ -> log sess "relaxing");
+ Queue.iter bump_relax relax;
+ loop ()
+ end
+ in
+ loop ()
+
+
+and resolve_frag_full (relax:relaxation Queue.t) (frag:frag)
+ : unit =
+ let file_pos = ref 0 in
+ let mem_pos = ref 0L in
+ let bump i =
+ mem_pos := Int64.add (!mem_pos) (Int64.of_int i);
+ file_pos := (!file_pos) + i
+ in
+
+ let uleb (e:expr64) : unit =
+ let rec loop value =
+ let value = Int64.shift_right_logical value 7 in
+ if value = 0L
+ then bump 1
+ else
+ begin
+ bump 1;
+ loop value
+ end
+ in
+ loop (eval64 e)
+ in
+
+ let sleb (e:expr64) : unit =
+ let rec loop value =
+ let byte = Int64.logand value 0xf7L in
+ let value = Int64.shift_right value 7 in
+ let signbit = Int64.logand byte 0x40L in
+ if (((value = 0L) && (signbit = 0L)) ||
+ ((value = -1L) && (signbit = 0x40L)))
+ then bump 1
+ else
+ begin
+ bump 1;
+ loop value
+ end
+ in
+ loop (eval64 e)
+ in
+ let rec resolve_frag it =
+ match it with
+ | MARK -> ()
+ | SEQ frags -> Array.iter resolve_frag frags
+ | PAD i -> bump i
+ | BSS i -> mem_pos := Int64.add (!mem_pos) i
+ | MEMPOS i -> mem_pos := i
+ | BYTE _ -> bump 1
+ | BYTES ia -> bump (Array.length ia)
+ | CHAR _ -> bump 1
+ | STRING s -> bump (String.length s)
+ | ZSTRING s -> bump ((String.length s) + 1)
+ | ULEB128 e -> uleb e
+ | SLEB128 e -> sleb e
+ | WORD (mach,_) -> bump (bytes_of_ty_mach mach)
+ | ALIGN_FILE (n, frag) ->
+ let spill = (!file_pos) mod n in
+ let pad = (n - spill) mod n in
+ file_pos := (!file_pos) + pad;
+ (*
+ * NB: aligning the file *causes* likewise alignment of
+ * memory, since we implement "file alignment" by
+ * padding!
+ *)
+ mem_pos := Int64.add (!mem_pos) (Int64.of_int pad);
+ resolve_frag frag
+
+ | ALIGN_MEM (n, frag) ->
+ let n64 = Int64.of_int n in
+ let spill = Int64.rem (!mem_pos) n64 in
+ let pad = Int64.rem (Int64.sub n64 spill) n64 in
+ mem_pos := Int64.add (!mem_pos) pad;
+ resolve_frag frag
+
+ | DEF (f, i) ->
+ let fpos1 = !file_pos in
+ let mpos1 = !mem_pos in
+ resolve_frag i;
+ f.fixup_file_pos <- Some fpos1;
+ f.fixup_mem_pos <- Some mpos1;
+ f.fixup_file_sz <- Some ((!file_pos) - fpos1);
+ f.fixup_mem_sz <- Some (Int64.sub (!mem_pos) mpos1)
+
+ | RELAX rel ->
+ begin
+ try
+ resolve_frag rel.relax_options.(!(rel.relax_choice))
+ with
+ Bad_fit _ -> Queue.add rel relax
+ end
+ in
+ resolve_frag frag
+
+and lower_frag
+ ~(sess:Session.sess)
+ ~(lsb0:bool)
+ ~(buf:Buffer.t)
+ ~(relax:relaxation Queue.t)
+ ~(frag:frag)
+ : unit =
+ let byte (i:int) =
+ if i < 0
+ then raise (Bad_fit "byte underflow")
+ else
+ if i > 255
+ then raise (Bad_fit "byte overflow")
+ else Buffer.add_char buf (Char.chr i)
+ in
+
+ let uleb (e:expr64) : unit =
+ let emit1 k = Buffer.add_char buf (Char.chr (Int64.to_int k)) in
+ let rec loop value =
+ let byte = Int64.logand value 0x7fL in
+ let value = Int64.shift_right_logical value 7 in
+ if value = 0L
+ then emit1 byte
+ else
+ begin
+ emit1 (Int64.logor byte 0x80L);
+ loop value
+ end
+ in
+ loop (eval64 e)
+ in
+
+ let sleb (e:expr64) : unit =
+ let emit1 k = Buffer.add_char buf (Char.chr (Int64.to_int k)) in
+ let rec loop value =
+ let byte = Int64.logand value 0x7fL in
+ let value = Int64.shift_right value 7 in
+ let signbit = Int64.logand byte 0x40L in
+ if (((value = 0L) && (signbit = 0L)) ||
+ ((value = -1L) && (signbit = 0x40L)))
+ then emit1 byte
+ else
+ begin
+ emit1 (Int64.logor byte 0x80L);
+ loop value
+ end
+ in
+ loop (eval64 e)
+ in
+
+ let word (nbytes:int) (signed:bool) (e:expr64) =
+ let i = eval64 e in
+
+ (*
+ FIXME:
+
+ We should really base the entire assembler and memory-position
+ system on Big_int.big_int, but in ocaml the big_int type lacks,
+ oh, just about every useful function (no format string spec, no
+ bitwise ops, blah blah) so it's useless; we're stuck on int64
+ for bootstrapping.
+
+ For the time being we're just going to require you to represent
+ those few unsigned 64 bit terms you have in mind via their
+ signed bit pattern. Suboptimal but it's the best we can do.
+ *)
+
+ let (top,bot) =
+ if nbytes >= 8
+ then
+ if signed
+ then (Int64.max_int,Int64.min_int)
+ else (Int64.max_int,0L)
+ else
+ if signed
+ then
+ let bound = (Int64.shift_left 1L ((8 * nbytes) - 1)) in
+ (Int64.sub bound 1L, Int64.neg bound)
+ else
+ let bound = (Int64.shift_left 1L (8 * nbytes)) in
+ (Int64.sub bound 1L, 0L)
+ in
+
+ let mask1 = Int64.logand 0xffL in
+ let shift = Int64.shift_right_logical in
+ let emit1 k = Buffer.add_char buf (Char.chr (Int64.to_int k)) in
+ if Int64.compare i bot = (-1)
+ then raise (Bad_fit ("word underflow: "
+ ^ (Int64.to_string i)
+ ^ " into "
+ ^ (string_of_int nbytes)
+ ^ (if signed then " signed" else " unsigned")
+ ^ " bytes"))
+ else
+ if Int64.compare i top = 1
+ then raise (Bad_fit ("word overflow: "
+ ^ (Int64.to_string i)
+ ^ " into "
+ ^ (string_of_int nbytes)
+ ^ (if signed then " signed" else " unsigned")
+ ^ " bytes"))
+ else
+ if lsb0
+ then
+ for n = 0 to (nbytes - 1) do
+ emit1 (mask1 (shift i (8*n)))
+ done
+ else
+ for n = (nbytes - 1) downto 0 do
+ emit1 (mask1 (shift i (8*n)))
+ done
+ in
+ match frag with
+ MARK -> ()
+
+ | SEQ frags ->
+ Array.iter
+ begin
+ fun frag ->
+ lower_frag ~sess ~lsb0 ~buf ~relax ~frag
+ end frags
+
+ | PAD c ->
+ for i = 1 to c do
+ Buffer.add_char buf '\x00'
+ done
+
+ | BSS _ -> ()
+
+ | MEMPOS _ -> ()
+
+ | BYTE i -> byte i
+
+ | BYTES bs ->
+ iflog sess (fun _ -> log sess "lowering %d bytes"
+ (Array.length bs));
+ Array.iter byte bs
+
+ | CHAR c ->
+ iflog sess (fun _ -> log sess "lowering char: %c" c);
+ Buffer.add_char buf c
+
+ | STRING s ->
+ iflog sess (fun _ -> log sess "lowering string: %s" s);
+ Buffer.add_string buf s
+
+ | ZSTRING s ->
+ iflog sess (fun _ -> log sess "lowering zstring: %s" s);
+ Buffer.add_string buf s;
+ byte 0
+
+ | ULEB128 e -> uleb e
+ | SLEB128 e -> sleb e
+
+ | WORD (m,e) ->
+ iflog sess
+ (fun _ ->
+ log sess "lowering word %s"
+ (string_of_ty_mach m));
+ word (bytes_of_ty_mach m) (mach_is_signed m) e
+
+ | ALIGN_FILE (n, frag) ->
+ let spill = (Buffer.length buf) mod n in
+ let pad = (n - spill) mod n in
+ for i = 1 to pad do
+ Buffer.add_char buf '\x00'
+ done;
+ lower_frag sess lsb0 buf relax frag
+
+ | ALIGN_MEM (_, i) -> lower_frag sess lsb0 buf relax i
+ | DEF (f, i) ->
+ iflog sess (fun _ -> log sess "lowering fixup: %s" f.fixup_name);
+ lower_frag sess lsb0 buf relax i;
+
+ | RELAX rel ->
+ begin
+ try
+ lower_frag sess lsb0 buf relax
+ rel.relax_options.(!(rel.relax_choice))
+ with
+ Bad_fit _ -> Queue.add rel relax
+ end
+;;
+
+let fold_flags (f:'a -> int64) (flags:'a list) : int64 =
+ List.fold_left (Int64.logor) 0x0L (List.map f flags)
+;;
+
+let write_out_frag sess lsb0 frag =
+ let buf = Buffer.create 0xffff in
+ let file = Session.filename_of sess.Session.sess_out in
+ let out = open_out_bin file in
+ write_frag ~sess ~lsb0 ~buf ~frag;
+ Buffer.output_buffer out buf;
+ flush out;
+ close_out out;
+ Unix.chmod file 0o755
+;;
+
+(* Asm-reader stuff for loading info back from mapped files. *)
+(*
+ * Unfortunately the ocaml Bigarray interface takes 'int' indices, so
+ * f.e. can't do 64-bit offsets / files when running on a 32bit platform.
+ * Despite the fact that we can possibly produce them. Sigh. Yet another
+ * "bootstrap compiler limitation".
+ *)
+type asm_reader =
+ {
+ asm_seek: int -> unit;
+ asm_get_u32: unit -> int;
+ asm_get_u16: unit -> int;
+ asm_get_u8: unit -> int;
+ asm_get_uleb: unit -> int;
+ asm_get_zstr: unit -> string;
+ asm_get_zstr_padded: int -> string;
+ asm_get_off: unit -> int;
+ asm_adv: int -> unit;
+ asm_adv_u32: unit -> unit;
+ asm_adv_u16: unit -> unit;
+ asm_adv_u8: unit -> unit;
+ asm_adv_zstr: unit -> unit;
+ asm_close: unit -> unit;
+ }
+;;
+
+type mmap_arr =
+ (int, Bigarray.int8_unsigned_elt, Bigarray.c_layout)
+ Bigarray.Array1.t
+;;
+
+let new_asm_reader (sess:Session.sess) (s:filename) : asm_reader =
+ iflog sess (fun _ -> log sess "opening file %s" s);
+ let fd = Unix.openfile s [ Unix.O_RDONLY ] 0 in
+ let arr = (Bigarray.Array1.map_file
+ fd ~pos:0L
+ Bigarray.int8_unsigned
+ Bigarray.c_layout
+ false (-1))
+ in
+ let tmp = ref Nativeint.zero in
+ let buf = Buffer.create 16 in
+ let off = ref 0 in
+ let is_open = ref true in
+ let get_word_as_int (nbytes:int) : int =
+ assert (!is_open);
+ let lsb0 = true in
+ tmp := Nativeint.zero;
+ if lsb0
+ then
+ for j = nbytes-1 downto 0 do
+ tmp := Nativeint.shift_left (!tmp) 8;
+ tmp := Nativeint.logor (!tmp) (Nativeint.of_int arr.{(!off) + j})
+ done
+ else
+ for j = 0 to nbytes-1 do
+ tmp := Nativeint.shift_left (!tmp) 8;
+ tmp := Nativeint.logor (!tmp) (Nativeint.of_int arr.{(!off) + j})
+ done;
+ off := (!off) + nbytes;
+ Nativeint.to_int (!tmp)
+ in
+ let get_zstr_padded pad_opt =
+ assert (!is_open);
+ let i = ref (!off) in
+ Buffer.clear buf;
+ let buflen_ok _ =
+ match pad_opt with
+ None -> true
+ | Some pad -> (Buffer.length buf) < pad
+ in
+ while arr.{!i} != 0 && (buflen_ok()) do
+ Buffer.add_char buf (Char.chr arr.{!i});
+ incr i
+ done;
+ begin
+ match pad_opt with
+ None -> off := (!off) + (Buffer.length buf) + 1
+ | Some pad ->
+ begin
+ assert ((Buffer.length buf) <= pad);
+ off := (!off) + pad
+ end
+ end;
+ Buffer.contents buf
+ in
+ let bump i =
+ assert (!is_open);
+ off := (!off) + i
+ in
+ {
+ asm_seek = (fun i -> off := i);
+ asm_get_u32 = (fun _ -> get_word_as_int 4);
+ asm_get_u16 = (fun _ -> get_word_as_int 2);
+ asm_get_u8 = (fun _ -> get_word_as_int 1);
+ asm_get_uleb =
+ begin
+ fun _ ->
+ let rec loop result shift =
+ let byte = arr.{!off} in
+ incr off;
+ let result = result lor ((byte land 0x7f) lsl shift) in
+ if (byte land 0x80) = 0
+ then result
+ else loop result (shift+7)
+ in
+ loop 0 0
+ end;
+ asm_get_zstr = (fun _ -> get_zstr_padded None);
+ asm_get_zstr_padded = (fun pad -> get_zstr_padded (Some pad));
+ asm_get_off = (fun _ -> !off);
+ asm_adv = bump;
+ asm_adv_u32 = (fun _ -> bump 4);
+ asm_adv_u16 = (fun _ -> bump 2);
+ asm_adv_u8 = (fun _ -> bump 1);
+ asm_adv_zstr = (fun _ -> while arr.{!off} != 0
+ do incr off done);
+ asm_close = (fun _ ->
+ assert (!is_open);
+ Unix.close fd;
+ is_open := false)
+ }
+;;
+
+
+(*
+ * Metadata note-section encoding / decoding.
+ *
+ * Since the only object format that defines a "note" section at all is
+ * ELF, we model the contents of the metadata section on ELF's
+ * notes. But the same blob of data is stuck into PE and Mach-O files
+ * too.
+ *
+ * The format is essentially just the ELF note format:
+ *
+ * <un-padded-size-of-name:u32>
+ * <size-of-desc:u32>
+ * <type-code=0:u32>
+ * <name="rust":zstr>
+ * <0-pad to 4-byte boundary>
+ * <n=meta-count:u32>
+ * <k1:zstr> <v1:zstr>
+ * ...
+ * <kn:zstr> <vn:zstr>
+ * <0-pad to 4-byte boundary>
+ *
+ *)
+let note_rust_frags (meta:(Ast.ident * string) array) : frag =
+ let desc_fixup = new_fixup ".rust.note metadata" in
+ let desc =
+ DEF (desc_fixup,
+ SEQ [|
+ WORD (TY_u32, IMM (Int64.of_int (Array.length meta)));
+ SEQ (Array.map
+ (fun (k,v) -> SEQ [| ZSTRING k; ZSTRING v; |])
+ meta);
+ ALIGN_FILE (4, MARK) |])
+ in
+ let name = "rust" in
+ let ty = 0L in
+ let padded_name = SEQ [| ZSTRING name;
+ ALIGN_FILE (4, MARK) |]
+ in
+ let name_sz = IMM (Int64.of_int ((String.length name) + 1)) in
+ SEQ [| WORD (TY_u32, name_sz);
+ WORD (TY_u32, F_SZ desc_fixup);
+ WORD (TY_u32, IMM ty);
+ padded_name;
+ desc;|]
+;;
+
+let read_rust_note (ar:asm_reader) : (Ast.ident * string) array =
+ ar.asm_adv_u32 ();
+ ar.asm_adv_u32 ();
+ assert ((ar.asm_get_u32 ()) = 0);
+ let rust_name = ar.asm_get_zstr_padded 8 in
+ assert (rust_name = "rust");
+ let n = ar.asm_get_u32() in
+ let meta = Queue.create () in
+ for i = 1 to n
+ do
+ let k = ar.asm_get_zstr() in
+ let v = ar.asm_get_zstr() in
+ Queue.add (k,v) meta
+ done;
+ queue_to_arr meta
+;;
+
+(*
+ * 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/be/elf.ml b/src/boot/be/elf.ml
new file mode 100644
index 00000000..56905b2a
--- /dev/null
+++ b/src/boot/be/elf.ml
@@ -0,0 +1,1760 @@
+(*
+ * Module for writing System V ELF files.
+ *
+ * FIXME: Presently heavily infected with x86 and elf32 specificities,
+ * though they are reasonably well marked. Needs to be refactored to
+ * depend on abi fields if it's to be usable for other elf
+ * configurations.
+ *)
+
+open Asm;;
+open Common;;
+
+let log (sess:Session.sess) =
+ Session.log "obj (elf)"
+ sess.Session.sess_log_obj
+ sess.Session.sess_log_out
+;;
+
+let iflog (sess:Session.sess) (thunk:(unit -> unit)) : unit =
+ if sess.Session.sess_log_obj
+ then thunk ()
+ else ()
+;;
+
+
+(* Fixed sizes of structs involved in elf32 spec. *)
+let elf32_ehsize = 52L;;
+let elf32_phentsize = 32L;;
+let elf32_shentsize = 40L;;
+let elf32_symsize = 16L;;
+let elf32_rela_entsz = 0xcL;;
+
+type ei_class =
+ ELFCLASSNONE
+ | ELFCLASS32
+ | ELFCLASS64
+;;
+
+
+type ei_data =
+ ELFDATANONE
+ | ELFDATA2LSB
+ | ELFDATA2MSB
+;;
+
+
+let elf_identification ei_class ei_data =
+ SEQ
+ [|
+ STRING "\x7fELF";
+ BYTES
+ [|
+ (match ei_class with (* EI_CLASS *)
+ ELFCLASSNONE -> 0
+ | ELFCLASS32 -> 1
+ | ELFCLASS64 -> 2);
+ (match ei_data with (* EI_DATA *)
+ ELFDATANONE -> 0
+ | ELFDATA2LSB -> 1
+ | ELFDATA2MSB -> 2);
+ 1; (* EI_VERSION = EV_CURRENT *)
+ 0; (* EI_PAD #7 *)
+ 0; (* EI_PAD #8 *)
+ 0; (* EI_PAD #9 *)
+ 0; (* EI_PAD #A *)
+ 0; (* EI_PAD #B *)
+ 0; (* EI_PAD #C *)
+ 0; (* EI_PAD #D *)
+ 0; (* EI_PAD #E *)
+ 0; (* EI_PAD #F *)
+ |]
+ |]
+;;
+
+
+type e_type =
+ ET_NONE
+ | ET_REL
+ | ET_EXEC
+ | ET_DYN
+ | ET_CORE
+;;
+
+
+type e_machine =
+ (* Maybe support more later. *)
+ EM_NONE
+ | EM_386
+ | EM_X86_64
+;;
+
+
+type e_version =
+ EV_NONE
+ | EV_CURRENT
+;;
+
+
+let elf32_header
+ ~(sess:Session.sess)
+ ~(ei_data:ei_data)
+ ~(e_type:e_type)
+ ~(e_machine:e_machine)
+ ~(e_version:e_version)
+ ~(e_entry_fixup:fixup)
+ ~(e_phoff_fixup:fixup)
+ ~(e_shoff_fixup:fixup)
+ ~(e_phnum:int64)
+ ~(e_shnum:int64)
+ ~(e_shstrndx:int64)
+ : frag =
+ let elf_header_fixup = new_fixup "elf header" in
+ let entry_pos =
+ if sess.Session.sess_library_mode
+ then (IMM 0L)
+ else (M_POS e_entry_fixup)
+ in
+ DEF
+ (elf_header_fixup,
+ SEQ [| elf_identification ELFCLASS32 ei_data;
+ WORD (TY_u16, (IMM (match e_type with
+ ET_NONE -> 0L
+ | ET_REL -> 1L
+ | ET_EXEC -> 2L
+ | ET_DYN -> 3L
+ | ET_CORE -> 4L)));
+ WORD (TY_u16, (IMM (match e_machine with
+ EM_NONE -> 0L
+ | EM_386 -> 3L
+ | EM_X86_64 -> 62L)));
+ WORD (TY_u32, (IMM (match e_version with
+ EV_NONE -> 0L
+ | EV_CURRENT -> 1L)));
+ WORD (TY_u32, entry_pos);
+ WORD (TY_u32, (F_POS e_phoff_fixup));
+ WORD (TY_u32, (F_POS e_shoff_fixup));
+ WORD (TY_u32, (IMM 0L)); (* e_flags *)
+ WORD (TY_u16, (IMM elf32_ehsize));
+ WORD (TY_u16, (IMM elf32_phentsize));
+ WORD (TY_u16, (IMM e_phnum));
+ WORD (TY_u16, (IMM elf32_shentsize));
+ WORD (TY_u16, (IMM e_shnum));
+ WORD (TY_u16, (IMM e_shstrndx));
+ |])
+;;
+
+
+type sh_type =
+ SHT_NULL
+ | SHT_PROGBITS
+ | SHT_SYMTAB
+ | SHT_STRTAB
+ | SHT_RELA
+ | SHT_HASH
+ | SHT_DYNAMIC
+ | SHT_NOTE
+ | SHT_NOBITS
+ | SHT_REL
+ | SHT_SHLIB
+ | SHT_DYNSYM
+;;
+
+
+type sh_flags =
+ SHF_WRITE
+ | SHF_ALLOC
+ | SHF_EXECINSTR
+;;
+
+
+let section_header
+ ~(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)
+ : frag =
+ SEQ
+ [|
+ WORD (TY_i32, (SUB
+ ((F_POS shname_string_fixup),
+ (F_POS shstring_table_fixup))));
+ WORD (TY_u32, (IMM (match sh_type with
+ SHT_NULL -> 0L
+ | SHT_PROGBITS -> 1L
+ | SHT_SYMTAB -> 2L
+ | SHT_STRTAB -> 3L
+ | SHT_RELA -> 4L
+ | SHT_HASH -> 5L
+ | SHT_DYNAMIC -> 6L
+ | SHT_NOTE -> 7L
+ | SHT_NOBITS -> 8L
+ | SHT_REL -> 9L
+ | SHT_SHLIB -> 10L
+ | SHT_DYNSYM -> 11L)));
+ WORD (TY_u32, (IMM (fold_flags
+ (fun f -> match f with
+ 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, (match section_fixup with
+ None -> (IMM 0L)
+ | Some s -> (F_POS s)));
+ WORD (TY_u32, (match section_fixup with
+ None -> (IMM 0L)
+ | Some s -> (F_SZ s)));
+ WORD (TY_u32, (IMM (match sh_link with
+ None -> 0L
+ | Some i -> i)));
+ WORD (TY_u32, (IMM 0L)); (* sh_info *)
+ WORD (TY_u32, (IMM sh_addralign));
+ WORD (TY_u32, (IMM sh_entsize));
+ |]
+;;
+
+
+type p_type =
+ PT_NULL
+ | PT_LOAD
+ | PT_DYNAMIC
+ | PT_INTERP
+ | PT_NOTE
+ | PT_SHLIB
+ | PT_PHDR
+;;
+
+
+type p_flag =
+ PF_X
+ | PF_W
+ | PF_R
+;;
+
+
+let program_header
+ ~(p_type:p_type)
+ ~(segment_fixup:fixup)
+ ~(p_flags:p_flag list)
+ ~(p_align:int64)
+ : frag =
+ SEQ
+ [|
+ WORD (TY_u32, (IMM (match p_type with
+ PT_NULL -> 0L
+ | PT_LOAD -> 1L
+ | PT_DYNAMIC -> 2L
+ | PT_INTERP -> 3L
+ | PT_NOTE -> 4L
+ | PT_SHLIB -> 5L
+ | PT_PHDR -> 6L)));
+ WORD (TY_u32, (F_POS segment_fixup));
+ WORD (TY_u32, (M_POS segment_fixup));
+ WORD (TY_u32, (M_POS segment_fixup));
+ WORD (TY_u32, (F_SZ segment_fixup));
+ WORD (TY_u32, (M_SZ segment_fixup));
+ WORD (TY_u32, (IMM (fold_flags
+ (fun f ->
+ match f with
+ PF_X -> 0x1L
+ | PF_W -> 0x2L
+ | PF_R -> 0x4L)
+ p_flags)));
+ WORD (TY_u32, (IMM p_align));
+ |]
+;;
+
+
+type st_bind =
+ STB_LOCAL
+ | STB_GLOBAL
+ | STB_WEAK
+;;
+
+
+type st_type =
+ STT_NOTYPE
+ | STT_OBJECT
+ | STT_FUNC
+ | STT_SECTION
+ | STT_FILE
+;;
+
+
+(* Special symbol-section indices *)
+let shn_UNDEF = 0L;;
+let shn_ABS = 0xfff1L;;
+let shn_ABS = 0xfff2L;;
+
+
+let symbol
+ ~(string_table_fixup:fixup)
+ ~(name_string_fixup:fixup)
+ ~(sym_target_fixup:fixup option)
+ ~(st_bind:st_bind)
+ ~(st_type:st_type)
+ ~(st_shndx:int64)
+ : frag =
+ let st_bind_num =
+ match st_bind with
+ STB_LOCAL -> 0L
+ | STB_GLOBAL -> 1L
+ | STB_WEAK -> 2L
+ in
+ let st_type_num =
+ match st_type with
+ STT_NOTYPE -> 0L
+ | STT_OBJECT -> 1L
+ | STT_FUNC -> 2L
+ | STT_SECTION -> 3L
+ | STT_FILE -> 4L
+ in
+ SEQ
+ [|
+ WORD (TY_u32, (SUB
+ ((F_POS name_string_fixup),
+ (F_POS string_table_fixup))));
+ WORD (TY_u32, (match sym_target_fixup with
+ None -> (IMM 0L)
+ | Some f -> (M_POS f)));
+ WORD (TY_u32, (match sym_target_fixup with
+ None -> (IMM 0L)
+ | Some f -> (M_SZ f)));
+ WORD (TY_u8, (* st_info *)
+ (OR
+ ((SLL ((IMM st_bind_num), 4)),
+ (AND ((IMM st_type_num), (IMM 0xfL))))));
+ WORD (TY_u8, (IMM 0L)); (* st_other *)
+ WORD (TY_u16, (IMM st_shndx));
+ |]
+;;
+
+type d_tag =
+ DT_NULL
+ | DT_NEEDED
+ | DT_PLTRELSZ
+ | DT_PLTGOT
+ | DT_HASH
+ | DT_STRTAB
+ | DT_SYMTAB
+ | DT_RELA
+ | DT_RELASZ
+ | DT_RELAENT
+ | DT_STRSZ
+ | DT_SYMENT
+ | DT_INIT
+ | DT_FINI
+ | DT_SONAME
+ | DT_RPATH
+ | DT_SYMBOLIC
+ | DT_REL
+ | DT_RELSZ
+ | DT_RELENT
+ | DT_PLTREL
+ | DT_DEBUG
+ | DT_TEXTREL
+ | DT_JMPREL
+ | DT_BIND_NOW
+ | DT_INIT_ARRAY
+ | DT_FINI_ARRAY
+ | DT_INIT_ARRAYSZ
+ | DT_FINI_ARRAYSZ
+ | DT_RUNPATH
+ | DT_FLAGS
+ | DT_ENCODING
+ | DT_PREINIT_ARRAY
+ | DT_PREINIT_ARRAYSZ
+;;
+
+type elf32_dyn = (d_tag * expr64);;
+
+let elf32_num_of_dyn_tag tag =
+ match tag with
+ DT_NULL -> 0L
+ | DT_NEEDED -> 1L
+ | DT_PLTRELSZ -> 2L
+ | DT_PLTGOT -> 3L
+ | DT_HASH -> 4L
+ | DT_STRTAB -> 5L
+ | DT_SYMTAB -> 6L
+ | DT_RELA -> 7L
+ | DT_RELASZ -> 8L
+ | DT_RELAENT -> 9L
+ | DT_STRSZ -> 10L
+ | DT_SYMENT -> 11L
+ | DT_INIT -> 12L
+ | DT_FINI -> 13L
+ | DT_SONAME -> 14L
+ | DT_RPATH -> 15L
+ | DT_SYMBOLIC -> 16L
+ | DT_REL -> 17L
+ | DT_RELSZ -> 18L
+ | DT_RELENT -> 19L
+ | DT_PLTREL -> 20L
+ | DT_DEBUG -> 21L
+ | DT_TEXTREL -> 22L
+ | DT_JMPREL -> 23L
+ | DT_BIND_NOW -> 24L
+ | DT_INIT_ARRAY -> 25L
+ | DT_FINI_ARRAY -> 26L
+ | DT_INIT_ARRAYSZ -> 27L
+ | DT_FINI_ARRAYSZ -> 28L
+ | DT_RUNPATH -> 29L
+ | DT_FLAGS -> 30L
+ | DT_ENCODING -> 31L
+ | DT_PREINIT_ARRAY -> 32L
+ | DT_PREINIT_ARRAYSZ -> 33L
+;;
+
+let elf32_dyn_frag d =
+ let (tag, expr) = d in
+ let tagval = elf32_num_of_dyn_tag tag in
+ SEQ [| WORD (TY_u32, (IMM tagval)); WORD (TY_u32, expr) |]
+;;
+
+type elf32_386_reloc_type =
+ R_386_NONE
+ | R_386_32
+ | R_386_PC32
+ | R_386_GOT32
+ | R_386_PLT32
+ | R_386_COPY
+ | R_386_GLOB_DAT
+ | R_386_JMP_SLOT
+ | R_386_RELATIVE
+ | R_386_GOTOFF
+ | R_386_GOTPC
+;;
+
+
+type elf32_386_rela =
+ { elf32_386_rela_type: elf32_386_reloc_type;
+ elf32_386_rela_offset: expr64;
+ elf32_386_rela_sym: expr64;
+ elf32_386_rela_addend: expr64 }
+;;
+
+let elf32_386_rela_frag r =
+ let type_val =
+ match r.elf32_386_rela_type with
+ R_386_NONE -> 0L
+ | R_386_32 -> 1L
+ | R_386_PC32 -> 2L
+ | R_386_GOT32 -> 3L
+ | R_386_PLT32 -> 4L
+ | R_386_COPY -> 5L
+ | R_386_GLOB_DAT -> 6L
+ | R_386_JMP_SLOT -> 7L
+ | R_386_RELATIVE -> 8L
+ | R_386_GOTOFF -> 9L
+ | R_386_GOTPC -> 10L
+ in
+ let info_expr =
+ WORD (TY_u32,
+ (OR
+ (SLL ((r.elf32_386_rela_sym), 8),
+ AND ((IMM 0xffL), (IMM type_val)))))
+ in
+ SEQ [| WORD (TY_u32, r.elf32_386_rela_offset);
+ info_expr;
+ WORD (TY_u32, r.elf32_386_rela_addend) |]
+;;
+
+
+let elf32_linux_x86_file
+ ~(sess:Session.sess)
+ ~(crate:Ast.crate)
+ ~(entry_name:string)
+ ~(text_frags:(string option, frag) Hashtbl.t)
+ ~(data_frags:(string option, frag) Hashtbl.t)
+ ~(rodata_frags:(string option, frag) Hashtbl.t)
+ ~(required_fixups:(string, fixup) Hashtbl.t)
+ ~(dwarf:Dwarf.debug_records)
+ ~(sem:Semant.ctxt)
+ ~(needed_libs:string array)
+ : frag =
+
+ (* Procedure Linkage Tables (PLTs), Global Offset Tables
+ * (GOTs), and the relocations that set them up:
+ *
+ * The PLT goes in a section called .plt and GOT in a section called
+ * .got. The portion of the GOT that holds PLT jump slots goes in a
+ * section called .got.plt. Dynamic relocations for these jump slots go in
+ * section .rela.plt.
+ *
+ * The easiest way to understand the PLT/GOT system is to draw it:
+ *
+ * PLT GOT
+ * +----------------------+ +----------------------+
+ * 0| push &<GOT[1]> 0| <reserved>
+ * | jmp *GOT[2] 1| <libcookie>
+ * | 2| & <ld.so:resolve-a-sym>
+ * 1| jmp *GOT[3] 3| & <'push 0' in PLT[1]>
+ * | push 0 4| & <'push 1' in PLT[2]>
+ * | jmp *PLT[0] 5| & <'push 2' in PLT[3]>
+ * |
+ * 2| jmp *GOT[4]
+ * | push 1
+ * | jmp *PLT[0]
+ * |
+ * 2| jmp *GOT[5]
+ * | push 2
+ * | jmp *PLT[0]
+ *
+ *
+ * In normal user code, we call PLT entries with a call to a
+ * PC-relative address, the PLT entry, which itself does an indirect
+ * jump through a slot in the GOT that it also addresses
+ * PC-relative. This makes the whole scheme PIC.
+ *
+ * The linker fills in the GOT on startup. For the first 3, it uses
+ * its own thinking. For the remainder it needs to be instructed to
+ * fill them in with "jump slot relocs", type R_386_JUMP_SLOT, each
+ * of which says in effect which PLT entry it's to point back to and
+ * which symbol it's to be resolved to later. These relocs go in the
+ * section .rela.plt.
+ *)
+
+ let plt0_fixup = new_fixup "PLT[0]" in
+ let got_prefix = SEQ [| WORD (TY_u32, (IMM 0L));
+ WORD (TY_u32, (IMM 0L));
+ WORD (TY_u32, (IMM 0L)); |]
+ in
+
+ let got_cell reg i =
+ let got_entry_off = Int64.of_int (i*4) in
+ let got_entry_mem = Il.RegIn (reg, (Some (Asm.IMM got_entry_off))) in
+ Il.Mem (got_entry_mem, Il.ScalarTy (Il.AddrTy Il.CodeTy))
+ in
+
+ let got_code_cell reg i =
+ Il.CodePtr (Il.Cell (got_cell reg i))
+ in
+
+ let plt0_frag =
+ let reg = Il.Hreg X86.eax in
+ let e = X86.new_emitter_without_vregs () in
+ Il.emit e (Il.Push (Il.Cell (got_cell reg 1)));
+ Il.emit e (Il.jmp Il.JMP (got_code_cell reg 2));
+ Il.emit e Il.Nop;
+ Il.emit e Il.Nop;
+ Il.emit e Il.Nop;
+ Il.emit e Il.Nop;
+ DEF (plt0_fixup, (X86.frags_of_emitted_quads sess e))
+ in
+
+ (*
+ * The existence of the GOT/PLT mish-mash causes, therefore, the
+ * following new sections:
+ *
+ * .plt - the PLT itself, in the r/x text segment
+ * .got.plt - the PLT-used portion of the GOT, in the r/w segment
+ * .rela.plt - the dynamic relocs for the GOT-PLT, in the r/x segment
+ *
+ * In addition, because we're starting up a dynamically linked executable,
+ * we have to have several more sections!
+ *
+ * .interp - the read-only section that names ld.so
+ * .dynsym - symbols named by the PLT/GOT entries, r/x segment
+ * .dynstr - string-names used in those symbols, r/x segment
+ * .hash - hashtable in which to look these up, r/x segment
+ * .dynamic - the machine-readable description of the dynamic
+ * linkage requirements of this elf file, in the
+ * r/w _DYNAMIC segment
+ *
+ * The Dynamic section contains a sequence of 2-word records of type
+ * d_tag.
+ *
+ *)
+
+ (* There are 17 official section headers in the file we're making: *)
+ (* *)
+ (* section 0: <null section> *)
+ (* *)
+ (* section 1: .interp (segment 1: R+X, INTERP) *)
+ (* *)
+ (* section 2: .text (segment 2: R+X, LOAD) *)
+ (* section 3: .rodata ... *)
+ (* section 4: .dynsym ... *)
+ (* section 5: .dynstr ... *)
+ (* section 6: .hash ... *)
+ (* section 7: .plt ... *)
+ (* section 8: .got ... *)
+ (* section 9: .rela.plt ... *)
+ (* *)
+ (* section 10: .data (segment 3: R+W, LOAD) *)
+ (* section 11: .bss ... *)
+ (* *)
+ (* section 12: .dynamic (segment 4: R+W, DYNAMIC) *)
+ (* *)
+ (* section 13: .shstrtab (not in a segment) *)
+ (* section 14: .debug_aranges (segment 2: cont'd) *)
+ (* section 15: .debug_pubnames ... *)
+ (* section 14: .debug_info ... *)
+ (* section 15: .debug_abbrev ... *)
+ (* section 14: .debug_line ... *)
+ (* section 15: .debug_frame ... *)
+ (* section 16: .note..rust (segment 5: NOTE) *)
+
+ let sname s =
+ new_fixup (Printf.sprintf "string name of '%s' section" s)
+ in
+ let null_section_name_fixup = sname "<null>" in
+ let interp_section_name_fixup = sname ".interp"in
+ let text_section_name_fixup = sname ".text" in
+ let rodata_section_name_fixup = sname ".rodata" in
+ let dynsym_section_name_fixup = sname ".dynsym" in
+ let dynstr_section_name_fixup = sname ".dynstr" in
+ let hash_section_name_fixup = sname ".hash" in
+ let plt_section_name_fixup = sname ".plt" in
+ let got_plt_section_name_fixup = sname ".got.plt" in
+ let rela_plt_section_name_fixup = sname ".rela.plt" in
+ let data_section_name_fixup = sname ".data" in
+ let bss_section_name_fixup = sname ".bss" in
+ let dynamic_section_name_fixup = sname ".dynamic" in
+ let shstrtab_section_name_fixup = sname ".shstrtab" in
+ let debug_aranges_section_name_fixup = sname ".debug_aranges" in
+ let debug_pubnames_section_name_fixup = sname ".debug_pubnames" in
+ let debug_info_section_name_fixup = sname ".debug_info" in
+ let debug_abbrev_section_name_fixup = sname ".debug_abbrev" in
+ let debug_line_section_name_fixup = sname ".debug_line" in
+ let debug_frame_section_name_fixup = sname ".debug_frame" in
+ let note_rust_section_name_fixup = sname ".note.rust" in
+
+ (* let interpndx = 1L in *) (* Section index of .interp *)
+ let textndx = 2L in (* Section index of .text *)
+ let rodatandx = 3L in (* Section index of .rodata *)
+ 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 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 *)
+ (* let bssndx = 11L in *) (* Section index of .bss *)
+ (* let dynamicndx = 12L in *) (* Section index of .dynamic *)
+ let shstrtabndx = 13L in (* Section index of .shstrtab *)
+
+ let section_header_table_fixup = new_fixup ".section header table" in
+ let interp_section_fixup = new_fixup ".interp section" in
+ let text_section_fixup = new_fixup ".text section" in
+ let rodata_section_fixup = new_fixup ".rodata section" in
+ let dynsym_section_fixup = new_fixup ".dynsym section" in
+ let dynstr_section_fixup = new_fixup ".dynstr section" in
+ let hash_section_fixup = new_fixup ".hash section" in
+ let plt_section_fixup = new_fixup ".plt section" in
+ let got_plt_section_fixup = new_fixup ".got.plt section" in
+ let rela_plt_section_fixup = new_fixup ".rela.plt section" in
+ let data_section_fixup = new_fixup ".data section" in
+ let bss_section_fixup = new_fixup ".bss section" in
+ let dynamic_section_fixup = new_fixup ".dynamic section" in
+ let shstrtab_section_fixup = new_fixup ".shstrtab section" in
+ let note_rust_section_fixup = new_fixup ".shstrtab section" in
+
+ let shstrtab_section =
+ SEQ
+ [|
+ DEF (null_section_name_fixup, ZSTRING "");
+ DEF (interp_section_name_fixup, ZSTRING ".interp");
+ DEF (text_section_name_fixup, ZSTRING ".text");
+ DEF (rodata_section_name_fixup, ZSTRING ".rodata");
+ DEF (dynsym_section_name_fixup, ZSTRING ".dynsym");
+ DEF (dynstr_section_name_fixup, ZSTRING ".dynstr");
+ DEF (hash_section_name_fixup, ZSTRING ".hash");
+ DEF (plt_section_name_fixup, ZSTRING ".plt");
+ DEF (got_plt_section_name_fixup, ZSTRING ".got.plt");
+ DEF (rela_plt_section_name_fixup, ZSTRING ".rela.plt");
+ DEF (data_section_name_fixup, ZSTRING ".data");
+ DEF (bss_section_name_fixup, ZSTRING ".bss");
+ DEF (dynamic_section_name_fixup, ZSTRING ".dynamic");
+ DEF (shstrtab_section_name_fixup, ZSTRING ".shstrtab");
+ DEF (debug_aranges_section_name_fixup, ZSTRING ".debug_aranges");
+ DEF (debug_pubnames_section_name_fixup, ZSTRING ".debug_pubnames");
+ DEF (debug_info_section_name_fixup, ZSTRING ".debug_info");
+ DEF (debug_abbrev_section_name_fixup, ZSTRING ".debug_abbrev");
+ DEF (debug_line_section_name_fixup, ZSTRING ".debug_line");
+ DEF (debug_frame_section_name_fixup, ZSTRING ".debug_frame");
+ DEF (note_rust_section_name_fixup, ZSTRING ".note.rust");
+ |]
+ in
+
+ let section_headers =
+ [|
+ (* <null> *)
+ (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);
+
+ (* .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);
+
+ (* .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);
+
+ (* .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);
+
+ (* .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_entsize: elf32_symsize
+ ~sh_link: (Some dynstrndx) );
+
+ (* .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);
+
+ (* .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));
+
+ (* .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);
+
+ (* .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);
+
+ (* .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));
+
+ (* .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);
+
+ (* .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);
+
+ (* .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);
+
+ (* .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);
+
+(*
+ FIXME: uncomment the dwarf section headers as you make use of them;
+ recent gdb versions have got fussier about parsing dwarf and don't
+ like seeing junk there.
+*)
+
+ (* .debug_aranges *)
+(*
+
+ (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);
+*)
+ (* .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);
+*)
+
+ (* .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);
+
+ (* .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);
+ (* .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);
+*)
+
+ (* .debug_frame *)
+(*
+ (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);
+*)
+
+ (* .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);
+
+ |]
+ in
+ let section_header_table = SEQ section_headers in
+
+
+ (* There are 6 official program headers in the file we're making: *)
+ (* segment 0: RX / PHDR *)
+ (* segment 1: R / INTERP *)
+ (* segment 2: RX / LOAD *)
+ (* segment 3: RW / LOAD *)
+ (* segment 4: RW / DYNAMIC *)
+ (* segment 5: R *)
+
+ let program_header_table_fixup = new_fixup "program header table" in
+ let segment_0_fixup = new_fixup "segment 0" in
+ let segment_1_fixup = new_fixup "segment 1" in
+ let segment_2_fixup = new_fixup "segment 2" in
+ let segment_3_fixup = new_fixup "segment 3" in
+ let segment_4_fixup = new_fixup "segment 4" in
+ let segment_5_fixup = new_fixup "segment 5" in
+
+ let segment_0_align = 4 in
+ let segment_1_align = 1 in
+ let segment_2_align = 0x1000 in
+ let segment_3_align = 0x1000 in
+ let segment_4_align = 0x1000 in
+ let segment_5_align = 1 in
+
+ let program_headers = [|
+ (program_header
+ ~p_type: PT_PHDR
+ ~segment_fixup: segment_0_fixup
+ ~p_flags: [ PF_R; PF_X ]
+ ~p_align: (Int64.of_int segment_0_align));
+ (program_header
+ ~p_type: PT_INTERP
+ ~segment_fixup: segment_1_fixup
+ ~p_flags: [ PF_R ]
+ ~p_align: (Int64.of_int segment_1_align));
+ (program_header
+ ~p_type: PT_LOAD
+ ~segment_fixup: segment_2_fixup
+ ~p_flags: [ PF_R; PF_X ]
+ ~p_align: (Int64.of_int segment_2_align));
+ (program_header
+ ~p_type: PT_LOAD
+ ~segment_fixup: segment_3_fixup
+ ~p_flags: [ PF_R; PF_W ]
+ ~p_align: (Int64.of_int segment_3_align));
+ (program_header
+ ~p_type: PT_DYNAMIC
+ ~segment_fixup: segment_4_fixup
+ ~p_flags: [ PF_R; PF_W ]
+ ~p_align: (Int64.of_int segment_4_align));
+ (program_header
+ ~p_type: PT_NOTE
+ ~segment_fixup: segment_5_fixup
+ ~p_flags: [ PF_R;]
+ ~p_align: (Int64.of_int segment_5_align));
+ |]
+ in
+ let program_header_table = SEQ program_headers in
+
+ let e_entry_fixup = new_fixup "entry symbol" in
+
+ let elf_header =
+ elf32_header
+ ~sess
+ ~ei_data: ELFDATA2LSB
+ ~e_type: ET_DYN
+ ~e_machine: EM_386
+ ~e_version: EV_CURRENT
+
+ ~e_entry_fixup: e_entry_fixup
+ ~e_phoff_fixup: program_header_table_fixup
+ ~e_shoff_fixup: section_header_table_fixup
+ ~e_phnum: (Int64.of_int (Array.length program_headers))
+ ~e_shnum: (Int64.of_int (Array.length section_headers))
+ ~e_shstrndx: shstrtabndx
+ in
+
+ let n_syms = ref 1 in (* The empty symbol, implicit. *)
+
+ let data_sym name st_bind fixup =
+ let name_fixup = new_fixup ("data symbol name fixup: '" ^ name ^ "'") in
+ let strtab_entry = DEF (name_fixup, ZSTRING name) in
+ let symtab_entry =
+ symbol
+ ~string_table_fixup: dynstr_section_fixup
+ ~name_string_fixup: name_fixup
+ ~sym_target_fixup: (Some fixup)
+ ~st_bind
+ ~st_type: STT_OBJECT
+ ~st_shndx: datandx
+ in
+ incr n_syms;
+ (strtab_entry, symtab_entry)
+ in
+
+ let rodata_sym name st_bind fixup =
+ let name_fixup = new_fixup ("rodata symbol name fixup: '" ^ name ^ "'") in
+ let strtab_entry = DEF (name_fixup, ZSTRING name) in
+ let symtab_entry =
+ symbol
+ ~string_table_fixup: dynstr_section_fixup
+ ~name_string_fixup: name_fixup
+ ~sym_target_fixup: (Some fixup)
+ ~st_bind
+ ~st_type: STT_OBJECT
+ ~st_shndx: rodatandx
+ in
+ incr n_syms;
+ (strtab_entry, symtab_entry)
+ in
+
+ let text_sym name st_bind fixup =
+ let name_fixup = new_fixup ("text symbol name fixup: '" ^ name ^ "'") in
+ let strtab_frag = DEF (name_fixup, ZSTRING name) in
+ let symtab_frag =
+ symbol
+ ~string_table_fixup: dynstr_section_fixup
+ ~name_string_fixup: name_fixup
+ ~sym_target_fixup: (Some fixup)
+ ~st_bind: st_bind
+ ~st_type: STT_FUNC
+ ~st_shndx: textndx
+ in
+ incr n_syms;
+ (strtab_frag, symtab_frag)
+ in
+
+ let require_sym name st_bind _(*fixup*) =
+ let name_fixup =
+ new_fixup ("require symbol name fixup: '" ^ name ^ "'")
+ in
+ let strtab_frag = DEF (name_fixup, ZSTRING name) in
+ let symtab_frag =
+ symbol
+ ~string_table_fixup: dynstr_section_fixup
+ ~name_string_fixup: name_fixup
+ ~sym_target_fixup: None
+ ~st_bind
+ ~st_type: STT_FUNC
+ ~st_shndx: shn_UNDEF
+ in
+ incr n_syms;
+ (strtab_frag, symtab_frag)
+ in
+
+ let frags_of_symbol sym_emitter st_bind symname_opt symbody x =
+ let (strtab_frags, symtab_frags, body_frags) = x in
+ let (strtab_frag, symtab_frag, body_frag) =
+ match symname_opt with
+ None -> (MARK, MARK, symbody)
+ | Some symname ->
+ let body_fixup =
+ new_fixup ("symbol body fixup: '" ^ symname ^ "'")
+ in
+ let body =
+ if symname = entry_name
+ then DEF (e_entry_fixup, DEF (body_fixup, symbody))
+ else DEF (body_fixup, symbody)
+ in
+ let (str, sym) = sym_emitter symname st_bind body_fixup in
+ (str, sym, body)
+ in
+ ((strtab_frag :: strtab_frags),
+ (symtab_frag :: symtab_frags),
+ (body_frag :: body_frags))
+ in
+
+ let frags_of_require_symbol sym_emitter st_bind symname plt_entry_fixup x =
+ let (i, strtab_frags, symtab_frags,
+ plt_frags, got_plt_frags, rela_plt_frags) = x in
+ let (strtab_frag, symtab_frag) = sym_emitter symname st_bind None in
+ let e = X86.new_emitter_without_vregs () in
+ let jump_slot_fixup = new_fixup ("jump slot #" ^ string_of_int i) in
+ let jump_slot_initial_target_fixup =
+ new_fixup ("jump slot #" ^ string_of_int i ^ " initial target") in
+
+ (* You may notice this PLT entry doesn't look like either of the
+ * types of "normal" PLT entries outlined in the ELF manual. It is,
+ * however, just what you get when you combine a PIC PLT entry with
+ * inline calls to the horrible __i686.get_pc_thunk.ax kludge used
+ * on x86 to support entering PIC PLTs. We're just doing it *in*
+ * the PLT entries rather than infecting all the callers with the
+ * obligation of having the GOT address in a register on
+ * PLT-entry.
+ *)
+
+ let plt_frag =
+ let (reg, _, _) = X86.get_next_pc_thunk in
+
+ Il.emit_full e (Some plt_entry_fixup) [] Il.Dead;
+
+ Abi.load_fixup_addr e reg got_plt_section_fixup Il.CodeTy;
+
+ Il.emit e (Il.jmp Il.JMP (got_code_cell reg (2+i)));
+
+ Il.emit_full e (Some jump_slot_initial_target_fixup)
+ [] (Il.Push (X86.immi (Int64.of_int i)));
+
+ Il.emit e (Il.jmp Il.JMP (Il.direct_code_ptr plt0_fixup));
+ X86.frags_of_emitted_quads sess e
+ in
+ let got_plt_frag =
+ DEF (jump_slot_fixup,
+ WORD (TY_u32, (M_POS jump_slot_initial_target_fixup)))
+ in
+ let rela_plt =
+ { elf32_386_rela_type = R_386_JMP_SLOT;
+ elf32_386_rela_offset = (M_POS jump_slot_fixup);
+ elf32_386_rela_sym = (IMM (Int64.of_int i));
+ elf32_386_rela_addend = (IMM 0L) }
+ in
+ let rela_plt_frag = elf32_386_rela_frag rela_plt in
+ (i+1,
+ (strtab_frag :: strtab_frags),
+ (symtab_frag :: symtab_frags),
+ (plt_frag :: plt_frags),
+ (got_plt_frag :: got_plt_frags),
+ (rela_plt_frag :: rela_plt_frags))
+ in
+
+ (* Emit text export symbols. *)
+ let (global_text_strtab_frags, global_text_symtab_frags) =
+ match htab_search sem.Semant.ctxt_native_provided SEG_text with
+ None -> ([], [])
+ | Some etab ->
+ Hashtbl.fold
+ begin
+ fun name fix x ->
+ let (strtab_frags, symtab_frags) = x in
+ let (str, sym) = text_sym name STB_GLOBAL fix in
+ (str :: strtab_frags,
+ sym :: symtab_frags)
+ end
+ etab
+ ([],[])
+ in
+
+ (* Emit text fragments (possibly named). *)
+ let (global_text_strtab_frags,
+ global_text_symtab_frags,
+ text_body_frags) =
+ Hashtbl.fold
+ (frags_of_symbol text_sym STB_GLOBAL)
+ text_frags
+ (global_text_strtab_frags, global_text_symtab_frags, [])
+ in
+
+ let (local_text_strtab_frags,
+ local_text_symtab_frags) =
+
+ let symbol_frags_of_code _ code accum =
+ let (strtab_frags, symtab_frags) = accum in
+ let fix = code.Semant.code_fixup in
+ let (strtab_frag, symtab_frag) =
+ text_sym fix.fixup_name STB_LOCAL fix
+ in
+ (strtab_frag :: strtab_frags,
+ symtab_frag :: symtab_frags)
+ in
+
+ let symbol_frags_of_glue_code g code accum =
+ let (strtab_frags, symtab_frags) = accum in
+ let fix = code.Semant.code_fixup in
+ let (strtab_frag, symtab_frag) =
+ text_sym (Semant.glue_str sem g) STB_LOCAL fix
+ in
+ (strtab_frag :: strtab_frags,
+ symtab_frag :: symtab_frags)
+ in
+
+ let item_str_frags, item_sym_frags =
+ Hashtbl.fold symbol_frags_of_code
+ sem.Semant.ctxt_all_item_code ([], [])
+ in
+ let glue_str_frags, glue_sym_frags =
+ Hashtbl.fold symbol_frags_of_glue_code
+ sem.Semant.ctxt_glue_code ([], [])
+ in
+ (item_str_frags @ glue_str_frags,
+ item_sym_frags @ glue_sym_frags)
+ in
+
+ (* Emit rodata export symbols. *)
+ let (rodata_strtab_frags, rodata_symtab_frags) =
+ match htab_search sem.Semant.ctxt_native_provided SEG_data with
+ None -> ([], [])
+ | Some etab ->
+ Hashtbl.fold
+ begin
+ fun name fix x ->
+ let (strtab_frags, symtab_frags) = x in
+ let (str, sym) = rodata_sym name STB_GLOBAL fix in
+ (str :: strtab_frags,
+ sym :: symtab_frags)
+ end
+ etab
+ ([],[])
+ in
+
+ (* Emit rodata fragments (possibly named). *)
+ let (rodata_strtab_frags,
+ rodata_symtab_frags,
+ rodata_body_frags) =
+ Hashtbl.fold
+ (frags_of_symbol rodata_sym STB_GLOBAL)
+ rodata_frags
+ (rodata_strtab_frags, rodata_symtab_frags, [])
+ in
+
+
+ let (data_strtab_frags,
+ data_symtab_frags,
+ data_body_frags) =
+ Hashtbl.fold (frags_of_symbol data_sym STB_GLOBAL) data_frags ([],[],[])
+ in
+
+ let (_,
+ require_strtab_frags,
+ require_symtab_frags,
+ plt_frags,
+ got_plt_frags,
+ rela_plt_frags) =
+ Hashtbl.fold (frags_of_require_symbol require_sym STB_GLOBAL)
+ required_fixups
+ (1,[],[],[plt0_frag],[got_prefix],[])
+ in
+ let require_symtab_frags = List.rev require_symtab_frags in
+ let plt_frags = List.rev plt_frags in
+ let got_plt_frags = List.rev got_plt_frags in
+ let rela_plt_frags = List.rev rela_plt_frags in
+
+ let dynamic_needed_strtab_frags =
+ Array.make (Array.length needed_libs) MARK
+ in
+
+ let dynamic_frags =
+ let dynamic_needed_frags = Array.make (Array.length needed_libs) MARK in
+ for i = 0 to (Array.length needed_libs) - 1 do
+ let fixup =
+ new_fixup ("needed library name fixup: " ^ needed_libs.(i))
+ in
+ dynamic_needed_frags.(i) <-
+ elf32_dyn_frag (DT_NEEDED, SUB (M_POS fixup,
+ M_POS dynstr_section_fixup));
+ dynamic_needed_strtab_frags.(i) <-
+ DEF (fixup, ZSTRING needed_libs.(i))
+ done;
+ (SEQ [|
+ SEQ dynamic_needed_frags;
+ elf32_dyn_frag (DT_STRTAB, M_POS dynstr_section_fixup);
+ elf32_dyn_frag (DT_STRSZ, M_SZ dynstr_section_fixup);
+
+ elf32_dyn_frag (DT_SYMTAB, M_POS dynsym_section_fixup);
+ elf32_dyn_frag (DT_SYMENT, IMM elf32_symsize);
+
+ elf32_dyn_frag (DT_HASH, M_POS hash_section_fixup);
+ elf32_dyn_frag (DT_PLTGOT, M_POS got_plt_section_fixup);
+
+ elf32_dyn_frag (DT_PLTREL, IMM (elf32_num_of_dyn_tag DT_RELA));
+ elf32_dyn_frag (DT_PLTRELSZ, M_SZ rela_plt_section_fixup);
+ elf32_dyn_frag (DT_JMPREL, M_POS rela_plt_section_fixup);
+
+ elf32_dyn_frag (DT_NULL, IMM 0L)
+ |])
+ in
+
+ let null_strtab_fixup = new_fixup "null dynstrtab entry" in
+ let null_strtab_frag = DEF (null_strtab_fixup, ZSTRING "") in
+ let null_symtab_frag = (symbol
+ ~string_table_fixup: dynstr_section_fixup
+ ~name_string_fixup: null_strtab_fixup
+ ~sym_target_fixup: None
+ ~st_bind: STB_LOCAL
+ ~st_type: STT_NOTYPE
+ ~st_shndx: 0L) in
+
+ let dynsym_frags = (null_symtab_frag ::
+ (require_symtab_frags @
+ global_text_symtab_frags @
+ local_text_symtab_frags @
+ rodata_symtab_frags @
+ data_symtab_frags))
+ in
+
+ let dynstr_frags = (null_strtab_frag ::
+ (require_strtab_frags @
+ global_text_strtab_frags @
+ local_text_strtab_frags @
+ rodata_strtab_frags @
+ data_strtab_frags @
+ (Array.to_list dynamic_needed_strtab_frags)))
+ in
+
+ let interp_section =
+ DEF (interp_section_fixup, ZSTRING "/lib/ld-linux.so.2")
+ in
+
+ let text_section =
+ DEF (text_section_fixup,
+ SEQ (Array.of_list text_body_frags))
+ in
+ let rodata_section =
+ DEF (rodata_section_fixup,
+ SEQ (Array.of_list rodata_body_frags))
+ in
+ let data_section =
+ DEF (data_section_fixup,
+ SEQ (Array.of_list data_body_frags))
+ in
+ let bss_section =
+ DEF (bss_section_fixup,
+ SEQ [| |])
+ in
+ let dynsym_section =
+ DEF (dynsym_section_fixup,
+ SEQ (Array.of_list dynsym_frags))
+ in
+ let dynstr_section =
+ DEF (dynstr_section_fixup,
+ SEQ (Array.of_list dynstr_frags))
+ in
+
+ let hash_section =
+ let n_syms = !n_syms in
+
+ DEF (hash_section_fixup,
+ (* Worst hashtable ever: one chain. *)
+ SEQ [|
+ WORD (TY_u32, IMM 1L); (* nbucket *)
+ WORD (TY_u32, (* nchain *)
+ IMM (Int64.of_int n_syms));
+ WORD (TY_u32, IMM 1L); (* bucket 0 => symbol 1. *)
+ SEQ
+ begin
+ Array.init
+ n_syms
+ (fun i ->
+ let next = (* chain[i] => if last then 0 else i+1 *)
+ if i > 0 && i < (n_syms-1)
+ then Int64.of_int (i+1)
+ else 0L
+ in
+ WORD (TY_u32, IMM next))
+ end;
+ |])
+ in
+
+ let plt_section =
+ DEF (plt_section_fixup,
+ SEQ (Array.of_list plt_frags))
+ in
+
+ let got_plt_section =
+ DEF (got_plt_section_fixup,
+ SEQ (Array.of_list got_plt_frags))
+ in
+
+ let rela_plt_section =
+ DEF (rela_plt_section_fixup,
+ SEQ (Array.of_list rela_plt_frags))
+ in
+
+ let dynamic_section =
+ DEF (dynamic_section_fixup, dynamic_frags)
+ in
+
+ let note_rust_section =
+ DEF (note_rust_section_fixup,
+ (Asm.note_rust_frags crate.node.Ast.crate_meta))
+ in
+
+
+ let page_alignment = 0x1000 in
+
+ let align_both i =
+ ALIGN_FILE (page_alignment,
+ (ALIGN_MEM (page_alignment, i)))
+ in
+
+ let def_aligned f i =
+ align_both
+ (SEQ [| DEF(f,i);
+ (align_both MARK)|])
+ in
+
+ let debug_aranges_section =
+ def_aligned
+ sem.Semant.ctxt_debug_aranges_fixup
+ dwarf.Dwarf.debug_aranges
+ in
+ let debug_pubnames_section =
+ def_aligned
+ sem.Semant.ctxt_debug_pubnames_fixup
+ dwarf.Dwarf.debug_pubnames
+ in
+ let debug_info_section =
+ def_aligned
+ sem.Semant.ctxt_debug_info_fixup
+ dwarf.Dwarf.debug_info
+ in
+ let debug_abbrev_section =
+ def_aligned
+ sem.Semant.ctxt_debug_abbrev_fixup
+ dwarf.Dwarf.debug_abbrev
+ in
+ let debug_line_section =
+ def_aligned
+ sem.Semant.ctxt_debug_line_fixup
+ dwarf.Dwarf.debug_line
+ in
+ let debug_frame_section =
+ def_aligned sem.Semant.ctxt_debug_frame_fixup dwarf.Dwarf.debug_frame
+ in
+
+ let load_address = 0x0804_8000L in
+
+ SEQ
+ [|
+ MEMPOS load_address;
+ ALIGN_FILE
+ (segment_2_align,
+ DEF
+ (segment_2_fixup,
+ SEQ
+ [|
+ DEF (sem.Semant.ctxt_image_base_fixup, MARK);
+ elf_header;
+ ALIGN_FILE
+ (segment_0_align,
+ DEF
+ (segment_0_fixup,
+ SEQ
+ [|
+ DEF (program_header_table_fixup,
+ program_header_table);
+ |]));
+ ALIGN_FILE
+ (segment_1_align,
+ DEF (segment_1_fixup, interp_section));
+ text_section;
+ rodata_section;
+ dynsym_section;
+ dynstr_section;
+ hash_section;
+ plt_section;
+ rela_plt_section;
+ debug_aranges_section;
+ debug_pubnames_section;
+ debug_info_section;
+ debug_abbrev_section;
+ debug_line_section;
+ debug_frame_section;
+ |]));
+ ALIGN_FILE
+ (segment_3_align,
+ DEF
+ (segment_3_fixup,
+ SEQ
+ [|
+ data_section;
+ got_plt_section;
+ bss_section;
+ ALIGN_FILE
+ (segment_4_align,
+ DEF (segment_4_fixup,
+ dynamic_section));
+ ALIGN_FILE
+ (segment_5_align,
+ DEF (segment_5_fixup,
+ note_rust_section));
+ |]));
+ DEF (shstrtab_section_fixup,
+ shstrtab_section);
+ DEF (section_header_table_fixup,
+ section_header_table);
+ |]
+;;
+
+let emit_file
+ (sess:Session.sess)
+ (crate:Ast.crate)
+ (code:Asm.frag)
+ (data:Asm.frag)
+ (sem:Semant.ctxt)
+ (dwarf:Dwarf.debug_records)
+ : unit =
+
+ let text_frags = Hashtbl.create 4 in
+ let rodata_frags = Hashtbl.create 4 in
+ let data_frags = Hashtbl.create 4 in
+ let required_fixups = Hashtbl.create 4 in
+
+ (*
+ * Startup on elf-linux is more complex than in win32. It's
+ * thankfully documented in some detail around the net.
+ *
+ * - The elf entry address is for _start.
+ *
+ * - _start pushes:
+ *
+ * eax (should be zero)
+ * esp (holding the kernel-provided stack end)
+ * edx (address of _rtld_fini)
+ * address of _fini
+ * address of _init
+ * ecx (argv)
+ * esi (argc)
+ * address of main
+ *
+ * and then calls __libc_start_main@plt.
+ *
+ * - This means any sensible binary has a PLT. Fun. So
+ * We call into the PLT, which itself is just a bunch
+ * of indirect jumps through slots in the GOT, and wind
+ * up in __libc_start_main. Which calls _init, then
+ * essentially exit(main(argc,argv)).
+ *)
+
+
+ let init_fixup = new_fixup "_init function entry" in
+ let fini_fixup = new_fixup "_fini function entry" in
+ let (start_fixup, rust_start_fixup) =
+ if sess.Session.sess_library_mode
+ then (None, None)
+ else (Some (new_fixup "start function entry"),
+ Some (Semant.require_native sem REQUIRED_LIB_rustrt "rust_start"))
+ in
+ let libc_start_main_fixup = new_fixup "__libc_start_main@plt stub" in
+
+ let start_fn _ =
+ let start_fixup =
+ match start_fixup with
+ None -> bug () "missing start fixup in non-library mode"
+ | Some s -> s
+ in
+ let e = X86.new_emitter_without_vregs () in
+ let push_r32 r = Il.emit e
+ (Il.Push (Il.Cell (Il.Reg (Il.Hreg r, Il.ValTy Il.Bits32))))
+ in
+ let push_pos32 = X86.push_pos32 e in
+
+ Il.emit e (Il.unary Il.UMOV (X86.rc X86.ebp) (X86.immi 0L));
+ Il.emit e (Il.Pop (X86.rc X86.esi));
+ Il.emit e (Il.unary Il.UMOV (X86.rc X86.ecx) (X86.ro X86.esp));
+ Il.emit e (Il.binary Il.AND
+ (X86.rc X86.esp) (X86.ro X86.esp)
+ (X86.immi 0xfffffffffffffff0L));
+
+ push_r32 X86.eax;
+ push_r32 X86.esp;
+ push_r32 X86.edx;
+ push_pos32 fini_fixup;
+ push_pos32 init_fixup;
+ push_r32 X86.ecx;
+ push_r32 X86.esi;
+ push_pos32 start_fixup;
+ Il.emit e (Il.call
+ (Il.Reg (Il.Hreg X86.eax, Il.ValTy Il.Bits32))
+ (Il.direct_code_ptr libc_start_main_fixup));
+ X86.frags_of_emitted_quads sess e
+ in
+
+ let do_nothing_fn _ =
+ let e = X86.new_emitter_without_vregs () in
+ Il.emit e Il.Ret;
+ X86.frags_of_emitted_quads sess e
+ in
+
+ let main_fn _ =
+ match (start_fixup, rust_start_fixup, sem.Semant.ctxt_main_fn_fixup) with
+ (None, _, _)
+ | (_, None, _)
+ | (_, _, None) -> MARK
+ | (Some start_fixup,
+ Some rust_start_fixup,
+ Some main_fn_fixup) ->
+ let e = X86.new_emitter_without_vregs () in
+ X86.objfile_start e
+ ~start_fixup
+ ~rust_start_fixup
+ ~main_fn_fixup
+ ~crate_fixup: sem.Semant.ctxt_crate_fixup
+ ~indirect_start: false;
+ X86.frags_of_emitted_quads sess e
+ in
+
+ let needed_libs =
+ [|
+ "libc.so.6";
+ "librustrt.so"
+ |]
+ in
+
+ let _ =
+ if not sess.Session.sess_library_mode
+ then
+ begin
+ htab_put text_frags (Some "_start") (start_fn());
+ htab_put text_frags (Some "_init")
+ (DEF (init_fixup, do_nothing_fn()));
+ htab_put text_frags (Some "_fini")
+ (DEF (fini_fixup, do_nothing_fn()));
+ htab_put text_frags (Some "main") (main_fn ());
+ htab_put required_fixups "__libc_start_main" libc_start_main_fixup;
+ end;
+ htab_put text_frags None code;
+ htab_put rodata_frags None data;
+
+ Hashtbl.iter
+ begin
+ fun _ tab ->
+ Hashtbl.iter
+ begin
+ fun name fixup ->
+ htab_put required_fixups name fixup
+ end
+ tab
+ end
+ sem.Semant.ctxt_native_required
+ in
+ let all_frags =
+ elf32_linux_x86_file
+ ~sess
+ ~crate
+ ~entry_name: "_start"
+ ~text_frags
+ ~data_frags
+ ~dwarf
+ ~sem
+ ~rodata_frags
+ ~required_fixups
+ ~needed_libs
+ in
+ write_out_frag sess true all_frags
+;;
+
+let elf_magic = "\x7fELF";;
+
+let sniff
+ (sess:Session.sess)
+ (filename:filename)
+ : asm_reader option =
+ try
+ let stat = Unix.stat filename in
+ if (stat.Unix.st_kind = Unix.S_REG) &&
+ (stat.Unix.st_size > 4)
+ then
+ let ar = new_asm_reader sess filename in
+ let _ = log sess "sniffing ELF file" in
+ if (ar.asm_get_zstr_padded 4) = elf_magic
+ then (ar.asm_seek 0; Some ar)
+ else None
+ else
+ None
+ with
+ _ -> None
+;;
+
+let get_sections
+ (sess:Session.sess)
+ (ar:asm_reader)
+ : (string,(int*int)) Hashtbl.t =
+ let sects = Hashtbl.create 0 in
+ let _ = log sess "reading sections" in
+ let elf_id = ar.asm_get_zstr_padded 4 in
+ let _ = assert (elf_id = elf_magic) in
+
+ let _ = ar.asm_seek 0x10 in
+ let _ = ar.asm_adv_u16 () in (* e_type *)
+ let _ = ar.asm_adv_u16 () in (* e_machine *)
+ let _ = ar.asm_adv_u32 () in (* e_version *)
+ let _ = ar.asm_adv_u32 () in (* e_entry *)
+ let _ = ar.asm_adv_u32 () in (* e_phoff *)
+ let e_shoff = ar.asm_get_u32 () in (* e_shoff *)
+ let _ = ar.asm_adv_u32 () in (* e_flags *)
+ let _ = ar.asm_adv_u16 () in (* e_ehsize *)
+ let _ = ar.asm_adv_u16 () in (* e_phentsize *)
+ let _ = ar.asm_adv_u16 () in (* e_phnum *)
+ let e_shentsize = ar.asm_get_u16 () in
+ let e_shnum = ar.asm_get_u16 () in
+ let e_shstrndx = ar.asm_get_u16 () in
+ let _ = log sess
+ "%d ELF section headers, %d bytes each, starting at 0x%x"
+ e_shnum e_shentsize e_shoff
+ in
+ let _ = log sess "section %d is .shstrtab" e_shstrndx in
+
+ let read_section_hdr n =
+ let _ = ar.asm_seek (e_shoff + n * e_shentsize) in
+ let str_off = ar.asm_get_u32() in
+ let _ = ar.asm_adv_u32() in (* sh_type *)
+ let _ = ar.asm_adv_u32() in (* sh_flags *)
+ let _ = ar.asm_adv_u32() in (* sh_addr *)
+ let off = ar.asm_get_u32() in (* sh_off *)
+ let size = ar.asm_get_u32() in (* sh_size *)
+ let _ = ar.asm_adv_u32() in (* sh_link *)
+ let _ = ar.asm_adv_u32() in (* sh_info *)
+ let _ = ar.asm_adv_u32() in (* sh_addralign *)
+ let _ = ar.asm_adv_u32() in (* sh_entsize *)
+ (str_off, off, size)
+ in
+
+ let (_, str_base, _) = read_section_hdr e_shstrndx in
+
+ let _ = ar.asm_seek e_shoff in
+ for i = 0 to (e_shnum - 1) do
+ let (str_off, off, size) = read_section_hdr i in
+ let _ = ar.asm_seek (str_base + str_off) in
+ let name = ar.asm_get_zstr() in
+ log sess "section %d: %s, size %d, offset 0x%x" i name size off;
+ Hashtbl.add sects name (off, size);
+ done;
+ sects
+;;
+
+
+(*
+ * 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/be/il.ml b/src/boot/be/il.ml
new file mode 100644
index 00000000..e095e627
--- /dev/null
+++ b/src/boot/be/il.ml
@@ -0,0 +1,1135 @@
+open Common;;
+
+(* FIXME (issue #1): thread a session object through this eventually. *)
+let log_iltypes = ref false;;
+
+(* IL type system, very rudimentary. *)
+
+type bits =
+ Bits8
+ | Bits16
+ | Bits32
+ | Bits64
+;;
+
+type scalar_ty =
+ ValTy of bits
+ | AddrTy of referent_ty
+
+and referent_ty =
+ ScalarTy of scalar_ty
+ | StructTy of referent_ty array
+ | UnionTy of referent_ty array
+ | ParamTy of ty_param_idx (* Thing of current-frame type-param #n *)
+ | OpaqueTy (* Unknown memory-resident thing. *)
+ | CodeTy (* Executable machine code. *)
+ | NilTy (* 0 bits of space. *)
+;;
+
+let (voidptr_t:scalar_ty) = AddrTy OpaqueTy;;
+let (codeptr_t:scalar_ty) = AddrTy CodeTy;;
+
+(* Operands. *)
+
+type vreg = int ;;
+type hreg = int ;;
+type label = int ;;
+type spill = int ;;
+
+type reg =
+ Vreg of vreg
+ | Hreg of hreg
+;;
+
+type mem =
+ Abs of Asm.expr64
+ | RegIn of (reg * (Asm.expr64 option))
+ | Spill of spill
+;;
+
+type typed_reg = (reg * scalar_ty);;
+type typed_mem = (mem * referent_ty);;
+type typed_imm = (Asm.expr64 * ty_mach);;
+type typed_imm_ptr = (fixup * referent_ty);;
+
+type cell =
+ Reg of typed_reg
+ | Mem of typed_mem
+;;
+
+(*
+ * ImmPtr (a, rty) can be assigned to anything of scalar_ty
+ * AddrTy rty; the difference is that ImmAddr carries its value
+ * so can be used in cases where we want to have an immediate
+ * address constant-propagated through the code to the backend.
+ *)
+type operand =
+ Cell of cell
+ | Imm of typed_imm
+ | ImmPtr of typed_imm_ptr
+;;
+
+
+type code =
+ CodeLabel of label (* Index into current quad block. *)
+ | CodePtr of operand
+ | CodeNone
+;;
+
+(* NB: for the most part, we let the register allocator assign spills
+ * from vregs, and we permanently allocate aliased slots to stack
+ * locations by static aliasing information early, in layout.
+ *
+ * The one awkward case this doesn't handle is when someone tries to
+ * pass a literal-atom to an alias-slot. This *requires* a memory slot
+ * but we only realize it rather late, much later than we'd normally
+ * have thougt to desugar the literal into a temporary.
+ *
+ * So in these cases, we let the trans module explicitly demand a
+ * "Spill n" operand, which the register allocator mops up before it
+ * gets started on the vregs.
+ *
+ * NOTE: if we were more clever we'd integrate vregs and spills like
+ * this together along with the general notion of a temporary way back
+ * at the desugaring stage, and use some kind of size-class
+ * consolidation so that spills with non-overlapping lifetimes could
+ * share memory. But we're not that clever yet.
+ *)
+
+
+(* Helpers. *)
+
+let direct_code_ptr fix =
+ (CodePtr (ImmPtr (fix, CodeTy)))
+;;
+
+let cell_referent_ty c =
+ match c with
+ Reg (_, st) -> ScalarTy st
+ | Mem (_, rt) -> rt
+;;
+
+let cell_is_nil c =
+ match c with
+ Mem (_, NilTy) -> true
+ | Reg (_, AddrTy NilTy) -> true
+ | _ -> false
+;;
+
+let operand_is_nil o =
+ match o with
+ Cell c -> cell_is_nil c
+ | _ -> false
+;;
+
+let mem_off (mem:mem) (off:Asm.expr64) : mem =
+ let addto e = Asm.ADD (off, e) in
+ match mem with
+ Abs e -> Abs (addto e)
+ | RegIn (r, None) -> RegIn (r, Some off)
+ | RegIn (r, Some e) -> RegIn (r, Some (addto e))
+ | Spill _ -> bug () "Adding offset to spill slot"
+;;
+
+let mem_off_imm (mem:mem) (imm:int64) : mem =
+ mem_off mem (Asm.IMM imm)
+;;
+
+
+(* Quads. *)
+
+type binop =
+ ADD | SUB
+ | IMUL | UMUL
+ | IDIV | UDIV
+ | IMOD | UMOD
+ | AND | OR | XOR
+ | LSL | LSR | ASR
+;;
+
+type unop =
+ NEG | NOT
+ | UMOV | IMOV
+ | ZERO
+;;
+
+type jmpop =
+ JE | JNE
+ | JZ | JNZ (* FIXME: Synonyms with JE/JNE in x86, others? *)
+ | JL | JLE | JG | JGE (* Signed. *)
+ | JB | JBE | JA | JAE (* Unsigned. *)
+ | JC | JNC | JO | JNO
+ | JMP
+;;
+
+type binary =
+ {
+ binary_op: binop;
+ binary_dst: cell;
+ binary_lhs: operand;
+ binary_rhs: operand
+ }
+;;
+
+type unary =
+ {
+ unary_op: unop;
+ unary_dst: cell;
+ unary_src: operand
+ }
+;;
+
+type cmp =
+ {
+ cmp_lhs: operand;
+ cmp_rhs: operand
+ }
+;;
+
+type lea =
+ {
+ lea_dst: cell;
+ lea_src: operand
+ }
+;;
+
+type jmp =
+ {
+ jmp_op: jmpop;
+ jmp_targ: code;
+ }
+;;
+
+type call =
+ {
+ call_dst: cell;
+ call_targ: code
+ }
+
+type quad' =
+ Binary of binary
+ | Unary of unary
+ | Lea of lea
+ | Cmp of cmp
+ | Jmp of jmp
+ | Push of operand
+ | Pop of cell
+ | Call of call
+ | Debug (* Debug-break pseudo-instruction. *)
+ | Enter of fixup (* Enter-fixup-block pseudo-instruction. *)
+ | Leave (* Leave-fixup-block pseudo-instruction. *)
+ | Ret (* Return to caller. *)
+ | Nop (* Keep this quad here, emit CPU nop. *)
+ | Dead (* Keep this quad but emit nothing. *)
+ | Regfence (* Clobber all hregs. *)
+ | End (* Space past the end of quads to emit. *)
+;;
+
+type quad =
+ { quad_fixup: fixup option;
+ quad_implicits: label list;
+ quad_body: quad'; }
+
+type quads = quad array ;;
+
+(* Query functions. *)
+
+let cell_is_scalar (c:cell) : bool =
+ match c with
+ Reg (_, _) -> true
+ | Mem (_, ScalarTy _) -> true
+ | _ -> false
+;;
+
+
+let bits_of_ty_mach (tm:ty_mach) : bits =
+ match tm with
+ | TY_u8 -> Bits8
+ | TY_i8 -> Bits8
+ | TY_u16 -> Bits16
+ | TY_i16 -> Bits16
+ | TY_u32 -> Bits32
+ | TY_i32 -> Bits32
+ | TY_u64 -> Bits64
+ | TY_i64 -> Bits64
+ | TY_f32 -> Bits32
+ | TY_f64 -> Bits64
+;;
+
+let cell_scalar_ty (c:cell) : scalar_ty =
+ match c with
+ Reg (_, st) -> st
+ | Mem (_, ScalarTy st) -> st
+ | _ -> bug () "mem of non-scalar in Il.cell_scalar_ty"
+;;
+
+let operand_scalar_ty (op:operand) : scalar_ty =
+ match op with
+ Cell c -> cell_scalar_ty c
+ | Imm (_, t) -> ValTy (bits_of_ty_mach t)
+ | ImmPtr (_, t) -> AddrTy t
+;;
+
+
+let scalar_ty_bits (word_bits:bits) (st:scalar_ty) : bits =
+ match st with
+ ValTy bits -> bits
+ | AddrTy _ -> word_bits
+;;
+
+let cell_bits (word_bits:bits) (c:cell) : bits =
+ match c with
+ Reg (_, st) -> scalar_ty_bits word_bits st
+ | Mem (_, ScalarTy st) -> scalar_ty_bits word_bits st
+ | Mem _ -> bug () "mem of non-scalar in Il.cell_bits"
+;;
+
+let operand_bits (word_bits:bits) (op:operand) : bits =
+ match op with
+ Cell cell -> cell_bits word_bits cell
+ | Imm (_, tm) -> bits_of_ty_mach tm
+ | ImmPtr _ -> word_bits
+;;
+
+let bits_size (bits:bits) : int64 =
+ match bits with
+ Bits8 -> 1L
+ | Bits16 -> 2L
+ | Bits32 -> 4L
+ | Bits64 -> 8L
+;;
+
+let bits_align (bits:bits) : int64 =
+ match bits with
+ Bits8 -> 1L
+ | Bits16 -> 2L
+ | Bits32 -> 4L
+ | Bits64 -> 8L
+;;
+
+let scalar_ty_size (word_bits:bits) (st:scalar_ty) : int64 =
+ bits_size (scalar_ty_bits word_bits st)
+;;
+
+let scalar_ty_align (word_bits:bits) (st:scalar_ty) : int64 =
+ bits_align (scalar_ty_bits word_bits st)
+;;
+
+let rec referent_ty_layout (word_bits:bits) (rt:referent_ty) : (size * size) =
+ match rt with
+ ScalarTy st -> (SIZE_fixed (scalar_ty_size word_bits st),
+ SIZE_fixed (scalar_ty_align word_bits st))
+ | StructTy rts ->
+ begin
+ let accum (off,align) rt : (size * size) =
+ let (elt_size, elt_align) = referent_ty_layout word_bits rt in
+ let elt_off = align_sz elt_align off in
+ (add_sz elt_off elt_size, max_sz elt_align align)
+ in
+ Array.fold_left accum (SIZE_fixed 0L, SIZE_fixed 1L) rts
+ end
+ | UnionTy rts ->
+ begin
+ let accum (sz,align) rt : (size * size) =
+ let (elt_size, elt_align) = referent_ty_layout word_bits rt in
+ (max_sz sz elt_size, max_sz elt_align align)
+ in
+ Array.fold_left accum (SIZE_fixed 0L, SIZE_fixed 1L) rts
+ end
+ | OpaqueTy -> bug () "opaque ty in referent_ty_layout"
+ | CodeTy -> bug () "code ty in referent_ty_layout"
+ | ParamTy i -> (SIZE_param_size i, SIZE_param_align i)
+ | NilTy -> (SIZE_fixed 0L, SIZE_fixed 1L)
+
+and referent_ty_size (word_bits:bits) (rt:referent_ty) : size =
+ (fst (referent_ty_layout word_bits rt))
+
+and referent_ty_align (word_bits:bits) (rt:referent_ty) : size =
+ (snd (referent_ty_layout word_bits rt))
+
+;;
+
+let get_element_offset
+ (word_bits:bits)
+ (elts:referent_ty array)
+ (i:int)
+ : size =
+ let elts_before = Array.sub elts 0 i in
+ let elt_rty = elts.(i) in
+ let elts_before_size = referent_ty_size word_bits (StructTy elts_before) in
+ let elt_align = referent_ty_align word_bits elt_rty in
+ let elt_off = align_sz elt_align elts_before_size in
+ elt_off
+;;
+
+(* Processor. *)
+
+type quad_processor =
+ { qp_reg: (quad_processor -> reg -> reg);
+ qp_mem: (quad_processor -> mem -> mem);
+ qp_cell_read: (quad_processor -> cell -> cell);
+ qp_cell_write: (quad_processor -> cell -> cell);
+ qp_code: (quad_processor -> code -> code);
+ qp_op: (quad_processor -> operand -> operand); }
+;;
+
+let identity_processor =
+ let qp_cell = (fun qp c -> match c with
+ Reg (r, b) -> Reg (qp.qp_reg qp r, b)
+ | Mem (a, b) -> Mem (qp.qp_mem qp a, b))
+ in
+ { qp_reg = (fun _ r -> r);
+ qp_mem = (fun qp a -> match a with
+ RegIn (r, o) -> RegIn (qp.qp_reg qp r, o)
+ | Abs _
+ | Spill _ -> a);
+ qp_cell_read = qp_cell;
+ qp_cell_write = qp_cell;
+ qp_code = (fun qp c -> match c with
+ CodePtr op -> CodePtr (qp.qp_op qp op)
+ | CodeLabel _
+ | CodeNone -> c);
+ qp_op = (fun qp op -> match op with
+ Cell c -> Cell (qp.qp_cell_read qp c)
+ | ImmPtr _ -> op
+ | Imm _ -> op) }
+;;
+
+let process_quad (qp:quad_processor) (q:quad) : quad =
+ { q with
+ quad_body = match q.quad_body with
+ Binary b ->
+ Binary { b with
+ binary_dst = qp.qp_cell_write qp b.binary_dst;
+ binary_lhs = qp.qp_op qp b.binary_lhs;
+ binary_rhs = qp.qp_op qp b.binary_rhs }
+ | Unary u ->
+ Unary { u with
+ unary_dst = qp.qp_cell_write qp u.unary_dst;
+ unary_src = qp.qp_op qp u.unary_src }
+
+ | Lea le ->
+ Lea { lea_dst = qp.qp_cell_write qp le.lea_dst;
+ lea_src = qp.qp_op qp le.lea_src }
+
+ | Cmp c ->
+ Cmp { cmp_lhs = qp.qp_op qp c.cmp_lhs;
+ cmp_rhs = qp.qp_op qp c.cmp_rhs }
+
+ | Jmp j ->
+ Jmp { j with
+ jmp_targ = qp.qp_code qp j.jmp_targ }
+
+ | Push op ->
+ Push (qp.qp_op qp op)
+
+ | Pop c ->
+ Pop (qp.qp_cell_write qp c)
+
+ | Call c ->
+ Call { call_dst = qp.qp_cell_write qp c.call_dst;
+ call_targ = qp.qp_code qp c.call_targ }
+
+ | Ret -> Ret
+ | Nop -> Nop
+ | Debug -> Debug
+ | Regfence -> Regfence
+ | Enter f -> Enter f
+ | Leave -> Leave
+ | Dead -> Dead
+ | End -> End }
+;;
+
+let visit_quads (qp:quad_processor) (qs:quads) : unit =
+ Array.iter (fun x ->ignore ( process_quad qp x); ()) qs
+;;
+
+let process_quads (qp:quad_processor) (qs:quads) : quads =
+ Array.map (process_quad qp) qs
+;;
+
+let rewrite_quads (qp:quad_processor) (qs:quads) : unit =
+ for i = 0 to ((Array.length qs) - 1) do
+ qs.(i) <- process_quad qp qs.(i)
+ done
+;;
+
+
+(* A little partial-evaluator to help lowering sizes. *)
+
+let rec size_to_expr64 (a:size) : Asm.expr64 option =
+ let binary a b f =
+ match (size_to_expr64 a, size_to_expr64 b) with
+ (Some a, Some b) -> Some (f a b)
+ | _ -> None
+ in
+ match a with
+ SIZE_fixed i -> Some (Asm.IMM i)
+ | SIZE_fixup_mem_sz f -> Some (Asm.M_SZ f)
+ | SIZE_fixup_mem_pos f -> Some (Asm.M_POS f)
+ | SIZE_rt_neg s ->
+ begin
+ match (size_to_expr64 s) with
+ None -> None
+ | Some s -> Some (Asm.NEG s)
+ end
+ | SIZE_rt_add (a, b) -> binary a b (fun a b -> Asm.ADD (a,b))
+ | SIZE_rt_mul (a, b) -> binary a b (fun a b -> Asm.MUL (a,b))
+ | SIZE_rt_max (a, b) -> binary a b (fun a b -> Asm.MAX (a,b))
+ | SIZE_rt_align (a, b) -> binary a b (fun a b -> Asm.ALIGN (a,b))
+ | _ -> None
+;;
+
+
+(* Formatters. *)
+
+let string_of_bits (b:bits) : string =
+ match b with
+ Bits8 -> "b8"
+ | Bits16 -> "b16"
+ | Bits32 -> "b32"
+ | Bits64 -> "b64"
+;;
+
+let rec string_of_scalar_ty (s:scalar_ty) : string =
+ match s with
+ ValTy b -> (string_of_bits b)
+ | AddrTy r -> (string_of_referent_ty r) ^ "*"
+
+and string_of_referent_ty (r:referent_ty) : string =
+ match r with
+ ScalarTy s -> (string_of_scalar_ty s)
+ | StructTy rs ->
+ Printf.sprintf "[%s]"
+ (String.concat ","
+ (Array.to_list (Array.map string_of_referent_ty rs)))
+ | UnionTy rs ->
+ Printf.sprintf "(%s)"
+ (String.concat "|"
+ (Array.to_list (Array.map string_of_referent_ty rs)))
+ | ParamTy i -> Printf.sprintf "#%d" i
+ | OpaqueTy -> "?"
+ | CodeTy -> "!"
+ | NilTy -> "()"
+;;
+
+
+type hreg_formatter = hreg -> string;;
+
+let string_of_reg (f:hreg_formatter) (r:reg) : string =
+ match r with
+ Vreg i -> Printf.sprintf "<v%d>" i
+ | Hreg i -> f i
+;;
+
+let rec string_of_expr64 (e64:Asm.expr64) : string =
+ let bin op a b =
+ Printf.sprintf "(%s %s %s)" (string_of_expr64 a) op (string_of_expr64 b)
+ in
+ let bini op a b =
+ Printf.sprintf "(%s %s %d)" (string_of_expr64 a) op b
+ in
+ match e64 with
+ Asm.IMM i when (i64_lt i 0L) -> Printf.sprintf "-0x%Lx" (Int64.neg i)
+ | Asm.IMM i -> Printf.sprintf "0x%Lx" i
+ | Asm.ADD (a,b) -> bin "+" a b
+ | Asm.SUB (a,b) -> bin "-" a b
+ | Asm.MUL (a,b) -> bin "*" a b
+ | Asm.DIV (a,b) -> bin "/" a b
+ | Asm.REM (a,b) -> bin "%" a b
+ | Asm.MAX (a,b) ->
+ Printf.sprintf "(max %s %s)"
+ (string_of_expr64 a) (string_of_expr64 b)
+ | Asm.ALIGN (a,b) ->
+ Printf.sprintf "(align %s %s)"
+ (string_of_expr64 a) (string_of_expr64 b)
+ | Asm.SLL (a,b) -> bini "<<" a b
+ | Asm.SLR (a,b) -> bini ">>" a b
+ | Asm.SAR (a,b) -> bini ">>>" a b
+ | Asm.AND (a,b) -> bin "&" a b
+ | Asm.XOR (a,b) -> bin "xor" a b
+ | Asm.OR (a,b) -> bin "|" a b
+ | Asm.NOT a -> Printf.sprintf "(not %s)" (string_of_expr64 a)
+ | Asm.NEG a -> Printf.sprintf "-%s" (string_of_expr64 a)
+ | Asm.F_POS f -> Printf.sprintf "<%s>.fpos" f.fixup_name
+ | Asm.F_SZ f -> Printf.sprintf "<%s>.fsz" f.fixup_name
+ | Asm.M_POS f -> Printf.sprintf "<%s>.mpos" f.fixup_name
+ | Asm.M_SZ f -> Printf.sprintf "<%s>.msz" f.fixup_name
+ | Asm.EXT _ -> "??ext??"
+;;
+
+let string_of_off (e:Asm.expr64 option) : string =
+ match e with
+ None -> ""
+ | Some (Asm.IMM i) when (i64_lt i 0L) ->
+ Printf.sprintf " - 0x%Lx" (Int64.neg i)
+ | Some e' -> " + " ^ (string_of_expr64 e')
+;;
+
+let string_of_mem (f:hreg_formatter) (a:mem) : string =
+ match a with
+ Abs e ->
+ Printf.sprintf "[%s]" (string_of_expr64 e)
+ | RegIn (r, off) ->
+ Printf.sprintf "[%s%s]" (string_of_reg f r) (string_of_off off)
+ | Spill i ->
+ Printf.sprintf "[<spill %d>]" i
+;;
+let string_of_cell (f:hreg_formatter) (c:cell) : string =
+ match c with
+ Reg (r,ty) ->
+ if !log_iltypes
+ then
+ Printf.sprintf "%s:%s" (string_of_reg f r) (string_of_scalar_ty ty)
+ else
+ Printf.sprintf "%s" (string_of_reg f r)
+ | Mem (a,ty) ->
+ if !log_iltypes
+ then
+ Printf.sprintf "%s:%s"
+ (string_of_mem f a) (string_of_referent_ty ty)
+ else
+ Printf.sprintf "%s" (string_of_mem f a)
+;;
+
+let string_of_operand (f:hreg_formatter) (op:operand) : string =
+ match op with
+ Cell c -> string_of_cell f c
+ | ImmPtr (f, ty) ->
+ if !log_iltypes
+ then
+ Printf.sprintf "$<%s>.mpos:%s*"
+ f.fixup_name (string_of_referent_ty ty)
+ else
+ Printf.sprintf "$<%s>.mpos" f.fixup_name
+ | Imm (i, ty) ->
+ if !log_iltypes
+ then
+ Printf.sprintf "$%s:%s" (string_of_expr64 i) (string_of_ty_mach ty)
+ else
+ Printf.sprintf "$%s" (string_of_expr64 i)
+;;
+
+
+let string_of_code (f:hreg_formatter) (c:code) : string =
+ match c with
+ CodeLabel lab -> Printf.sprintf "<label %d>" lab
+ | CodePtr op -> string_of_operand f op
+ | CodeNone -> "<none>"
+;;
+
+
+let string_of_binop (op:binop) : string =
+ match op with
+ ADD -> "add"
+ | SUB -> "sub"
+ | IMUL -> "imul"
+ | UMUL -> "umul"
+ | IDIV -> "idiv"
+ | UDIV -> "udiv"
+ | IMOD -> "imod"
+ | UMOD -> "umod"
+ | AND -> "and"
+ | OR -> "or"
+ | XOR -> "xor"
+ | LSL -> "lsl"
+ | LSR -> "lsr"
+ | ASR -> "asr"
+;;
+
+let string_of_unop (op:unop) : string =
+ match op with
+ NEG -> "neg"
+ | NOT -> "not"
+ | UMOV -> "umov"
+ | IMOV -> "imov"
+ | ZERO -> "zero"
+;;
+
+let string_of_jmpop (op:jmpop) : string =
+ match op with
+ JE -> "je"
+ | JNE -> "jne"
+ | JL -> "jl"
+ | JLE -> "jle"
+ | JG -> "jg"
+ | JGE -> "jge"
+ | JB -> "jb"
+ | JBE -> "jbe"
+ | JA -> "ja"
+ | JAE -> "jae"
+ | JC -> "jc"
+ | JNC ->"jnc"
+ | JO -> "jo"
+ | JNO -> "jno"
+ | JZ -> "jz"
+ | JNZ ->"jnz"
+ | JMP -> "jmp"
+;;
+
+let string_of_quad (f:hreg_formatter) (q:quad) : string =
+ match q.quad_body with
+ Binary b ->
+ Printf.sprintf "%s = %s %s %s"
+ (string_of_cell f b.binary_dst)
+ (string_of_operand f b.binary_lhs)
+ (string_of_binop b.binary_op)
+ (string_of_operand f b.binary_rhs)
+
+ | Unary u ->
+ Printf.sprintf "%s = %s %s"
+ (string_of_cell f u.unary_dst)
+ (string_of_unop u.unary_op)
+ (string_of_operand f u.unary_src)
+
+ | Cmp c ->
+ Printf.sprintf "cmp %s %s"
+ (string_of_operand f c.cmp_lhs)
+ (string_of_operand f c.cmp_rhs)
+
+ | Lea le ->
+ Printf.sprintf "lea %s %s"
+ (string_of_cell f le.lea_dst)
+ (string_of_operand f le.lea_src)
+
+ | Jmp j ->
+ Printf.sprintf "%s %s"
+ (string_of_jmpop j.jmp_op)
+ (string_of_code f j.jmp_targ)
+
+ | Push op ->
+ Printf.sprintf "push %s"
+ (string_of_operand f op)
+
+ | Pop c ->
+ Printf.sprintf "%s = pop"
+ (string_of_cell f c)
+
+ | Call c ->
+ Printf.sprintf "%s = call %s"
+ (string_of_cell f c.call_dst)
+ (string_of_code f c.call_targ)
+
+ | Ret -> "ret"
+ | Nop -> "nop"
+ | Dead -> "dead"
+ | Debug -> "debug"
+ | Regfence -> "regfence"
+ | Enter _ -> "enter lexical block"
+ | Leave -> "leave lexical block"
+ | End -> "---"
+;;
+
+
+
+(* Emitters. *)
+
+
+type emitter = { mutable emit_pc: int;
+ mutable emit_next_vreg: int option;
+ mutable emit_next_spill: int;
+ emit_preallocator: (quad' -> quad');
+ emit_is_2addr: bool;
+ mutable emit_quads: quads;
+ emit_annotations: (int,string) Hashtbl.t;
+ emit_size_cache: ((size,operand) Hashtbl.t) Stack.t;
+ emit_node: node_id option;
+ }
+
+
+let badq = { quad_fixup = None;
+ quad_implicits = [];
+ quad_body = End }
+;;
+
+
+let deadq = { quad_fixup = None;
+ quad_implicits = [];
+ quad_body = Dead }
+;;
+
+
+let new_emitter
+ (preallocator:quad' -> quad')
+ (is_2addr:bool)
+ (vregs_ok:bool)
+ (node:node_id option)
+ : emitter =
+ {
+ emit_pc = 0;
+ emit_next_vreg = (if vregs_ok then Some 0 else None);
+ emit_next_spill = 0;
+ emit_preallocator = preallocator;
+ emit_is_2addr = is_2addr;
+ emit_quads = Array.create 4 badq;
+ emit_annotations = Hashtbl.create 0;
+ emit_size_cache = Stack.create ();
+ emit_node = node;
+ }
+;;
+
+
+let num_vregs (e:emitter) : int =
+ match e.emit_next_vreg with
+ None -> 0
+ | Some i -> i
+;;
+
+let next_vreg_num (e:emitter) : vreg =
+ match e.emit_next_vreg with
+ None -> bug () "Il.next_vreg_num on non-vreg emitter"
+ | Some i ->
+ e.emit_next_vreg <- Some (i + 1);
+ i
+;;
+
+let next_vreg (e:emitter) : reg =
+ Vreg (next_vreg_num e)
+;;
+
+let next_vreg_cell (e:emitter) (s:scalar_ty) : cell =
+ Reg ((next_vreg e), s)
+;;
+
+let next_spill (e:emitter) : spill =
+ let i = e.emit_next_spill in
+ e.emit_next_spill <- i + 1;
+ i
+;;
+
+let next_spill_slot (e:emitter) (r:referent_ty) : typed_mem =
+ (Spill (next_spill e), r);
+;;
+
+
+let grow_if_necessary e =
+ let len = Array.length e.emit_quads in
+ if e.emit_pc >= len - 1
+ then
+ let n = Array.create (2 * len) badq in
+ Array.blit e.emit_quads 0 n 0 len;
+ e.emit_quads <- n
+;;
+
+
+let binary (op:binop) (dst:cell) (lhs:operand) (rhs:operand) : quad' =
+ Binary { binary_op = op;
+ binary_dst = dst;
+ binary_lhs = lhs;
+ binary_rhs = rhs }
+;;
+
+let unary (op:unop) (dst:cell) (src:operand) : quad' =
+ Unary { unary_op = op;
+ unary_dst = dst;
+ unary_src = src }
+
+let jmp (op:jmpop) (targ:code) : quad' =
+ Jmp { jmp_op = op;
+ jmp_targ = targ; }
+;;
+
+
+let lea (dst:cell) (src:operand) : quad' =
+ Lea { lea_dst = dst;
+ lea_src = src; }
+;;
+
+let cmp (lhs:operand) (rhs:operand) : quad' =
+ Cmp { cmp_lhs = lhs;
+ cmp_rhs = rhs; }
+;;
+
+let call (dst:cell) (targ:code) : quad' =
+ Call { call_dst = dst;
+ call_targ = targ; }
+;;
+
+let umov (dst:cell) (src:operand) : quad' =
+ if (cell_is_nil dst || operand_is_nil src)
+ then Dead
+ else unary UMOV dst src
+;;
+
+let zero (dst:cell) (count:operand) : quad' =
+ unary ZERO dst count
+;;
+
+let is_mov uop =
+ match uop with
+ UMOV | IMOV -> true
+ | _ -> false
+;;
+
+let mk_quad (q':quad') : quad =
+ { quad_body = q';
+ quad_implicits = [];
+ quad_fixup = None }
+;;
+
+let emit_full
+ (e:emitter)
+ (fix:fixup option)
+ (implicits:label list)
+ (q':quad')
+ : unit =
+ let fixup = ref fix in
+ let emit_quad_bottom q' =
+ grow_if_necessary e;
+ e.emit_quads.(e.emit_pc) <- { quad_body = q';
+ quad_implicits = implicits;
+ quad_fixup = (!fixup) };
+ fixup := None;
+ e.emit_pc <- e.emit_pc + 1
+ in
+
+ let emit_quad (q':quad') : unit =
+ (* re-decay any freshly generated mem-mem movs. *)
+ match q' with
+ Unary { unary_dst = Mem (dst_mem, ScalarTy src_st);
+ unary_src = Cell (Mem (src_mem, ScalarTy dst_st));
+ unary_op = op }
+ when is_mov op ->
+ let v = next_vreg_cell e dst_st in
+ emit_quad_bottom
+ (unary op v (Cell (Mem (src_mem, ScalarTy src_st))));
+ emit_quad_bottom
+ (unary op (Mem (dst_mem, ScalarTy dst_st)) (Cell v))
+ | _ -> emit_quad_bottom q'
+ in
+
+ let default_mov =
+ match q' with
+ Binary b ->
+ begin
+ match b.binary_op with
+ IDIV | IMUL | IMOD -> IMOV
+ | _ -> UMOV
+ end
+ | Unary u ->
+ begin
+ match u.unary_op with
+ IMOV -> IMOV
+ | _ -> UMOV
+ end
+ | _ -> UMOV
+ in
+
+ let emit_mov (dst:cell) (src:operand) : unit =
+ emit_quad (unary default_mov dst src)
+ in
+
+ let mov_if_operands_differ
+ (old_op:operand) (new_op:operand)
+ : unit =
+ if (new_op <> old_op)
+ then
+ match new_op with
+ (Cell new_cell) ->
+ emit_mov new_cell old_op
+ | _ -> ()
+ in
+
+ let mov_if_two_operands_differ
+ (old_lhs_op:operand) (new_lhs_op:operand)
+ (old_rhs_op:operand) (new_rhs_op:operand)
+ : unit =
+ (*
+ * This is sufficiently obscure that it deserves an explanation.
+ *
+ * The main idea here is to do two "mov_if_operands_differ" calls,
+ * such as one might have when setting up a binary quad.
+ *
+ * The problem comes when you happen to hit a case like X86 div,
+ * which preallocates *both* operands. Preallocating both means we
+ * have to potentially issue two movs into the preallocated regs,
+ * and the second of those movs might be a problem. Specifically:
+ * the second mov-to-prealloc might make be moving from a
+ * register-indirect mem cell based on a vreg, and that vreg may
+ * wind up being assigned to an hreg that we just loaded with the
+ * first mov. In other words, the second mov may retask the
+ * preallocated hreg we set up in the first mov.
+ *
+ * You laugh, but of course this actually happens.
+ *
+ * So here we do a conservative thing and check to see if either
+ * operand is memory-indirect at all. If either is, then for either
+ * of the 'old' operands we're *about* to mov into a prealloc reg,
+ * we first bounce them off a spill slot. Spill slots, thankfully,
+ * we can always count on being able to address irrespective of the
+ * opinions of the RA, as they are all just fp-relative.
+ *
+ * A slightly more aggressive version of this would only bounce
+ * cases that are not fp-relative already, though doing so would
+ * require threading the notion of what fp *is* through to
+ * here. Possibly tighten this up in the future (or just
+ * ... destroy this backend ASAP).
+ *
+ *)
+ let has_reg_indirect op =
+ match op with
+ Cell (Mem _) -> true
+ | _ -> false
+ in
+ let either_old_op_has_reg_indirect =
+ (has_reg_indirect old_lhs_op) || (has_reg_indirect old_rhs_op)
+ in
+ let old_lhs_op =
+ if either_old_op_has_reg_indirect && (new_lhs_op <> old_lhs_op)
+ then
+ let tmp =
+ Mem (next_spill_slot e
+ (ScalarTy (operand_scalar_ty old_lhs_op)))
+ in
+ emit_mov tmp old_lhs_op;
+ Cell tmp
+ else
+ old_lhs_op
+ in
+ let old_rhs_op =
+ if either_old_op_has_reg_indirect && (new_rhs_op <> old_rhs_op)
+ then
+ let tmp =
+ Mem (next_spill_slot e
+ (ScalarTy (operand_scalar_ty old_rhs_op)))
+ in
+ emit_mov tmp old_rhs_op;
+ Cell tmp
+ else
+ old_rhs_op
+ in
+ mov_if_operands_differ old_lhs_op new_lhs_op;
+ mov_if_operands_differ old_rhs_op new_rhs_op;
+ in
+
+ let mov_if_cells_differ (old_cell:cell) (new_cell:cell) : unit =
+ if not (new_cell = old_cell)
+ then
+ emit_mov old_cell (Cell new_cell)
+ in
+
+ let emit_decayed_quad q' =
+ match (q', e.emit_preallocator q') with
+ (Binary b, Binary b') ->
+ begin
+ mov_if_two_operands_differ
+ b.binary_lhs b'.binary_lhs
+ b.binary_rhs b'.binary_rhs;
+ if e.emit_is_2addr &&
+ (not (b'.binary_lhs = (Cell b'.binary_dst)))
+ then
+ begin
+ emit_mov b'.binary_dst b'.binary_lhs;
+ emit_quad (Binary { b' with
+ binary_lhs = (Cell b'.binary_dst) })
+ end
+ else
+ emit_quad (Binary b');
+ mov_if_cells_differ b.binary_dst b'.binary_dst
+ end
+
+ | (Unary u, Unary u') ->
+ mov_if_operands_differ u.unary_src u'.unary_src;
+ (* Assume '2addr' means '1addr' for unary ops. *)
+ if e.emit_is_2addr &&
+ (u'.unary_op = NEG || u'.unary_op = NOT) &&
+ (not (u'.unary_src = (Cell u'.unary_dst)))
+ then
+ begin
+ emit_mov u'.unary_dst u'.unary_src;
+ emit_quad (Unary { u' with unary_src = (Cell u'.unary_dst) })
+ end
+ else
+ emit_quad (Unary u');
+ mov_if_cells_differ u.unary_dst u'.unary_dst
+
+ | (Cmp c, Cmp c') ->
+ mov_if_two_operands_differ
+ c.cmp_lhs c'.cmp_lhs
+ c.cmp_rhs c'.cmp_rhs;
+ emit_quad (Cmp c');
+
+ | (Push op, Push op') ->
+ mov_if_operands_differ op op';
+ emit_quad (Push op');
+
+ | (Pop c, Pop c') ->
+ emit_quad (Pop c');
+ mov_if_cells_differ c c'
+
+ | (Call c, Call c') ->
+ emit_quad (Call c');
+ mov_if_cells_differ c.call_dst c'.call_dst
+
+ | (Lea lea, Lea lea') ->
+ emit_quad (Lea lea');
+ mov_if_cells_differ lea.lea_dst lea'.lea_dst
+
+ | (x, y) ->
+ assert (x = y);
+ emit_quad x
+ in
+
+ (* pre-decay mem-mem movs. *)
+ match q' with
+ Unary { unary_dst = Mem (dst_mem, ScalarTy src_st);
+ unary_src = Cell (Mem (src_mem, ScalarTy dst_st));
+ unary_op = op }
+ when is_mov op ->
+ let v = next_vreg_cell e dst_st in
+ emit_decayed_quad
+ (unary op v (Cell (Mem (src_mem, ScalarTy src_st))));
+ emit_decayed_quad
+ (unary op (Mem (dst_mem, ScalarTy dst_st)) (Cell v))
+ | _ -> emit_decayed_quad q'
+;;
+
+let emit (e:emitter) (q':quad') : unit =
+ emit_full e None [] q'
+;;
+
+let patch_jump (e:emitter) (jmp:int) (targ:int) : unit =
+ let q = e.emit_quads.(jmp) in
+ match q.quad_body with
+ Jmp j ->
+ assert (j.jmp_targ = CodeNone);
+ e.emit_quads.(jmp) <-
+ { q with quad_body =
+ Jmp { j with jmp_targ = CodeLabel targ } }
+ | _ -> ()
+;;
+
+(* More query functions. *)
+
+let get_element_ptr
+ (word_bits:bits)
+ (fmt:hreg_formatter)
+ (mem_cell:cell)
+ (i:int)
+ : cell =
+ match mem_cell with
+ Mem (mem, StructTy elts) when i >= 0 && i < (Array.length elts) ->
+ assert ((Array.length elts) != 0);
+ begin
+ let elt_rty = elts.(i) in
+ let elt_off = get_element_offset word_bits elts i in
+ match elt_off with
+ SIZE_fixed fixed_off ->
+ Mem (mem_off_imm mem fixed_off, elt_rty)
+ | _ -> bug ()
+ "get_element_ptr %d on dynamic-size cell: offset %s"
+ i (string_of_size elt_off)
+ end
+
+ | _ -> bug () "get_element_ptr %d on cell %s" i
+ (string_of_cell fmt mem_cell)
+;;
+
+(*
+ * 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/be/macho.ml b/src/boot/be/macho.ml
new file mode 100644
index 00000000..7fccdfd3
--- /dev/null
+++ b/src/boot/be/macho.ml
@@ -0,0 +1,1184 @@
+open Asm;;
+open Common;;
+
+(* Mach-O writer. *)
+
+let log (sess:Session.sess) =
+ Session.log "obj (mach-o)"
+ sess.Session.sess_log_obj
+ sess.Session.sess_log_out
+;;
+
+let iflog (sess:Session.sess) (thunk:(unit -> unit)) : unit =
+ if sess.Session.sess_log_obj
+ then thunk ()
+ else ()
+;;
+
+let (cpu_arch_abi64:int64) = 0x01000000L
+;;
+
+let (mh_magic:int64) = 0xfeedfaceL
+;;
+
+let cpu_subtype_intel (f:int64) (m:int64) : int64 =
+ Int64.add f (Int64.shift_left m 4)
+;;
+
+type cpu_type =
+ (* Maybe support more later. *)
+ CPU_TYPE_X86
+ | CPU_TYPE_X86_64
+ | CPU_TYPE_ARM
+ | CPU_TYPE_POWERPC
+;;
+
+type cpu_subtype =
+ (* Maybe support more later. *)
+ CPU_SUBTYPE_X86_ALL
+ | CPU_SUBTYPE_X86_64_ALL
+ | CPU_SUBTYPE_ARM_ALL
+ | CPU_SUBTYPE_POWERPC_ALL
+;;
+
+type file_type =
+ MH_OBJECT
+ | MH_EXECUTE
+ | MH_FVMLIB
+ | MH_CORE
+ | MH_PRELOAD
+ | MH_DYLIB
+ | MH_DYLINKER
+ | MH_BUNDLE
+ | MH_DYLIB_STUB
+ | MH_DSYM
+;;
+
+let file_type_code (ft:file_type) : int64 =
+ match ft with
+ MH_OBJECT ->0x1L (* object *)
+ | MH_EXECUTE -> 0x2L (* executable *)
+ | MH_FVMLIB -> 0x3L (* fixed-VM shared lib *)
+ | MH_CORE -> 0x4L (* core *)
+ | MH_PRELOAD -> 0x5L (* preloaded executable *)
+ | MH_DYLIB -> 0x6L (* dynamic lib *)
+ | MH_DYLINKER -> 0x7L (* dynamic linker *)
+ | MH_BUNDLE -> 0x8L (* bundle *)
+ | MH_DYLIB_STUB -> 0x9L (* shared lib stub *)
+ | MH_DSYM -> 0xaL (* debuginfo only *)
+;;
+
+type file_flag =
+ MH_NOUNDEFS
+ | MH_INCRLINK
+ | MH_DYLDLINK
+ | MH_BINDATLOAD
+ | MH_PREBOUND
+ | MH_SPLIT_SEGS
+ | MH_LAZY_INIT
+ | MH_TWOLEVEL
+ | MH_FORCE_FLAT
+ | MH_NOMULTIDEFS
+ | MH_NOFIXPREBINDING
+ | MH_PREBINDABLE
+ | MH_ALLMODSBOUND
+ | MH_SUBSECTIONS_VIA_SYMBOLS
+ | MH_CANONICAL
+ | MH_WEAK_DEFINES
+ | MH_BINDS_TO_WEAK
+ | MH_ALLOW_STACK_EXECUTION
+ | MH_ROOT_SAFE
+ | MH_SETUID_SAFE
+ | MH_NO_REEXPORTED_DYLIBS
+ | MH_PIE
+;;
+
+let file_flag_code (ff:file_flag) : int64 =
+ match ff with
+ MH_NOUNDEFS -> 0x1L
+ | MH_INCRLINK -> 0x2L
+ | MH_DYLDLINK -> 0x4L
+ | MH_BINDATLOAD -> 0x8L
+ | MH_PREBOUND -> 0x10L
+ | MH_SPLIT_SEGS -> 0x20L
+ | MH_LAZY_INIT -> 0x40L
+ | MH_TWOLEVEL -> 0x80L
+ | MH_FORCE_FLAT -> 0x100L
+ | MH_NOMULTIDEFS -> 0x200L
+ | MH_NOFIXPREBINDING -> 0x400L
+ | MH_PREBINDABLE -> 0x800L
+ | MH_ALLMODSBOUND -> 0x1000L
+ | MH_SUBSECTIONS_VIA_SYMBOLS -> 0x2000L
+ | MH_CANONICAL -> 0x4000L
+ | MH_WEAK_DEFINES -> 0x8000L
+ | MH_BINDS_TO_WEAK -> 0x10000L
+ | MH_ALLOW_STACK_EXECUTION -> 0x20000L
+ | MH_ROOT_SAFE -> 0x40000L
+ | MH_SETUID_SAFE -> 0x80000L
+ | MH_NO_REEXPORTED_DYLIBS -> 0x100000L
+ | MH_PIE -> 0x200000L
+;;
+
+
+type vm_prot =
+ VM_PROT_NONE
+ | VM_PROT_READ
+ | VM_PROT_WRITE
+ | VM_PROT_EXECUTE
+;;
+
+
+type load_command =
+ LC_SEGMENT
+ | LC_SYMTAB
+ | LC_SYMSEG
+ | LC_THREAD
+ | LC_UNIXTHREAD
+ | LC_LOADFVMLIB
+ | LC_IDFVMLIB
+ | LC_IDENT
+ | LC_FVMFILE
+ | LC_PREPAGE
+ | LC_DYSYMTAB
+ | LC_LOAD_DYLIB
+ | LC_ID_DYLIB
+ | LC_LOAD_DYLINKER
+ | LC_ID_DYLINKER
+ | LC_PREBOUND_DYLIB
+ | LC_ROUTINES
+ | LC_SUB_FRAMEWORK
+ | LC_SUB_UMBRELLA
+ | LC_SUB_CLIENT
+ | LC_SUB_LIBRARY
+ | LC_TWOLEVEL_HINTS
+ | LC_PREBIND_CKSUM
+ | LC_LOAD_WEAK_DYLIB
+ | LC_SEGMENT_64
+ | LC_ROUTINES_64
+ | LC_UUID
+ | LC_RPATH
+ | LC_CODE_SIGNATURE
+ | LC_SEGMENT_SPLIT_INFO
+ | LC_REEXPORT_DYLIB
+ | LC_LAZY_LOAD_DYLIB
+ | LC_ENCRYPTION_INFO
+;;
+
+
+let cpu_type_code (cpu:cpu_type) : int64 =
+ match cpu with
+ CPU_TYPE_X86 -> 7L
+ | CPU_TYPE_X86_64 -> Int64.logor 7L cpu_arch_abi64
+ | CPU_TYPE_ARM -> 12L
+ | CPU_TYPE_POWERPC -> 18L
+;;
+
+let cpu_subtype_code (cpu:cpu_subtype) : int64 =
+ match cpu with
+ CPU_SUBTYPE_X86_ALL -> 3L
+ | CPU_SUBTYPE_X86_64_ALL -> 3L
+ | CPU_SUBTYPE_ARM_ALL -> 0L
+ | CPU_SUBTYPE_POWERPC_ALL -> 0L
+;;
+
+
+let vm_prot_code (vmp:vm_prot) : int64 =
+ match vmp with
+ VM_PROT_NONE -> 0L
+ | VM_PROT_READ -> 1L
+ | VM_PROT_WRITE -> 2L
+ | VM_PROT_EXECUTE -> 4L
+;;
+
+
+let lc_req_dyld = 0x80000000L;;
+
+let load_command_code (lc:load_command) =
+ match lc with
+ | LC_SEGMENT -> 0x1L
+ | LC_SYMTAB -> 0x2L
+ | LC_SYMSEG -> 0x3L
+ | LC_THREAD -> 0x4L
+ | LC_UNIXTHREAD -> 0x5L
+ | LC_LOADFVMLIB -> 0x6L
+ | LC_IDFVMLIB -> 0x7L
+ | LC_IDENT -> 0x8L
+ | LC_FVMFILE -> 0x9L
+ | LC_PREPAGE -> 0xaL
+ | LC_DYSYMTAB -> 0xbL
+ | LC_LOAD_DYLIB -> 0xcL
+ | LC_ID_DYLIB -> 0xdL
+ | LC_LOAD_DYLINKER -> 0xeL
+ | LC_ID_DYLINKER -> 0xfL
+ | LC_PREBOUND_DYLIB -> 0x10L
+ | LC_ROUTINES -> 0x11L
+ | LC_SUB_FRAMEWORK -> 0x12L
+ | LC_SUB_UMBRELLA -> 0x13L
+ | LC_SUB_CLIENT -> 0x14L
+ | LC_SUB_LIBRARY -> 0x15L
+ | LC_TWOLEVEL_HINTS -> 0x16L
+ | LC_PREBIND_CKSUM -> 0x17L
+ | LC_LOAD_WEAK_DYLIB -> Int64.logor lc_req_dyld 0x18L
+ | LC_SEGMENT_64 -> 0x19L
+ | LC_ROUTINES_64 -> 0x1aL
+ | LC_UUID -> 0x1bL
+ | LC_RPATH -> Int64.logor lc_req_dyld 0x1cL
+ | LC_CODE_SIGNATURE -> 0x1dL
+ | LC_SEGMENT_SPLIT_INFO -> 0x1eL
+ | LC_REEXPORT_DYLIB -> Int64.logor lc_req_dyld 0x1fL
+ | LC_LAZY_LOAD_DYLIB -> 0x20L
+ | LC_ENCRYPTION_INFO -> 0x21L
+;;
+
+
+let fixed_sz_string (sz:int) (str:string) : frag =
+ if String.length str > sz
+ then STRING (String.sub str 0 sz)
+ else SEQ [| STRING str; PAD (sz - (String.length str)) |]
+;;
+
+type sect_type =
+ S_REGULAR
+ | S_ZEROFILL
+ | S_CSTRING_LITERALS
+ | S_4BYTE_LITERALS
+ | S_8BYTE_LITERALS
+ | S_LITERAL_POINTERS
+ | S_NON_LAZY_SYMBOL_POINTERS
+ | S_LAZY_SYMBOL_POINTERS
+ | S_SYMBOL_STUBS
+ | S_MOD_INIT_FUNC_POINTERS
+ | S_MOD_TERM_FUNC_POINTERS
+ | S_COALESCED
+ | S_GB_ZEROFILL
+ | S_INTERPOSING
+ | S_16BYTE_LITERALS
+ | S_DTRACE_DOF
+ | S_LAZY_DYLIB_SYMBOL_POINTERS
+;;
+
+let sect_type_code (s:sect_type) : int64 =
+ match s with
+ S_REGULAR -> 0x0L
+ | S_ZEROFILL -> 0x1L
+ | S_CSTRING_LITERALS -> 0x2L
+ | S_4BYTE_LITERALS -> 0x3L
+ | S_8BYTE_LITERALS -> 0x4L
+ | S_LITERAL_POINTERS -> 0x5L
+ | S_NON_LAZY_SYMBOL_POINTERS -> 0x6L
+ | S_LAZY_SYMBOL_POINTERS -> 0x7L
+ | S_SYMBOL_STUBS -> 0x8L
+ | S_MOD_INIT_FUNC_POINTERS -> 0x9L
+ | S_MOD_TERM_FUNC_POINTERS -> 0xaL
+ | S_COALESCED -> 0xbL
+ | S_GB_ZEROFILL -> 0xcL
+ | S_INTERPOSING -> 0xdL
+ | S_16BYTE_LITERALS -> 0xeL
+ | S_DTRACE_DOF -> 0xfL
+ | S_LAZY_DYLIB_SYMBOL_POINTERS -> 0x10L
+;;
+
+type sect_attr =
+ S_ATTR_PURE_INSTRUCTIONS
+ | S_ATTR_NO_TOC
+ | S_ATTR_STRIP_STATIC_SYMS
+ | S_ATTR_NO_DEAD_STRIP
+ | S_ATTR_LIVE_SUPPORT
+ | S_ATTR_SELF_MODIFYING_CODE
+ | S_ATTR_DEBUG
+ | S_ATTR_SOME_INSTRUCTIONS
+ | S_ATTR_EXT_RELOC
+ | S_ATTR_LOC_RELOC
+;;
+
+let sect_attr_code (s:sect_attr) : int64 =
+ match s with
+ S_ATTR_PURE_INSTRUCTIONS -> 0x80000000L
+ | S_ATTR_NO_TOC -> 0x40000000L
+ | S_ATTR_STRIP_STATIC_SYMS -> 0x20000000L
+ | S_ATTR_NO_DEAD_STRIP -> 0x10000000L
+ | S_ATTR_LIVE_SUPPORT -> 0x08000000L
+ | S_ATTR_SELF_MODIFYING_CODE -> 0x04000000L
+ | S_ATTR_DEBUG -> 0x02000000L
+ | S_ATTR_SOME_INSTRUCTIONS -> 0x00000400L
+ | S_ATTR_EXT_RELOC -> 0x00000200L
+ | S_ATTR_LOC_RELOC -> 0x00000100L
+;;
+
+type n_type =
+ | N_EXT
+ | N_UNDF
+ | N_ABS
+ | N_SECT
+ | N_PBUD
+ | N_INDIR
+;;
+
+let n_type_code (n:n_type) : int64 =
+ match n with
+ N_EXT -> 0x1L
+ | N_UNDF -> 0x0L
+ | N_ABS -> 0x2L
+ | N_SECT -> 0xeL
+ | N_PBUD -> 0xcL
+ | N_INDIR -> 0xaL
+;;
+
+
+type n_desc_reference_type =
+ REFERENCE_FLAG_UNDEFINED_NON_LAZY
+ | REFERENCE_FLAG_UNDEFINED_LAZY
+ | REFERENCE_FLAG_DEFINED
+ | REFERENCE_FLAG_PRIVATE_DEFINED
+ | REFERENCE_FLAG_PRIVATE_UNDEFINED_NON_LAZY
+ | REFERENCE_FLAG_PRIVATE_UNDEFINED_LAZY
+;;
+
+let n_desc_reference_type_code (n:n_desc_reference_type) : int64 =
+ match n with
+ REFERENCE_FLAG_UNDEFINED_NON_LAZY -> 0x0L
+ | REFERENCE_FLAG_UNDEFINED_LAZY -> 0x1L
+ | REFERENCE_FLAG_DEFINED -> 0x2L
+ | REFERENCE_FLAG_PRIVATE_DEFINED -> 0x3L
+ | REFERENCE_FLAG_PRIVATE_UNDEFINED_NON_LAZY -> 0x4L
+ | REFERENCE_FLAG_PRIVATE_UNDEFINED_LAZY -> 0x5L
+;;
+
+type n_desc_flags =
+ REFERENCED_DYNAMICALLY
+ | N_DESC_DISCARDED
+ | N_NO_DEAD_STRIP
+ | N_WEAK_REF
+ | N_WEAK_DEF
+;;
+
+let n_desc_flags_code (n:n_desc_flags) : int64 =
+ match n with
+ REFERENCED_DYNAMICALLY -> 0x10L
+ | N_DESC_DISCARDED -> 0x20L
+ | N_NO_DEAD_STRIP -> 0x20L (* Yes, they reuse 0x20. *)
+ | N_WEAK_REF -> 0x40L
+ | N_WEAK_DEF -> 0x80L
+;;
+
+type n_desc_dylib_ordinal = int;;
+
+type n_desc = (n_desc_dylib_ordinal *
+ (n_desc_flags list) *
+ n_desc_reference_type)
+;;
+
+let n_desc_code (n:n_desc) : int64 =
+ let (dylib_ordinal, flags, ty) = n in
+ Int64.logor
+ (Int64.of_int (dylib_ordinal lsl 8))
+ (Int64.logor
+ (fold_flags n_desc_flags_code flags)
+ (n_desc_reference_type_code ty))
+;;
+
+
+let macho_section_command
+ (seg_name:string)
+ (sect:(string * int * (sect_attr list) * sect_type * fixup))
+ : frag =
+ let (sect_name, sect_align, sect_attrs, sect_type, sect_fixup) = sect in
+ SEQ [|
+ fixed_sz_string 16 sect_name;
+ fixed_sz_string 16 seg_name;
+ WORD (TY_u32, M_POS sect_fixup);
+ WORD (TY_u32, M_SZ sect_fixup);
+ WORD (TY_u32, F_POS sect_fixup);
+ WORD (TY_u32, IMM (Int64.of_int sect_align));
+ WORD (TY_u32, IMM 0L); (* reloff *)
+ WORD (TY_u32, IMM 0L); (* nreloc *)
+ WORD (TY_u32, (IMM (Int64.logor (* flags (and attrs) *)
+ (fold_flags sect_attr_code sect_attrs)
+ (sect_type_code sect_type))));
+ WORD (TY_u32, IMM 0L); (* reserved1 *)
+ WORD (TY_u32, IMM 0L); (* reserved2 *)
+ |]
+;;
+
+let macho_segment_command
+ (seg_name:string)
+ (seg_fixup:fixup)
+ (maxprot:vm_prot list)
+ (initprot:vm_prot list)
+ (sects:(string * int * (sect_attr list) * sect_type * fixup) array)
+ : frag =
+
+ let cmd_fixup = new_fixup "segment command" in
+ let cmd =
+ SEQ [|
+ WORD (TY_u32, IMM (load_command_code LC_SEGMENT));
+ WORD (TY_u32, F_SZ cmd_fixup);
+ fixed_sz_string 16 seg_name;
+ WORD (TY_u32, M_POS seg_fixup);
+ WORD (TY_u32, M_SZ seg_fixup);
+ WORD (TY_u32, F_POS seg_fixup);
+ WORD (TY_u32, F_SZ seg_fixup);
+ WORD (TY_u32, IMM (fold_flags vm_prot_code maxprot));
+ WORD (TY_u32, IMM (fold_flags vm_prot_code initprot));
+ WORD (TY_u32, IMM (Int64.of_int (Array.length sects)));
+ WORD (TY_u32, IMM 0L); (* Flags? *)
+ |]
+ in
+ DEF (cmd_fixup,
+ SEQ [|
+ cmd;
+ SEQ (Array.map (macho_section_command seg_name) sects);
+ |])
+;;
+
+let macho_thread_command
+ (entry:fixup)
+ : frag =
+ let cmd_fixup = new_fixup "thread command" in
+ let x86_THREAD_STATE32 = 1L in
+ let regs =
+ [|
+ WORD (TY_u32, IMM 0x0L); (* eax *)
+ WORD (TY_u32, IMM 0x0L); (* ebx *)
+ WORD (TY_u32, IMM 0x0L); (* ecx *)
+ WORD (TY_u32, IMM 0x0L); (* edx *)
+
+ WORD (TY_u32, IMM 0x0L); (* edi *)
+ WORD (TY_u32, IMM 0x0L); (* esi *)
+ WORD (TY_u32, IMM 0x0L); (* ebp *)
+ WORD (TY_u32, IMM 0x0L); (* esp *)
+
+ WORD (TY_u32, IMM 0x0L); (* ss *)
+ WORD (TY_u32, IMM 0x0L); (* eflags *)
+ WORD (TY_u32, M_POS entry); (* eip *)
+ WORD (TY_u32, IMM 0x0L); (* cs *)
+
+ WORD (TY_u32, IMM 0x0L); (* ds *)
+ WORD (TY_u32, IMM 0x0L); (* es *)
+ WORD (TY_u32, IMM 0x0L); (* fs *)
+ WORD (TY_u32, IMM 0x0L); (* gs *)
+ |]
+ in
+ let cmd =
+ SEQ [|
+ WORD (TY_u32, IMM (load_command_code LC_UNIXTHREAD));
+ WORD (TY_u32, F_SZ cmd_fixup);
+ WORD (TY_u32, IMM x86_THREAD_STATE32); (* "flavour" *)
+ WORD (TY_u32, IMM (Int64.of_int (Array.length regs)));
+ SEQ regs
+ |]
+ in
+ DEF (cmd_fixup, cmd)
+;;
+
+let macho_dylinker_command : frag =
+ let cmd_fixup = new_fixup "dylinker command" in
+ let str_fixup = new_fixup "dylinker lc_str fixup" in
+ let cmd =
+ SEQ
+ [|
+ WORD (TY_u32, IMM (load_command_code LC_LOAD_DYLINKER));
+ WORD (TY_u32, F_SZ cmd_fixup);
+
+ (* see definition of lc_str; these things are weird. *)
+ WORD (TY_u32, SUB (F_POS (str_fixup), F_POS (cmd_fixup)));
+ DEF (str_fixup, ZSTRING "/usr/lib/dyld");
+ ALIGN_FILE (4, MARK);
+ |]
+ in
+ DEF (cmd_fixup, cmd);
+;;
+
+let macho_dylib_command (dylib:string) : frag =
+
+ let cmd_fixup = new_fixup "dylib command" in
+ let str_fixup = new_fixup "dylib lc_str fixup" in
+ let cmd =
+ SEQ
+ [|
+ WORD (TY_u32, IMM (load_command_code LC_LOAD_DYLIB));
+ WORD (TY_u32, F_SZ cmd_fixup);
+
+ (* see definition of lc_str; these things are weird. *)
+ WORD (TY_u32, SUB (F_POS (str_fixup), F_POS (cmd_fixup)));
+
+ WORD (TY_u32, IMM 0L); (* timestamp *)
+ WORD (TY_u32, IMM 0L); (* current_version *)
+ WORD (TY_u32, IMM 0L); (* compatibility_version *)
+
+ (* Payload-and-alignment of an lc_str goes at end of command. *)
+ DEF (str_fixup, ZSTRING dylib);
+ ALIGN_FILE (4, MARK);
+
+ |]
+ in
+ DEF (cmd_fixup, cmd)
+;;
+
+
+let macho_symtab_command
+ (symtab_fixup:fixup)
+ (nsyms:int64)
+ (strtab_fixup:fixup)
+ : frag =
+ let cmd_fixup = new_fixup "symtab command" in
+ let cmd =
+ SEQ
+ [|
+ WORD (TY_u32, IMM (load_command_code LC_SYMTAB));
+ WORD (TY_u32, F_SZ cmd_fixup);
+
+ WORD (TY_u32, F_POS symtab_fixup); (* symoff *)
+ WORD (TY_u32, IMM nsyms); (* nsyms *)
+
+ WORD (TY_u32, F_POS strtab_fixup); (* stroff *)
+ WORD (TY_u32, F_SZ strtab_fixup); (* strsz *)
+ |]
+ in
+ DEF (cmd_fixup, cmd)
+;;
+
+let macho_dysymtab_command
+ (local_defined_syms_index:int64)
+ (local_defined_syms_count:int64)
+ (external_defined_syms_index:int64)
+ (external_defined_syms_count:int64)
+ (undefined_syms_index:int64)
+ (undefined_syms_count:int64)
+ (indirect_symtab_fixup:fixup) : frag =
+ let cmd_fixup = new_fixup "dysymtab command" in
+ let cmd =
+ SEQ
+ [|
+ WORD (TY_u32, IMM (load_command_code LC_DYSYMTAB));
+ WORD (TY_u32, F_SZ cmd_fixup);
+
+ WORD (TY_u32, IMM local_defined_syms_index); (* ilocalsym *)
+ WORD (TY_u32, IMM local_defined_syms_count); (* nlocalsym *)
+
+ WORD (TY_u32, IMM external_defined_syms_index); (* iextdefsym *)
+ WORD (TY_u32, IMM external_defined_syms_count); (* nextdefsym *)
+
+ WORD (TY_u32, IMM undefined_syms_index); (* iundefsym *)
+ WORD (TY_u32, IMM undefined_syms_count); (* nundefsym *)
+
+ WORD (TY_u32, IMM 0L); (* tocoff *)
+ WORD (TY_u32, IMM 0L); (* ntoc *)
+
+ WORD (TY_u32, IMM 0L); (* modtaboff *)
+ WORD (TY_u32, IMM 0L); (* nmodtab *)
+
+ WORD (TY_u32, IMM 0L); (* extrefsymoff *)
+ WORD (TY_u32, IMM 0L); (* nextrefsyms *)
+
+ WORD (TY_u32, F_POS indirect_symtab_fixup); (* indirectsymoff *)
+ WORD (TY_u32, IMM undefined_syms_count); (* nindirectsyms *)
+
+ WORD (TY_u32, IMM 0L); (* extreloff *)
+ WORD (TY_u32, IMM 0L); (* nextrel *)
+
+ WORD (TY_u32, IMM 0L); (* locreloff *)
+ WORD (TY_u32, IMM 0L); (* nlocrel *)
+ |]
+ in
+ DEF (cmd_fixup, cmd)
+;;
+
+let macho_header_32
+ (cpu:cpu_type)
+ (sub:cpu_subtype)
+ (ftype:file_type)
+ (flags:file_flag list)
+ (loadcmds:frag array) : frag =
+ let load_commands_fixup = new_fixup "load commands" in
+ let cmds = DEF (load_commands_fixup, SEQ loadcmds) in
+ SEQ
+ [|
+ WORD (TY_u32, IMM mh_magic);
+ WORD (TY_u32, IMM (cpu_type_code cpu));
+ WORD (TY_u32, IMM (cpu_subtype_code sub));
+ WORD (TY_u32, IMM (file_type_code ftype));
+ WORD (TY_u32, IMM (Int64.of_int (Array.length loadcmds)));
+ WORD (TY_u32, F_SZ load_commands_fixup);
+ WORD (TY_u32, IMM (fold_flags file_flag_code flags));
+ cmds
+ |]
+;;
+
+let emit_file
+ (sess:Session.sess)
+ (crate:Ast.crate)
+ (code:Asm.frag)
+ (data:Asm.frag)
+ (sem:Semant.ctxt)
+ (dwarf:Dwarf.debug_records)
+ : unit =
+
+ (* FIXME: alignment? *)
+
+ let mh_execute_header_fixup = new_fixup "__mh_execute header" in
+
+ let nxargc_fixup = (Semant.provide_native sem SEG_data "NXArgc") in
+ let nxargv_fixup = (Semant.provide_native sem SEG_data "NXArgv") in
+ let progname_fixup = (Semant.provide_native sem SEG_data "__progname") in
+ let environ_fixup = (Semant.provide_native sem SEG_data "environ") in
+ let exit_fixup = (Semant.require_native sem REQUIRED_LIB_crt "exit") in
+ let (start_fixup, rust_start_fixup) =
+ if sess.Session.sess_library_mode
+ then (None, None)
+ else (Some (new_fixup "start function entry"),
+ Some (Semant.require_native sem REQUIRED_LIB_rustrt "rust_start"))
+ in
+
+ let text_sect_align_log2 = 2 in
+ let data_sect_align_log2 = 2 in
+
+ let seg_align = 0x1000 in
+ let text_sect_align = 2 lsl text_sect_align_log2 in
+ let data_sect_align = 2 lsl data_sect_align_log2 in
+
+ let align_both align i =
+ ALIGN_FILE (align,
+ (ALIGN_MEM (align, i)))
+ in
+
+ let def_aligned a f i =
+ align_both a
+ (SEQ [| DEF(f, i);
+ (align_both a MARK)|])
+ in
+
+ (* Segments. *)
+ let zero_segment_fixup = new_fixup "__PAGEZERO segment" in
+ let text_segment_fixup = new_fixup "__TEXT segment" in
+ let data_segment_fixup = new_fixup "__DATA segment" in
+ let dwarf_segment_fixup = new_fixup "__DWARF segment" in
+ let linkedit_segment_fixup = new_fixup "__LINKEDIT segment" in
+
+ (* Sections in the text segment. *)
+ let text_section_fixup = new_fixup "__text section" in
+
+ (* Sections in the data segment. *)
+ let data_section_fixup = new_fixup "__data section" in
+ let const_section_fixup = new_fixup "__const section" in
+ let bss_section_fixup = new_fixup "__bss section" in
+ let note_rust_section_fixup = new_fixup "__note.rust section" in
+ let nl_symbol_ptr_section_fixup = new_fixup "__nl_symbol_ptr section" in
+
+ let data_section = def_aligned data_sect_align data_section_fixup data in
+ let const_section =
+ def_aligned data_sect_align const_section_fixup (SEQ [| |])
+ in
+ let bss_section =
+ def_aligned data_sect_align bss_section_fixup (SEQ [| |])
+ in
+ let note_rust_section =
+ def_aligned
+ data_sect_align note_rust_section_fixup
+ (Asm.note_rust_frags crate.node.Ast.crate_meta)
+ in
+
+ (* Officially Apple doesn't claim to support DWARF sections like this, but
+ they work. *)
+ let debug_info_section =
+ def_aligned data_sect_align
+ sem.Semant.ctxt_debug_info_fixup
+ dwarf.Dwarf.debug_info
+ in
+ let debug_abbrev_section =
+ def_aligned data_sect_align
+ sem.Semant.ctxt_debug_abbrev_fixup
+ dwarf.Dwarf.debug_abbrev
+ in
+
+
+ (* String, symbol and parallel "nonlazy-pointer" tables. *)
+ let symtab_fixup = new_fixup "symtab" in
+ let strtab_fixup = new_fixup "strtab" in
+
+ let symbol_nlist_entry
+ (sect_index:int)
+ (nty:n_type list)
+ (nd:n_desc)
+ (nv:Asm.expr64)
+ : (frag * fixup) =
+ let strtab_entry_fixup = new_fixup "strtab entry" in
+ (SEQ
+ [|
+ WORD (TY_u32, SUB ((F_POS strtab_entry_fixup),
+ (F_POS strtab_fixup)));
+ BYTE (Int64.to_int (fold_flags n_type_code nty));
+ BYTE sect_index;
+ WORD (TY_u16, IMM (n_desc_code nd));
+ WORD (TY_u32, nv);
+ |], strtab_entry_fixup)
+ in
+
+ let sect_symbol_nlist_entry
+ (seg:segment)
+ (fixup_to_use:fixup)
+ : (frag * fixup) =
+ let nty = [ N_SECT; N_EXT ] in
+ let nd = (0, [], REFERENCE_FLAG_UNDEFINED_NON_LAZY) in
+ let (sect_index, _(*seg_fix*)) =
+ match seg with
+ SEG_text -> (1, text_segment_fixup)
+ | SEG_data -> (2, data_segment_fixup)
+ in
+ symbol_nlist_entry sect_index nty nd (M_POS fixup_to_use)
+ in
+
+ let sect_private_symbol_nlist_entry
+ (seg:segment)
+ (fixup_to_use:fixup)
+ : (frag * fixup) =
+ let nty = [ N_SECT; ] in
+ let nd = (0, [], REFERENCE_FLAG_UNDEFINED_NON_LAZY) in
+ let (sect_index, _(*seg_fix*)) =
+ match seg with
+ SEG_text -> (1, text_segment_fixup)
+ | SEG_data -> (2, data_segment_fixup)
+ in
+ symbol_nlist_entry sect_index nty nd (M_POS fixup_to_use)
+ in
+
+ let indirect_symbol_nlist_entry (dylib_index:int) : (frag * fixup) =
+ let nty = [ N_UNDF; N_EXT ] in
+ let nd = (dylib_index, [], REFERENCE_FLAG_UNDEFINED_NON_LAZY) in
+ symbol_nlist_entry 0 nty nd (IMM 0L)
+ in
+
+ let indirect_symbols =
+ Array.of_list
+ (List.concat
+ (List.map
+ (fun (lib, tab) ->
+ (List.map
+ (fun (name,fix) -> (lib,name,fix))
+ (htab_pairs tab)))
+ (htab_pairs sem.Semant.ctxt_native_required)))
+ in
+
+ let dylib_index (lib:required_lib) : int =
+ match lib with
+ REQUIRED_LIB_rustrt -> 1
+ | REQUIRED_LIB_crt -> 2
+ | _ -> bug () "Macho.dylib_index on nonstandard required lib."
+ in
+
+ (* Make undef symbols for native imports. *)
+ let (undefined_symbols:(string * (frag * fixup)) array) =
+ Array.map (fun (lib,name,_) ->
+ ("_" ^ name,
+ indirect_symbol_nlist_entry (dylib_index lib)))
+ indirect_symbols
+ in
+
+ (* Make symbols for exports. *)
+ let (export_symbols:(string * (frag * fixup)) array) =
+ let export_symbols_of_seg (seg, tab) =
+ List.map
+ begin
+ fun (name, fix) ->
+ let name = "_" ^ name in
+ let sym = sect_symbol_nlist_entry seg fix in
+ (name, sym)
+ end
+ (htab_pairs tab)
+ in
+ Array.of_list
+ (List.concat
+ (List.map export_symbols_of_seg
+ (htab_pairs sem.Semant.ctxt_native_provided)))
+ in
+
+ (* Make private symbols for items. *)
+ let (local_item_symbols:(string * (frag * fixup)) array) =
+ Array.map (fun code ->
+ let fix = code.Semant.code_fixup in
+ ("_" ^ fix.fixup_name,
+ sect_private_symbol_nlist_entry SEG_text fix))
+ (Array.of_list (htab_vals sem.Semant.ctxt_all_item_code))
+ in
+
+ (* Make private symbols for glue. *)
+ let (local_glue_symbols:(string * (frag * fixup)) array) =
+ Array.map (fun (g, code) ->
+ let fix = code.Semant.code_fixup in
+ ("_" ^ (Semant.glue_str sem g),
+ sect_private_symbol_nlist_entry SEG_text fix))
+ (Array.of_list (htab_pairs sem.Semant.ctxt_glue_code))
+ in
+
+ let (export_header_symbols:(string * (frag * fixup)) array) =
+ let name =
+ if sess.Session.sess_library_mode
+ then "__mh_dylib_header"
+ else "__mh_execute_header"
+ in
+ [|
+ (name, sect_symbol_nlist_entry SEG_text mh_execute_header_fixup);
+ |]
+ in
+
+ let export_symbols = Array.concat [ export_symbols;
+ export_header_symbols ]
+ in
+
+ let local_symbols = Array.concat [ local_item_symbols;
+ local_glue_symbols ]
+ in
+
+ let symbols = Array.concat [ local_symbols;
+ export_symbols;
+ undefined_symbols ]
+ in
+ let n_local_syms = Array.length local_symbols in
+ let n_export_syms = Array.length export_symbols in
+ let n_undef_syms = Array.length undefined_symbols in
+
+ let indirect_symbols_off = n_local_syms + n_export_syms in
+ let indirect_symtab_fixup = new_fixup "indirect symbol table" in
+ let indirect_symtab =
+ DEF (indirect_symtab_fixup,
+ SEQ (Array.mapi
+ (fun i _ -> WORD (TY_u32,
+ IMM (Int64.of_int
+ (i + indirect_symbols_off))))
+ indirect_symbols))
+ in
+
+ let nl_symbol_ptr_section =
+ def_aligned data_sect_align nl_symbol_ptr_section_fixup
+ (SEQ (Array.map
+ (fun (_, _, fix) ->
+ DEF(fix, WORD(TY_u32, IMM 0L)))
+ indirect_symbols))
+ in
+ let strtab = DEF (strtab_fixup,
+ SEQ (Array.map
+ (fun (name, (_, fix)) -> DEF(fix, ZSTRING name))
+ symbols))
+ in
+ let symtab = DEF (symtab_fixup,
+ SEQ (Array.map (fun (_, (frag, _)) -> frag) symbols))
+ in
+
+
+ let load_commands =
+ [|
+ macho_segment_command "__PAGEZERO" zero_segment_fixup
+ [] [] [||];
+
+ macho_segment_command "__TEXT" text_segment_fixup
+ [VM_PROT_READ; VM_PROT_EXECUTE]
+ [VM_PROT_READ; VM_PROT_EXECUTE]
+ [|
+ ("__text", text_sect_align_log2, [], S_REGULAR, text_section_fixup)
+ |];
+
+ macho_segment_command "__DATA" data_segment_fixup
+ [VM_PROT_READ; VM_PROT_WRITE]
+ [VM_PROT_READ; VM_PROT_WRITE]
+ [|
+ ("__data", data_sect_align_log2, [],
+ S_REGULAR, data_section_fixup);
+ ("__const", data_sect_align_log2, [],
+ S_REGULAR, const_section_fixup);
+ ("__bss", data_sect_align_log2, [],
+ S_REGULAR, bss_section_fixup);
+ ("__note.rust", data_sect_align_log2, [],
+ S_REGULAR, note_rust_section_fixup);
+ ("__nl_symbol_ptr", data_sect_align_log2, [],
+ S_NON_LAZY_SYMBOL_POINTERS, nl_symbol_ptr_section_fixup)
+ |];
+
+ macho_segment_command "__DWARF" dwarf_segment_fixup
+ [VM_PROT_READ]
+ [VM_PROT_READ]
+ [|
+ ("__debug_info", data_sect_align_log2, [],
+ S_REGULAR, sem.Semant.ctxt_debug_info_fixup);
+ ("__debug_abbrev", data_sect_align_log2, [],
+ S_REGULAR, sem.Semant.ctxt_debug_abbrev_fixup);
+ |];
+
+ macho_segment_command "__LINKEDIT" linkedit_segment_fixup
+ [VM_PROT_READ]
+ [VM_PROT_READ]
+ [|
+ |];
+
+ macho_symtab_command
+ symtab_fixup (Int64.of_int (Array.length symbols)) strtab_fixup;
+
+
+ macho_dysymtab_command
+ 0L
+ (Int64.of_int n_local_syms)
+ (Int64.of_int n_local_syms)
+ (Int64.of_int n_export_syms)
+ (Int64.of_int (n_local_syms + n_export_syms))
+ (Int64.of_int n_undef_syms)
+ indirect_symtab_fixup;
+
+ macho_dylinker_command;
+
+ macho_dylib_command "librustrt.dylib";
+
+ macho_dylib_command "/usr/lib/libSystem.B.dylib";
+
+ begin
+ match start_fixup with
+ None -> MARK
+ | Some start_fixup ->
+ macho_thread_command start_fixup
+ end;
+ |]
+ in
+
+ let header_and_commands =
+ macho_header_32
+ CPU_TYPE_X86
+ CPU_SUBTYPE_X86_ALL
+ (if sess.Session.sess_library_mode then MH_DYLIB else MH_EXECUTE)
+ [ MH_BINDATLOAD; MH_DYLDLINK; MH_TWOLEVEL ]
+ load_commands
+ in
+
+ let objfile_start e start_fixup rust_start_fixup main_fn_fixup =
+ let edx = X86.h X86.edx in
+ let edx_pointee =
+ Il.Mem ((Il.RegIn (edx, None)), Il.ScalarTy (Il.AddrTy Il.OpaqueTy))
+ in
+ Il.emit_full e (Some start_fixup) [] Il.Dead;
+
+ (* zero marks the bottom of the frame chain. *)
+ Il.emit e (Il.Push (X86.imm (Asm.IMM 0L)));
+ Il.emit e (Il.umov (X86.rc X86.ebp) (X86.ro X86.esp));
+
+ (* 16-byte align stack for SSE. *)
+ Il.emit e (Il.binary Il.AND (X86.rc X86.esp) (X86.ro X86.esp)
+ (X86.imm (Asm.IMM 0xfffffffffffffff0L)));
+
+ (* Store argv. *)
+ Abi.load_fixup_addr e edx nxargv_fixup Il.OpaqueTy;
+ Il.emit e (Il.lea (X86.rc X86.ecx)
+ (Il.Cell (Il.Mem ((Il.RegIn (Il.Hreg X86.ebp,
+ Some (X86.word_off_n 2))),
+ Il.OpaqueTy))));
+ Il.emit e (Il.umov edx_pointee (X86.ro X86.ecx));
+ Il.emit e (Il.Push (X86.ro X86.ecx));
+
+ (* Store argc. *)
+ Abi.load_fixup_addr e edx nxargc_fixup Il.OpaqueTy;
+ Il.emit e (Il.umov (X86.rc X86.eax)
+ (X86.c (X86.word_n (Il.Hreg X86.ebp) 1)));
+ Il.emit e (Il.umov edx_pointee (X86.ro X86.eax));
+ Il.emit e (Il.Push (X86.ro X86.eax));
+
+ (* Calculte and store envp. *)
+ Il.emit e (Il.binary Il.ADD
+ (X86.rc X86.eax) (X86.ro X86.eax)
+ (X86.imm (Asm.IMM 1L)));
+ Il.emit e (Il.binary Il.UMUL
+ (X86.rc X86.eax) (X86.ro X86.eax)
+ (X86.imm (Asm.IMM X86.word_sz)));
+ Il.emit e (Il.binary Il.ADD (X86.rc X86.eax)
+ (X86.ro X86.eax) (X86.ro X86.ecx));
+ Abi.load_fixup_addr e edx environ_fixup Il.OpaqueTy;
+ Il.emit e (Il.umov edx_pointee (X86.ro X86.eax));
+
+ (* Push 16 bytes to preserve SSE alignment. *)
+ Abi.load_fixup_addr e edx sem.Semant.ctxt_crate_fixup Il.OpaqueTy;
+ Il.emit e (Il.Push (X86.ro X86.edx));
+ Abi.load_fixup_addr e edx main_fn_fixup Il.OpaqueTy;
+ Il.emit e (Il.Push (X86.ro X86.edx));
+ let fptr = Abi.load_fixup_codeptr e edx rust_start_fixup true true in
+ Il.emit e (Il.call (X86.rc X86.eax) fptr);
+ Il.emit e (Il.Pop (X86.rc X86.ecx));
+ Il.emit e (Il.Push (X86.ro X86.eax));
+ let fptr = Abi.load_fixup_codeptr e edx exit_fixup true true in
+ Il.emit e (Il.call (X86.rc X86.eax) fptr);
+ Il.emit e (Il.Pop (X86.rc X86.ecx));
+ Il.emit e (Il.Pop (X86.rc X86.ecx));
+ Il.emit e (Il.Pop (X86.rc X86.ecx));
+ Il.emit e (Il.Pop (X86.rc X86.ecx));
+
+ Il.emit e Il.Ret;
+ in
+
+ let text_section =
+ let start_code =
+ match (start_fixup, rust_start_fixup,
+ sem.Semant.ctxt_main_fn_fixup) with
+ (None, _, _)
+ | (_, None, _)
+ | (_, _, None) -> MARK
+ | (Some start_fixup,
+ Some rust_start_fixup,
+ Some main_fn_fixup) ->
+ let e = X86.new_emitter_without_vregs () in
+ objfile_start e start_fixup rust_start_fixup main_fn_fixup;
+ X86.frags_of_emitted_quads sess e
+ in
+ def_aligned text_sect_align text_section_fixup
+ (SEQ [|
+ start_code;
+ code
+ |])
+ in
+
+ let text_segment =
+ def_aligned seg_align text_segment_fixup
+ (SEQ [|
+ DEF (mh_execute_header_fixup, header_and_commands);
+ text_section;
+ align_both seg_align MARK;
+ |]);
+ in
+
+ let zero_segment = align_both seg_align
+ (SEQ [| MEMPOS 0L; DEF (zero_segment_fixup,
+ SEQ [| MEMPOS 0x1000L; MARK |] ) |])
+ in
+
+ let data_segment = def_aligned seg_align data_segment_fixup
+ (SEQ [|
+ DEF(nxargc_fixup, WORD (TY_u32, IMM 0L));
+ DEF(nxargv_fixup, WORD (TY_u32, IMM 0L));
+ DEF(environ_fixup, WORD (TY_u32, IMM 0L));
+ DEF(progname_fixup, WORD (TY_u32, IMM 0L));
+ data_section;
+ const_section;
+ bss_section;
+ note_rust_section;
+ nl_symbol_ptr_section
+ |])
+ in
+
+ let dwarf_segment = def_aligned seg_align dwarf_segment_fixup
+ (SEQ [|
+ debug_info_section;
+ debug_abbrev_section;
+ |])
+ in
+
+ let linkedit_segment = def_aligned seg_align linkedit_segment_fixup
+ (SEQ [|
+ symtab;
+ strtab;
+ indirect_symtab;
+ |])
+ in
+
+ let segments =
+ SEQ [|
+ DEF (sem.Semant.ctxt_image_base_fixup, MARK);
+ zero_segment;
+ text_segment;
+ data_segment;
+ dwarf_segment;
+ linkedit_segment;
+ |]
+ in
+ write_out_frag sess true segments
+;;
+
+
+let sniff
+ (sess:Session.sess)
+ (filename:filename)
+ : asm_reader option =
+ try
+ let stat = Unix.stat filename in
+ if (stat.Unix.st_kind = Unix.S_REG) &&
+ (stat.Unix.st_size > 4)
+ then
+ let ar = new_asm_reader sess filename in
+ let _ = log sess "sniffing Mach-O file" in
+ if (ar.asm_get_u32()) = (Int64.to_int mh_magic)
+ then (ar.asm_seek 0; Some ar)
+ else None
+ else
+ None
+ with
+ _ -> None
+;;
+
+let get_sections
+ (sess:Session.sess)
+ (ar:asm_reader)
+ : (string,(int*int)) Hashtbl.t =
+ let sects = Hashtbl.create 0 in
+ let _ = log sess "reading sections" in
+ let magic = ar.asm_get_u32() in
+ let _ = assert (magic = (Int64.to_int mh_magic)) in
+ let _ = ar.asm_adv_u32() in (* cpu type *)
+ let _ = ar.asm_adv_u32() in (* cpu subtype *)
+ let _ = ar.asm_adv_u32() in (* file type *)
+ let n_load_cmds = ar.asm_get_u32() in
+ let _ = ar.asm_adv_u32() in
+ let _ = log sess "Mach-o file with %d load commands" n_load_cmds in
+ let _ = ar.asm_adv_u32() in (* flags *)
+ let lc_seg = Int64.to_int (load_command_code LC_SEGMENT) in
+ for i = 0 to n_load_cmds - 1 do
+ let load_cmd_code = ar.asm_get_u32() in
+ let load_cmd_size = ar.asm_get_u32() in
+ let _ = log sess "load command %d:" i in
+ if load_cmd_code != lc_seg
+ then ar.asm_adv (load_cmd_size - 8)
+ else
+ begin
+ let seg_name = ar.asm_get_zstr_padded 16 in
+ let _ = log sess "LC_SEGMENT %s" seg_name in
+ let _ = ar.asm_adv_u32() in (* seg mem pos *)
+ let _ = ar.asm_adv_u32() in (* seg mem sz *)
+ let _ = ar.asm_adv_u32() in (* seg file pos *)
+ let _ = ar.asm_adv_u32() in (* seg file sz *)
+ let _ = ar.asm_adv_u32() in (* maxprot *)
+ let _ = ar.asm_adv_u32() in (* initprot *)
+ let n_sects = ar.asm_get_u32() in
+ let _ = ar.asm_get_u32() in (* flags *)
+ let _ = log sess "%d sections" in
+ for j = 0 to n_sects - 1 do
+ let sect_name = ar.asm_get_zstr_padded 16 in
+ let _ = ar.asm_adv 16 in (* seg name *)
+ let _ = ar.asm_adv_u32() in (* sect mem pos *)
+ let m_sz = ar.asm_get_u32() in
+ let f_pos = ar.asm_get_u32() in
+ let _ = ar.asm_adv_u32() in (* sect align *)
+ let _ = ar.asm_adv_u32() in (* reloff *)
+ let _ = ar.asm_adv_u32() in (* nreloc *)
+ let _ = ar.asm_adv_u32() in (* flags *)
+ let _ = ar.asm_adv_u32() in (* reserved1 *)
+ let _ = ar.asm_adv_u32() in (* reserved2 *)
+ let _ =
+ log sess
+ " section %d: 0x%x - 0x%x %s "
+ j f_pos (f_pos + m_sz) sect_name
+ in
+ let len = String.length sect_name in
+ let sect_name =
+ if (len > 2
+ && sect_name.[0] = '_'
+ && sect_name.[1] = '_')
+ then "." ^ (String.sub sect_name 2 (len-2))
+ else sect_name
+ in
+ Hashtbl.add sects sect_name (f_pos, m_sz)
+ done
+ end
+ done;
+ sects
+;;
+
+
+(*
+ * 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/be/pe.ml b/src/boot/be/pe.ml
new file mode 100644
index 00000000..d360ddf5
--- /dev/null
+++ b/src/boot/be/pe.ml
@@ -0,0 +1,1149 @@
+(*
+
+ Module for writing Microsoft PE files
+
+ Every image has a base address it's to be loaded at.
+
+ "file pointer" = offset in file
+
+ "VA" = address at runtime
+
+ "RVA" = VA - base address
+
+ If you write a non-RVA absolute address at any point you must put it
+ in a rebasing list so the loader can adjust it when/if it has to load
+ you at a different address.
+
+ Almost all addresses in the file are RVAs. Worry about the VAs.
+
+*)
+
+open Asm;;
+open Common;;
+
+let log (sess:Session.sess) =
+ Session.log "obj (pe)"
+ sess.Session.sess_log_obj
+ sess.Session.sess_log_out
+;;
+
+let iflog (sess:Session.sess) (thunk:(unit -> unit)) : unit =
+ if sess.Session.sess_log_obj
+ then thunk ()
+ else ()
+;;
+
+(*
+
+ The default image base (VA) for an executable on Win32 is 0x400000.
+
+ We use this too. RVAs are relative to this. RVA 0 = VA 0x400000.
+
+ Alignments are also relatively standard and fixed for Win32/PE32:
+ 4k memory pages, 512 byte disk sectors.
+
+ Since this is a stupid emitter, and we're not generating an awful
+ lot of sections, we are not going to differentiate between these
+ two kinds of alignment: we just align our sections to memory pages
+ and sometimes waste most of them. Shucks.
+
+*)
+
+let pe_image_base = 0x400000L;;
+let pe_file_alignment = 0x200;;
+let pe_mem_alignment = 0x1000;;
+
+let rva (f:fixup) = (SUB ((M_POS f), (IMM pe_image_base)));;
+
+let def_file_aligned f i =
+ ALIGN_FILE
+ (pe_file_alignment,
+ SEQ [|
+ DEF(f,
+ SEQ [| i;
+ ALIGN_FILE
+ (pe_file_alignment, MARK) |]) |] )
+;;
+
+let def_mem_aligned f i =
+ ALIGN_MEM
+ (pe_mem_alignment,
+ SEQ [|
+ DEF(f,
+ SEQ [| i;
+ ALIGN_MEM
+ (pe_mem_alignment, MARK) |]) |] )
+;;
+
+let align_both i =
+ ALIGN_FILE (pe_file_alignment,
+ (ALIGN_MEM (pe_mem_alignment, i)))
+;;
+
+let def_aligned f i =
+ align_both
+ (SEQ [| DEF(f,i);
+ (align_both MARK)|])
+;;
+
+
+(*
+
+ At the beginning of a PE file there is an MS-DOS stub, 0x00 - 0x7F,
+ that we just insert literally. It prints "This program must be run
+ under Win32" and exits. Woo!
+
+ Within it, at offset 0x3C, there is an encoded offset of the PE
+ header we actually care about. So 0x3C - 0x3F are 0x00000100 (LE)
+ which say "the PE header is actually at 0x100", a nice sensible spot
+ for it. We pad the next 128 bytes out to 0x100 and start there for
+ real.
+
+ From then on in it's a sensible object file. Here's the MS-DOS bit.
+*)
+
+let pe_msdos_header_and_padding
+ : frag =
+ SEQ [|
+ BYTES
+ [|
+ (* 00000000 *)
+ 0x4d; 0x5a; 0x50; 0x00; 0x02; 0x00; 0x00; 0x00;
+ 0x04; 0x00; 0x0f; 0x00; 0xff; 0xff; 0x00; 0x00;
+
+ (* 00000010 *)
+ 0xb8; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00;
+ 0x40; 0x00; 0x1a; 0x00; 0x00; 0x00; 0x00; 0x00;
+
+ (* 00000020 *)
+ 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00;
+ 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00;
+
+ (* 00000030 *)
+ 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00;
+ 0x00; 0x00; 0x00; 0x00; 0x00; 0x01; 0x00; 0x00;
+ (* ^^^^PE HDR offset^^^^^ *)
+
+ (* 00000040 *)
+ 0xba; 0x10; 0x00; 0x0e; 0x1f; 0xb4; 0x09; 0xcd;
+ 0x21; 0xb8; 0x01; 0x4c; 0xcd; 0x21; 0x90; 0x90;
+
+ (* 00000050 *)
+ 0x54; 0x68; 0x69; 0x73; 0x20; 0x70; 0x72; 0x6f; (* "This pro" *)
+ 0x67; 0x72; 0x61; 0x6d; 0x20; 0x6d; 0x75; 0x73; (* "gram mus" *)
+
+ (* 00000060 *)
+ 0x74; 0x20; 0x62; 0x65; 0x20; 0x72; 0x75; 0x6e; (* "t be run" *)
+ 0x20; 0x75; 0x6e; 0x64; 0x65; 0x72; 0x20; 0x57; (* " under W" *)
+
+ (* 00000070 *)
+ 0x69; 0x6e; 0x33; 0x32; 0x0d; 0x0a; 0x24; 0x37; (* "in32\r\n" *)
+ 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00; 0x00;
+ |];
+ PAD 0x80
+ |]
+;;
+
+(*
+ A work of art, is it not? Take a moment to appreciate the madness.
+
+ All done? Ok, now on to the PE header proper.
+
+ PE headers are just COFF headers with a little preamble.
+*)
+
+type pe_machine =
+ (* Maybe support more later. *)
+ IMAGE_FILE_MACHINE_AMD64
+ | IMAGE_FILE_MACHINE_I386
+;;
+
+
+let pe_timestamp _ =
+ Int64.of_float (Unix.gettimeofday())
+;;
+
+
+type pe_characteristics =
+ (* Maybe support more later. *)
+ IMAGE_FILE_RELOCS_STRIPPED
+ | IMAGE_FILE_EXECUTABLE_IMAGE
+ | IMAGE_FILE_LINE_NUMS_STRIPPED
+ | IMAGE_FILE_LOCAL_SYMS_STRIPPED
+ | IMAGE_FILE_32BIT_MACHINE
+ | IMAGE_FILE_DEBUG_STRIPPED
+ | IMAGE_FILE_DLL
+;;
+
+
+let pe_header
+ ~(machine:pe_machine)
+ ~(symbol_table_fixup:fixup)
+ ~(number_of_sections:int64)
+ ~(number_of_symbols:int64)
+ ~(loader_hdr_fixup:fixup)
+ ~(characteristics:pe_characteristics list)
+ : frag =
+ ALIGN_FILE
+ (8,
+ SEQ [|
+ STRING "PE\x00\x00";
+ WORD (TY_u16, (IMM (match machine with
+ IMAGE_FILE_MACHINE_AMD64 -> 0x8664L
+ | IMAGE_FILE_MACHINE_I386 -> 0x014cL)));
+ WORD (TY_u16, (IMM number_of_sections));
+ WORD (TY_u32, (IMM (pe_timestamp())));
+ WORD (TY_u32, (F_POS symbol_table_fixup));
+ WORD (TY_u32, (IMM number_of_symbols));
+ WORD (TY_u16, (F_SZ loader_hdr_fixup));
+ WORD (TY_u16, (IMM (fold_flags
+ (fun c -> match c with
+ IMAGE_FILE_RELOCS_STRIPPED -> 0x1L
+ | IMAGE_FILE_EXECUTABLE_IMAGE -> 0x2L
+ | IMAGE_FILE_LINE_NUMS_STRIPPED -> 0x4L
+ | IMAGE_FILE_LOCAL_SYMS_STRIPPED -> 0x8L
+ | IMAGE_FILE_32BIT_MACHINE -> 0x100L
+ | IMAGE_FILE_DEBUG_STRIPPED -> 0x200L
+ | IMAGE_FILE_DLL -> 0x2000L)
+ characteristics)))
+ |])
+;;
+
+(*
+
+ After the PE header comes an "optional" header for the loader. In
+ our case this is hardly optional since we are producing a file for
+ the loader.
+
+*)
+
+type pe_subsystem =
+ (* Maybe support more later. *)
+ IMAGE_SUBSYSTEM_WINDOWS_GUI
+ | IMAGE_SUBSYSTEM_WINDOWS_CUI
+;;
+
+let zero32 = WORD (TY_u32, (IMM 0L))
+;;
+
+let pe_loader_header
+ ~(text_fixup:fixup)
+ ~(init_data_fixup:fixup)
+ ~(size_of_uninit_data:int64)
+ ~(entry_point_fixup:fixup option)
+ ~(image_fixup:fixup)
+ ~(all_hdrs_fixup:fixup)
+ ~(subsys:pe_subsystem)
+ ~(loader_hdr_fixup:fixup)
+ ~(import_dir_fixup:fixup)
+ ~(export_dir_fixup:fixup)
+ : frag =
+ DEF
+ (loader_hdr_fixup,
+ SEQ [|
+ WORD (TY_u16, (IMM 0x10bL)); (* COFF magic tag for PE32. *)
+ (* Snagged *)
+ WORD (TY_u8, (IMM 0x2L)); (* Linker major version. *)
+ WORD (TY_u8, (IMM 0x38L)); (* Linker minor version. *)
+
+ WORD (TY_u32, (F_SZ text_fixup)); (* "size of code" *)
+ WORD (TY_u32, (* "size of all init data" *)
+ (F_SZ init_data_fixup));
+ WORD (TY_u32,
+ (IMM size_of_uninit_data));
+
+ begin
+ match entry_point_fixup with
+ None -> zero32 (* Library mode: DLLMain *)
+ | Some entry_point_fixup ->
+ WORD (TY_u32,
+ (rva
+ entry_point_fixup)) (* "address of entry point" *)
+ end;
+
+ WORD (TY_u32, (rva text_fixup)); (* "base of code" *)
+ WORD (TY_u32, (rva init_data_fixup)); (* "base of data" *)
+ WORD (TY_u32, (IMM pe_image_base));
+ WORD (TY_u32, (IMM (Int64.of_int
+ pe_mem_alignment)));
+ WORD (TY_u32, (IMM (Int64.of_int
+ pe_file_alignment)));
+
+ WORD (TY_u16, (IMM 4L)); (* Major OS version: NT4. *)
+ WORD (TY_u16, (IMM 0L)); (* Minor OS version. *)
+ WORD (TY_u16, (IMM 1L)); (* Major image version. *)
+ WORD (TY_u16, (IMM 0L)); (* Minor image version. *)
+ WORD (TY_u16, (IMM 4L)); (* Major subsystem version. *)
+ WORD (TY_u16, (IMM 0L)); (* Minor subsystem version. *)
+
+ zero32; (* Reserved. *)
+
+ WORD (TY_u32, (M_SZ image_fixup));
+ WORD (TY_u32, (M_SZ all_hdrs_fixup));
+
+ zero32; (* Checksum, but OK if zero. *)
+ WORD (TY_u16, (IMM (match subsys with
+ IMAGE_SUBSYSTEM_WINDOWS_GUI -> 2L
+ | IMAGE_SUBSYSTEM_WINDOWS_CUI -> 3L)));
+
+ WORD (TY_u16, (IMM 0L)); (* DLL characteristics. *)
+
+ WORD (TY_u32, (IMM 0x100000L)); (* Size of stack reserve. *)
+ WORD (TY_u32, (IMM 0x4000L)); (* Size of stack commit. *)
+
+ WORD (TY_u32, (IMM 0x100000L)); (* Size of heap reserve. *)
+ WORD (TY_u32, (IMM 0x1000L)); (* Size of heap commit. *)
+
+ zero32; (* Reserved. *)
+ WORD (TY_u32, (IMM 16L)); (* Number of dir references. *)
+
+ (* Begin directories, variable part of hdr. *)
+
+ (*
+
+ Standard PE files have ~10 directories referenced from
+ here. We only fill in two of them -- the export/import
+ directories -- because we don't care about the others. We
+ leave the rest as zero in case someone is looking for
+ them. This may be superfluous or wrong.
+
+ *)
+
+
+ WORD (TY_u32, (rva export_dir_fixup));
+ WORD (TY_u32, (M_SZ export_dir_fixup));
+
+ WORD (TY_u32, (rva import_dir_fixup));
+ WORD (TY_u32, (M_SZ import_dir_fixup));
+
+ zero32; zero32; (* Resource dir. *)
+ zero32; zero32; (* Exception dir. *)
+ zero32; zero32; (* Security dir. *)
+ zero32; zero32; (* Base reloc dir. *)
+ zero32; zero32; (* Debug dir. *)
+ zero32; zero32; (* Image desc dir. *)
+ zero32; zero32; (* Mach spec dir. *)
+ zero32; zero32; (* TLS dir. *)
+
+ zero32; zero32; (* Load config. *)
+ zero32; zero32; (* Bound import. *)
+ zero32; zero32; (* IAT *)
+ zero32; zero32; (* Delay import. *)
+ zero32; zero32; (* COM descriptor *)
+ zero32; zero32; (* ???????? *)
+ |])
+
+;;
+
+
+type pe_section_id =
+ (* Maybe support more later. *)
+ SECTION_ID_TEXT
+ | SECTION_ID_DATA
+ | SECTION_ID_RDATA
+ | SECTION_ID_BSS
+ | SECTION_ID_IMPORTS
+ | SECTION_ID_EXPORTS
+ | SECTION_ID_DEBUG_ARANGES
+ | SECTION_ID_DEBUG_PUBNAMES
+ | SECTION_ID_DEBUG_INFO
+ | SECTION_ID_DEBUG_ABBREV
+ | SECTION_ID_DEBUG_LINE
+ | SECTION_ID_DEBUG_FRAME
+ | SECTION_ID_NOTE_RUST
+;;
+
+type pe_section_characteristics =
+ (* Maybe support more later. *)
+ IMAGE_SCN_CNT_CODE
+ | IMAGE_SCN_CNT_INITIALIZED_DATA
+ | IMAGE_SCN_CNT_UNINITIALIZED_DATA
+ | IMAGE_SCN_MEM_DISCARDABLE
+ | IMAGE_SCN_MEM_SHARED
+ | IMAGE_SCN_MEM_EXECUTE
+ | IMAGE_SCN_MEM_READ
+ | IMAGE_SCN_MEM_WRITE
+
+let pe_section_header
+ ~(id:pe_section_id)
+ ~(hdr_fixup:fixup)
+ : frag =
+ let
+ characteristics =
+ match id with
+ SECTION_ID_TEXT -> [ IMAGE_SCN_CNT_CODE;
+ IMAGE_SCN_MEM_READ;
+ IMAGE_SCN_MEM_EXECUTE ]
+ | SECTION_ID_DATA -> [ IMAGE_SCN_CNT_INITIALIZED_DATA;
+ IMAGE_SCN_MEM_READ;
+ IMAGE_SCN_MEM_WRITE ]
+ | SECTION_ID_BSS -> [ IMAGE_SCN_CNT_UNINITIALIZED_DATA;
+ IMAGE_SCN_MEM_READ;
+ IMAGE_SCN_MEM_WRITE ]
+ | SECTION_ID_IMPORTS -> [ IMAGE_SCN_CNT_INITIALIZED_DATA;
+ IMAGE_SCN_MEM_READ;
+ IMAGE_SCN_MEM_WRITE ]
+ | SECTION_ID_EXPORTS -> [ IMAGE_SCN_CNT_INITIALIZED_DATA;
+ IMAGE_SCN_MEM_READ ]
+ | SECTION_ID_RDATA
+ | SECTION_ID_DEBUG_ARANGES
+ | SECTION_ID_DEBUG_PUBNAMES
+ | SECTION_ID_DEBUG_INFO
+ | SECTION_ID_DEBUG_ABBREV
+ | SECTION_ID_DEBUG_LINE
+ | SECTION_ID_DEBUG_FRAME
+ | SECTION_ID_NOTE_RUST -> [ IMAGE_SCN_CNT_INITIALIZED_DATA;
+ IMAGE_SCN_MEM_READ ]
+ in
+ SEQ [|
+ STRING
+ begin
+ match id with
+ SECTION_ID_TEXT -> ".text\x00\x00\x00"
+ | SECTION_ID_DATA -> ".data\x00\x00\x00"
+ | SECTION_ID_RDATA -> ".rdata\x00\x00"
+ | SECTION_ID_BSS -> ".bss\x00\x00\x00\x00"
+ | SECTION_ID_IMPORTS -> ".idata\x00\x00"
+ | SECTION_ID_EXPORTS -> ".edata\x00\x00"
+
+ (* There is a bizarre Microsoft COFF extension to account
+ * for longer-than-8-char section names: you emit a single
+ * '/' character then the ASCII-numeric encoding of the
+ * offset within the file's string table of the full name.
+ * So we put all our extended section names at the
+ * beginning of the string table in a very specific order
+ * and hard-wire the offsets as "names" here. You could
+ * theoretically extend this to a "new kind" of fixup
+ * reference (ASCII_POS or such), if you feel this is
+ * something you want to twiddle with.
+ *)
+
+ | SECTION_ID_DEBUG_ARANGES -> "/4\x00\x00\x00\x00\x00\x00"
+ | SECTION_ID_DEBUG_PUBNAMES -> "/19\x00\x00\x00\x00\x00"
+ | SECTION_ID_DEBUG_INFO -> "/35\x00\x00\x00\x00\x00"
+ | SECTION_ID_DEBUG_ABBREV -> "/47\x00\x00\x00\x00\x00"
+ | SECTION_ID_DEBUG_LINE -> "/61\x00\x00\x00\x00\x00"
+ | SECTION_ID_DEBUG_FRAME -> "/73\x00\x00\x00\x00\x00"
+ | SECTION_ID_NOTE_RUST -> "/86\x00\x00\x00\x00\x00"
+ end;
+
+ (* The next two pairs are only supposed to be different if the
+ file and section alignments differ. This is a stupid emitter
+ so they're not, no problem. *)
+
+ WORD (TY_u32, (M_SZ hdr_fixup)); (* "Virtual size" *)
+ WORD (TY_u32, (rva hdr_fixup)); (* "Virtual address" *)
+
+ WORD (TY_u32, (F_SZ hdr_fixup)); (* "Size of raw data" *)
+ WORD (TY_u32, (F_POS hdr_fixup)); (* "Pointer to raw data" *)
+
+ zero32; (* Reserved. *)
+ zero32; (* Reserved. *)
+ zero32; (* Reserved. *)
+
+ WORD (TY_u32, (IMM (fold_flags
+ (fun c -> match c with
+ IMAGE_SCN_CNT_CODE -> 0x20L
+ | IMAGE_SCN_CNT_INITIALIZED_DATA -> 0x40L
+ | IMAGE_SCN_CNT_UNINITIALIZED_DATA -> 0x80L
+ | IMAGE_SCN_MEM_DISCARDABLE -> 0x2000000L
+ | IMAGE_SCN_MEM_SHARED -> 0x10000000L
+ | IMAGE_SCN_MEM_EXECUTE -> 0x20000000L
+ | IMAGE_SCN_MEM_READ -> 0x40000000L
+ | IMAGE_SCN_MEM_WRITE -> 0x80000000L)
+ characteristics)))
+ |]
+;;
+
+
+(*
+
+ "Thunk" is a misnomer here; the thunk RVA is the address of a word
+ that the loader will store an address into. The stored address is
+ the address of the imported object.
+
+ So if the imported object is X, and the thunk slot is Y, the loader
+ is doing "Y = &X" and returning &Y as the thunk RVA. To load datum X
+ after the imports are resolved, given the thunk RVA R, you load
+ **R.
+
+*)
+
+type pe_import =
+ {
+ pe_import_name_fixup: fixup;
+ pe_import_name: string;
+ pe_import_address_fixup: fixup;
+ }
+
+type pe_import_dll_entry =
+ {
+ pe_import_dll_name_fixup: fixup;
+ pe_import_dll_name: string;
+ pe_import_dll_ILT_fixup: fixup;
+ pe_import_dll_IAT_fixup: fixup;
+ pe_import_dll_imports: pe_import array;
+ }
+
+ (*
+
+ The import section .idata has a mostly self-contained table
+ structure. You feed it a list of DLL entries, each of which names
+ a DLL and lists symbols in the DLL to import.
+
+ For each named symbol, a 4-byte slot will be reserved in an
+ "import lookup table" (ILT, also in this section). The slot is
+ a pointer to a string in this section giving the name.
+
+ Immediately *after* the ILT, there is an "import address table" (IAT),
+ which is initially identical to the ILT. The loader replaces the entries
+ in the IAT slots with the imported pointers at runtime.
+
+ A central directory at the start of the section lists all the the import
+ thunk tables. Each entry in the import directory is 20 bytes (5 words)
+ but only the last 2 are used: the second last is a pointer to the string
+ name of the DLL in question (string also in this section) and the last is
+ a pointer to the import thunk table itself (also in this section).
+
+ Curiously, of the 5 documents I've consulted on the nature of the
+ first 3 fields, I find a variety of interpretations.
+
+ *)
+
+let pe_import_section
+ ~(import_dir_fixup:fixup)
+ ~(dlls:pe_import_dll_entry array)
+ : frag =
+
+ let form_dir_entry
+ (entry:pe_import_dll_entry)
+ : frag =
+ SEQ [|
+ (* Note: documented opinions vary greatly about whether the
+ first, last, or both of the slots in one of these rows points
+ to the RVA of the name/hint used to look the import up. This
+ table format is a mess! *)
+ WORD (TY_u32,
+ (rva
+ entry.pe_import_dll_ILT_fixup)); (* Import lookup table. *)
+ WORD (TY_u32, (IMM 0L)); (* Timestamp, unused. *)
+ WORD (TY_u32, (IMM 0x0L)); (* Forwarder chain, unused. *)
+ WORD (TY_u32, (rva entry.pe_import_dll_name_fixup));
+ WORD (TY_u32,
+ (rva
+ entry.pe_import_dll_IAT_fixup)); (* Import address table.*)
+ |]
+ in
+
+ let form_ILT_slot
+ (import:pe_import)
+ : frag =
+ (WORD (TY_u32, (rva import.pe_import_name_fixup)))
+ in
+
+ let form_IAT_slot
+ (import:pe_import)
+ : frag =
+ (DEF (import.pe_import_address_fixup,
+ (WORD (TY_u32, (rva import.pe_import_name_fixup)))))
+ in
+
+ let form_tables_for_dll
+ (dll:pe_import_dll_entry)
+ : frag =
+ let terminator = WORD (TY_u32, (IMM 0L)) in
+ let ilt =
+ SEQ [|
+ SEQ (Array.map form_ILT_slot dll.pe_import_dll_imports);
+ terminator
+ |]
+ in
+ let iat =
+ SEQ [|
+ SEQ (Array.map form_IAT_slot dll.pe_import_dll_imports);
+ terminator
+ |]
+ in
+ if Array.length dll.pe_import_dll_imports < 1
+ then bug () "Pe.form_tables_for_dll: empty imports"
+ else
+ SEQ [|
+ DEF (dll.pe_import_dll_ILT_fixup, ilt);
+ DEF (dll.pe_import_dll_IAT_fixup, iat)
+ |]
+
+ in
+
+ let form_import_string
+ (import:pe_import)
+ : frag =
+ DEF
+ (import.pe_import_name_fixup,
+ SEQ [|
+ (* import string entries begin with a 2-byte "hint", but we just
+ set it to zero. *)
+ (WORD (TY_u16, (IMM 0L)));
+ ZSTRING import.pe_import_name;
+ (if String.length import.pe_import_name mod 2 == 0
+ then PAD 1
+ else PAD 0)
+ |])
+ in
+
+ let form_dir_entry_string
+ (dll:pe_import_dll_entry)
+ : frag =
+ DEF
+ (dll.pe_import_dll_name_fixup,
+ SEQ [| ZSTRING dll.pe_import_dll_name;
+ (if String.length dll.pe_import_dll_name mod 2 == 0
+ then PAD 1
+ else PAD 0);
+ SEQ (Array.map form_import_string dll.pe_import_dll_imports) |])
+ in
+
+ let dir = SEQ (Array.map form_dir_entry dlls) in
+ let dir_terminator = PAD 20 in
+ let tables = SEQ (Array.map form_tables_for_dll dlls) in
+ let strings = SEQ (Array.map form_dir_entry_string dlls)
+ in
+ def_aligned
+ import_dir_fixup
+ (SEQ
+ [|
+ dir;
+ dir_terminator;
+ tables;
+ strings
+ |])
+
+;;
+
+type pe_export =
+ {
+ pe_export_name_fixup: fixup;
+ pe_export_name: string;
+ pe_export_address_fixup: fixup;
+ }
+;;
+
+let pe_export_section
+ ~(sess:Session.sess)
+ ~(export_dir_fixup:fixup)
+ ~(exports:pe_export array)
+ : frag =
+ Array.sort (fun a b -> compare a.pe_export_name b.pe_export_name) exports;
+ let export_addr_table_fixup = new_fixup "export address table" in
+ let export_addr_table =
+ DEF
+ (export_addr_table_fixup,
+ SEQ
+ (Array.map
+ (fun e -> (WORD (TY_u32, rva e.pe_export_address_fixup)))
+ exports))
+ in
+ let export_name_pointer_table_fixup =
+ new_fixup "export name pointer table"
+ in
+ let export_name_pointer_table =
+ DEF
+ (export_name_pointer_table_fixup,
+ SEQ
+ (Array.map
+ (fun e -> (WORD (TY_u32, rva e.pe_export_name_fixup)))
+ exports))
+ in
+ let export_name_table_fixup = new_fixup "export name table" in
+ let export_name_table =
+ DEF
+ (export_name_table_fixup,
+ SEQ
+ (Array.map
+ (fun e -> (DEF (e.pe_export_name_fixup,
+ (ZSTRING e.pe_export_name))))
+ exports))
+ in
+ let export_ordinal_table_fixup = new_fixup "export ordinal table" in
+ let export_ordinal_table =
+ DEF
+ (export_ordinal_table_fixup,
+ SEQ
+ (Array.mapi
+ (fun i _ -> (WORD (TY_u16, IMM (Int64.of_int (i)))))
+ exports))
+ in
+ let image_name_fixup = new_fixup "image name fixup" in
+ let n_exports = IMM (Int64.of_int (Array.length exports)) in
+ let export_dir_table =
+ SEQ [|
+ WORD (TY_u32, IMM 0L); (* Flags, reserved. *)
+ WORD (TY_u32, IMM 0L); (* Timestamp, unused. *)
+ WORD (TY_u16, IMM 0L); (* Major vers., unused *)
+ WORD (TY_u16, IMM 0L); (* Minor vers., unused *)
+ WORD (TY_u32, rva image_name_fixup); (* Name RVA. *)
+ WORD (TY_u32, IMM 1L); (* Ordinal base = 1. *)
+ WORD (TY_u32, n_exports); (* # entries in EAT. *)
+ WORD (TY_u32, n_exports); (* # entries in ENPT/EOT.*)
+ WORD (TY_u32, rva export_addr_table_fixup); (* EAT *)
+ WORD (TY_u32, rva export_name_pointer_table_fixup); (* ENPT *)
+ WORD (TY_u32, rva export_ordinal_table_fixup); (* EOT *)
+ |]
+ in
+ def_aligned export_dir_fixup
+ (SEQ [|
+ export_dir_table;
+ export_addr_table;
+ export_name_pointer_table;
+ export_ordinal_table;
+ DEF (image_name_fixup,
+ ZSTRING (Session.filename_of sess.Session.sess_out));
+ export_name_table
+ |])
+;;
+
+let pe_text_section
+ ~(sess:Session.sess)
+ ~(sem:Semant.ctxt)
+ ~(start_fixup:fixup option)
+ ~(rust_start_fixup:fixup option)
+ ~(main_fn_fixup:fixup option)
+ ~(text_fixup:fixup)
+ ~(crate_code:frag)
+ : frag =
+ let startup =
+ match (start_fixup, rust_start_fixup, main_fn_fixup) with
+ (None, _, _)
+ | (_, None, _)
+ | (_, _, None) -> MARK
+ | (Some start_fixup,
+ Some rust_start_fixup,
+ Some main_fn_fixup) ->
+ let e = X86.new_emitter_without_vregs () in
+ (*
+ * We are called from the Microsoft C library startup routine,
+ * and assumed to be stdcall; so we have to clean up our own
+ * stack before returning.
+ *)
+ X86.objfile_start e
+ ~start_fixup
+ ~rust_start_fixup
+ ~main_fn_fixup
+ ~crate_fixup: sem.Semant.ctxt_crate_fixup
+ ~indirect_start: true;
+ X86.frags_of_emitted_quads sess e;
+ in
+ def_aligned
+ text_fixup
+ (SEQ [|
+ startup;
+ crate_code
+ |])
+;;
+
+let rustrt_imports sem =
+ let make_imports_for_lib (lib, tab) =
+ {
+ pe_import_dll_name_fixup = new_fixup "dll name";
+ pe_import_dll_name = (match lib with
+ REQUIRED_LIB_rustrt -> "rustrt.dll"
+ | REQUIRED_LIB_crt -> "msvcrt.dll"
+ | REQUIRED_LIB_rust ls
+ | REQUIRED_LIB_c ls -> ls.required_libname);
+ pe_import_dll_ILT_fixup = new_fixup "dll ILT";
+ pe_import_dll_IAT_fixup = new_fixup "dll IAT";
+ pe_import_dll_imports =
+ Array.of_list
+ (List.map
+ begin
+ fun (name, fixup) ->
+ {
+ pe_import_name_fixup = new_fixup "import name";
+ pe_import_name = name;
+ pe_import_address_fixup = fixup;
+ }
+ end
+ (htab_pairs tab))
+ }
+ in
+ Array.of_list
+ (List.map
+ make_imports_for_lib
+ (htab_pairs sem.Semant.ctxt_native_required))
+;;
+
+
+let crate_exports (sem:Semant.ctxt) : pe_export array =
+ let export_sym (name, fixup) =
+ {
+ pe_export_name_fixup = new_fixup "export name fixup";
+ pe_export_name = name;
+ pe_export_address_fixup = fixup;
+ }
+ in
+ let export_seg (_, tab) =
+ Array.of_list (List.map export_sym (htab_pairs tab))
+ in
+ Array.concat
+ (List.map export_seg
+ (htab_pairs sem.Semant.ctxt_native_provided))
+;;
+
+
+let emit_file
+ (sess:Session.sess)
+ (crate:Ast.crate)
+ (code:Asm.frag)
+ (data:Asm.frag)
+ (sem:Semant.ctxt)
+ (dw:Dwarf.debug_records)
+ : unit =
+
+ let all_hdrs_fixup = new_fixup "all headers" in
+ let all_init_data_fixup = new_fixup "all initialized data" in
+ let loader_hdr_fixup = new_fixup "loader header" in
+ let import_dir_fixup = new_fixup "import directory" in
+ let export_dir_fixup = new_fixup "export directory" in
+ let text_fixup = new_fixup "text section" in
+ let bss_fixup = new_fixup "bss section" in
+ let data_fixup = new_fixup "data section" in
+ let image_fixup = new_fixup "image fixup" in
+ let symtab_fixup = new_fixup "symbol table" in
+ let strtab_fixup = new_fixup "string table" in
+ let note_rust_fixup = new_fixup ".note.rust section" in
+
+ let (start_fixup, rust_start_fixup) =
+ if sess.Session.sess_library_mode
+ then (None, None)
+ else
+ (Some (new_fixup "start"),
+ Some (Semant.require_native sem REQUIRED_LIB_rustrt "rust_start"))
+ in
+
+ let header = (pe_header
+ ~machine: IMAGE_FILE_MACHINE_I386
+ ~symbol_table_fixup: symtab_fixup
+ ~number_of_sections: 8L
+ ~number_of_symbols: 0L
+ ~loader_hdr_fixup: loader_hdr_fixup
+ ~characteristics:([IMAGE_FILE_EXECUTABLE_IMAGE;
+ IMAGE_FILE_LINE_NUMS_STRIPPED;
+ IMAGE_FILE_32BIT_MACHINE;]
+ @
+ (if sess.Session.sess_library_mode
+ then [ IMAGE_FILE_DLL ]
+ else [ ])))
+ in
+ let symtab =
+ (*
+ * We're not actually presenting a "symbol table", but wish to
+ * provide a "string table" which comes immediately *after* the
+ * symbol table. It's a violation of the PE spec to put one of
+ * these in an executable file (as opposed to just loadable) but
+ * it's necessary to communicate the debug section names to GDB,
+ * and nobody else complains.
+ *)
+ (def_aligned
+ symtab_fixup
+ (def_aligned
+ strtab_fixup
+ (SEQ
+ [|
+ WORD (TY_u32, (F_SZ strtab_fixup));
+ ZSTRING ".debug_aranges";
+ ZSTRING ".debug_pubnames";
+ ZSTRING ".debug_info";
+ ZSTRING ".debug_abbrev";
+ ZSTRING ".debug_line";
+ ZSTRING ".debug_frame";
+ ZSTRING ".note.rust";
+ |])))
+ in
+ let loader_header = (pe_loader_header
+ ~text_fixup
+ ~init_data_fixup: all_init_data_fixup
+ ~size_of_uninit_data: 0L
+ ~entry_point_fixup: start_fixup
+ ~image_fixup: image_fixup
+ ~subsys: IMAGE_SUBSYSTEM_WINDOWS_CUI
+ ~all_hdrs_fixup
+ ~loader_hdr_fixup
+ ~import_dir_fixup
+ ~export_dir_fixup)
+ in
+ let text_header = (pe_section_header
+ ~id: SECTION_ID_TEXT
+ ~hdr_fixup: text_fixup)
+
+ in
+ let bss_header = (pe_section_header
+ ~id: SECTION_ID_BSS
+ ~hdr_fixup: bss_fixup)
+ in
+ let import_section = (pe_import_section
+ ~import_dir_fixup
+ ~dlls: (rustrt_imports sem))
+ in
+ let import_header = (pe_section_header
+ ~id: SECTION_ID_IMPORTS
+ ~hdr_fixup: import_dir_fixup)
+ in
+ let export_section = (pe_export_section
+ ~sess
+ ~export_dir_fixup
+ ~exports: (crate_exports sem))
+ in
+ let export_header = (pe_section_header
+ ~id: SECTION_ID_EXPORTS
+ ~hdr_fixup: export_dir_fixup)
+ in
+ let data_header = (pe_section_header
+ ~id: SECTION_ID_DATA
+ ~hdr_fixup: data_fixup)
+ in
+(*
+ let debug_aranges_header =
+ (pe_section_header
+ ~id: SECTION_ID_DEBUG_ARANGES
+ ~hdr_fixup: sem.Semant.ctxt_debug_aranges_fixup)
+ in
+ let debug_pubnames_header =
+ (pe_section_header
+ ~id: SECTION_ID_DEBUG_PUBNAMES
+ ~hdr_fixup: sem.Semant.ctxt_debug_pubnames_fixup)
+ in
+*)
+ let debug_info_header = (pe_section_header
+ ~id: SECTION_ID_DEBUG_INFO
+ ~hdr_fixup: sem.Semant.ctxt_debug_info_fixup)
+ in
+ let debug_abbrev_header = (pe_section_header
+ ~id: SECTION_ID_DEBUG_ABBREV
+ ~hdr_fixup: sem.Semant.ctxt_debug_abbrev_fixup)
+ in
+(*
+ let debug_line_header =
+ (pe_section_header
+ ~id: SECTION_ID_DEBUG_LINE
+ ~hdr_fixup: sem.Semant.ctxt_debug_line_fixup)
+ in
+ let debug_frame_header =
+ (pe_section_header
+ ~id: SECTION_ID_DEBUG_FRAME
+ ~hdr_fixup: sem.Semant.ctxt_debug_frame_fixup)
+ in
+*)
+ let note_rust_header = (pe_section_header
+ ~id: SECTION_ID_NOTE_RUST
+ ~hdr_fixup: note_rust_fixup)
+ in
+ let all_headers = (def_file_aligned
+ all_hdrs_fixup
+ (SEQ
+ [|
+ pe_msdos_header_and_padding;
+ header;
+ loader_header;
+ text_header;
+ bss_header;
+ import_header;
+ export_header;
+ data_header;
+ (*
+ debug_aranges_header;
+ debug_pubnames_header;
+ *)
+ debug_info_header;
+ debug_abbrev_header;
+ (*
+ debug_line_header;
+ debug_frame_header;
+ *)
+ note_rust_header;
+ |]))
+ in
+
+ let text_section = (pe_text_section
+ ~sem
+ ~sess
+ ~start_fixup
+ ~rust_start_fixup
+ ~main_fn_fixup: sem.Semant.ctxt_main_fn_fixup
+ ~text_fixup
+ ~crate_code: code)
+ in
+ let bss_section = def_aligned bss_fixup (BSS 0x10L)
+ in
+ let data_section = (def_aligned data_fixup
+ (SEQ [| data; symtab; |]))
+ in
+ let all_init_data = (def_aligned
+ all_init_data_fixup
+ (SEQ [| import_section;
+ export_section;
+ data_section; |]))
+ in
+(*
+ let debug_aranges_section =
+ def_aligned sem.Semant.ctxt_debug_aranges_fixup dw.Dwarf.debug_aranges
+ in
+ let debug_pubnames_section =
+ def_aligned sem.Semant.ctxt_debug_pubnames_fixup dw.Dwarf.debug_pubnames
+ in
+*)
+ let debug_info_section =
+ def_aligned sem.Semant.ctxt_debug_info_fixup dw.Dwarf.debug_info
+ in
+ let debug_abbrev_section =
+ def_aligned sem.Semant.ctxt_debug_abbrev_fixup dw.Dwarf.debug_abbrev
+ in
+(*
+ let debug_line_section =
+ def_aligned sem.Semant.ctxt_debug_line_fixup dw.Dwarf.debug_line
+ in
+ let debug_frame_section =
+ def_aligned sem.Semant.ctxt_debug_frame_fixup dw.Dwarf.debug_frame
+ in
+*)
+ let note_rust_section =
+ def_aligned note_rust_fixup
+ (Asm.note_rust_frags crate.node.Ast.crate_meta)
+ in
+
+ let all_frags = SEQ [| MEMPOS pe_image_base;
+ (def_file_aligned image_fixup
+ (SEQ [| DEF (sem.Semant.ctxt_image_base_fixup,
+ MARK);
+ all_headers;
+ text_section;
+ bss_section;
+ all_init_data;
+ (* debug_aranges_section; *)
+ (* debug_pubnames_section; *)
+ debug_info_section;
+ debug_abbrev_section;
+ (* debug_line_section; *)
+ (* debug_frame_section; *)
+ note_rust_section;
+ ALIGN_MEM (pe_mem_alignment, MARK)
+ |]
+ )
+ )
+ |]
+ in
+ write_out_frag sess true all_frags
+;;
+
+let pe_magic = "PE";;
+
+let sniff
+ (sess:Session.sess)
+ (filename:filename)
+ : asm_reader option =
+ try
+ let stat = Unix.stat filename in
+ if (stat.Unix.st_kind = Unix.S_REG) &&
+ (stat.Unix.st_size >= pe_file_alignment)
+ then
+ let ar = new_asm_reader sess filename in
+ let _ = log sess "sniffing PE file" in
+ (* PE header offset is at 0x3c in the MS-DOS compatibility header. *)
+ let _ = ar.asm_seek 0x3c in
+ let pe_hdr_off = ar.asm_get_u32() in
+ let _ = log sess "PE header offset: 0x%x" pe_hdr_off in
+
+ let _ = ar.asm_seek pe_hdr_off in
+ let pe_signature = ar.asm_get_zstr_padded 4 in
+ let _ = log sess " PE signature: '%s'" pe_signature in
+ if pe_signature = pe_magic
+ then (ar.asm_seek 0; Some ar)
+ else None
+ else
+ None
+ with
+ _ -> None
+;;
+
+
+let get_sections
+ (sess:Session.sess)
+ (ar:asm_reader)
+ : (string,(int*int)) Hashtbl.t =
+ let _ = log sess "reading sections" in
+ (* PE header offset is at 0x3c in the MS-DOS compatibility header. *)
+ let _ = ar.asm_seek 0x3c in
+ let pe_hdr_off = ar.asm_get_u32() in
+ let _ = log sess "PE header offset: 0x%x" pe_hdr_off in
+
+ let _ = ar.asm_seek pe_hdr_off in
+ let pe_signature = ar.asm_get_zstr_padded 4 in
+ let _ = log sess " PE signature: '%s'" pe_signature in
+ let _ = assert (pe_signature = pe_magic) in
+ let _ = ar.asm_adv_u16() in (* machine type *)
+
+ let num_sections = ar.asm_get_u16() in
+ let _ = log sess " num sections: %d" num_sections in
+
+ let _ = ar.asm_adv_u32() in (* timestamp *)
+
+ let symtab_off = ar.asm_get_u32() in
+ let _ = log sess " symtab offset: 0x%x" symtab_off in
+
+ let num_symbols = ar.asm_get_u32() in
+ let _ = log sess " num symbols: %d" num_symbols in
+
+ let loader_hdr_size = ar.asm_get_u16() in
+ let _ = log sess "loader header sz: %d" loader_hdr_size in
+
+ let _ = ar.asm_adv_u16() in (* flags *)
+ let sections_off = (ar.asm_get_off()) + loader_hdr_size in
+
+ let sects = Hashtbl.create 0 in
+
+ let _ =
+ ar.asm_seek sections_off;
+ for i = 0 to (num_sections - 1) do
+ (*
+ * Section-name encoding is crazy. ASCII-encoding offsets of
+ * long names. See pe_section_header for details.
+ *)
+ let sect_name =
+ let sect_name = ar.asm_get_zstr_padded 8 in
+ assert ((String.length sect_name) > 0);
+ if sect_name.[0] = '/'
+ then
+ let off_str =
+ String.sub sect_name 1 ((String.length sect_name) - 1)
+ in
+ let i = int_of_string off_str in
+ let curr = ar.asm_get_off() in
+ ar.asm_seek (symtab_off + i);
+ let ext_name = ar.asm_get_zstr() in
+ ar.asm_seek curr;
+ ext_name
+ else
+ sect_name
+ in
+ let _ = ar.asm_adv_u32() in (* virtual size *)
+ let _ = ar.asm_adv_u32() in (* virtual address *)
+ let file_sz = ar.asm_get_u32() in
+ let file_off = ar.asm_get_u32() in
+ let _ = ar.asm_adv_u32() in (* reserved *)
+ let _ = ar.asm_adv_u32() in (* reserved *)
+ let _ = ar.asm_adv_u32() in (* reserved *)
+ let _ = ar.asm_adv_u32() in (* flags *)
+ Hashtbl.add sects sect_name (file_off, file_sz);
+ log sess " section %d: %s, size %d, offset 0x%x"
+ i sect_name file_sz file_off;
+ done
+ in
+ sects
+;;
+
+(*
+ * 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/be/ra.ml b/src/boot/be/ra.ml
new file mode 100644
index 00000000..db70b21d
--- /dev/null
+++ b/src/boot/be/ra.ml
@@ -0,0 +1,664 @@
+open Il;;
+open Common;;
+
+type ctxt =
+ {
+ ctxt_sess: Session.sess;
+ ctxt_n_vregs: int;
+ ctxt_abi: Abi.abi;
+ mutable ctxt_quads: Il.quads;
+ mutable ctxt_next_spill: int;
+ mutable ctxt_next_label: int;
+ (* More state as necessary. *)
+ }
+;;
+
+let new_ctxt
+ (sess:Session.sess)
+ (quads:Il.quads)
+ (vregs:int)
+ (abi:Abi.abi)
+ : ctxt =
+ {
+ ctxt_sess = sess;
+ ctxt_quads = quads;
+ ctxt_n_vregs = vregs;
+ ctxt_abi = abi;
+ ctxt_next_spill = 0;
+ ctxt_next_label = 0;
+ }
+;;
+
+let log (cx:ctxt) =
+ Session.log "ra"
+ cx.ctxt_sess.Session.sess_log_ra
+ cx.ctxt_sess.Session.sess_log_out
+;;
+
+let iflog (cx:ctxt) (thunk:(unit -> unit)) : unit =
+ if cx.ctxt_sess.Session.sess_log_ra
+ then thunk ()
+ else ()
+;;
+
+let list_to_str list eltstr =
+ (String.concat "," (List.map eltstr (List.sort compare list)))
+;;
+
+let next_spill (cx:ctxt) : int =
+ let i = cx.ctxt_next_spill in
+ cx.ctxt_next_spill <- i + 1;
+ i
+;;
+
+let next_label (cx:ctxt) : string =
+ let i = cx.ctxt_next_label in
+ cx.ctxt_next_label <- i + 1;
+ (".L" ^ (string_of_int i))
+;;
+
+exception Ra_error of string ;;
+
+let convert_labels (cx:ctxt) : unit =
+ let quad_fixups = Array.map (fun q -> q.quad_fixup) cx.ctxt_quads in
+ let qp_code (_:Il.quad_processor) (c:Il.code) : Il.code =
+ match c with
+ Il.CodeLabel lab ->
+ let fix =
+ match quad_fixups.(lab) with
+ None ->
+ let fix = new_fixup (next_label cx) in
+ begin
+ quad_fixups.(lab) <- Some fix;
+ fix
+ end
+ | Some f -> f
+ in
+ Il.CodePtr (Il.ImmPtr (fix, Il.CodeTy))
+ | _ -> c
+ in
+ let qp = { Il.identity_processor
+ with Il.qp_code = qp_code }
+ in
+ Il.rewrite_quads qp cx.ctxt_quads;
+ Array.iteri (fun i fix ->
+ cx.ctxt_quads.(i) <- { cx.ctxt_quads.(i) with
+ Il.quad_fixup = fix })
+ quad_fixups;
+;;
+
+let convert_pre_spills
+ (cx:ctxt)
+ (mkspill:(Il.spill -> Il.mem))
+ : int =
+ let n = ref 0 in
+ let qp_mem (_:Il.quad_processor) (a:Il.mem) : Il.mem =
+ match a with
+ Il.Spill i ->
+ begin
+ if i+1 > (!n)
+ then n := i+1;
+ mkspill i
+ end
+ | _ -> a
+ in
+ let qp = Il.identity_processor in
+ let qp = { qp with
+ Il.qp_mem = qp_mem }
+ in
+ begin
+ Il.rewrite_quads qp cx.ctxt_quads;
+ !n
+ end
+;;
+
+let kill_quad (i:int) (cx:ctxt) : unit =
+ cx.ctxt_quads.(i) <-
+ { Il.deadq with
+ Il.quad_fixup = cx.ctxt_quads.(i).Il.quad_fixup }
+;;
+
+let kill_redundant_moves (cx:ctxt) : unit =
+ let process_quad i q =
+ match q.Il.quad_body with
+ Il.Unary u when
+ ((Il.is_mov u.Il.unary_op) &&
+ (Il.Cell u.Il.unary_dst) = u.Il.unary_src) ->
+ kill_quad i cx
+ | _ -> ()
+ in
+ Array.iteri process_quad cx.ctxt_quads
+;;
+
+let quad_jump_target_labels (q:quad) : Il.label list =
+ let explicits =
+ match q.Il.quad_body with
+ Il.Jmp { Il.jmp_targ = Il.CodeLabel lab } -> [ lab ]
+ | _ -> []
+ in
+ explicits @ q.quad_implicits;
+;;
+
+let quad_used_vregs (q:quad) : Il.vreg list =
+ let vregs = ref [] in
+ let qp_reg _ r =
+ match r with
+ Il.Vreg v -> (vregs := (v :: (!vregs)); r)
+ | _ -> r
+ in
+ let qp_cell_write qp c =
+ match c with
+ Il.Reg _ -> c
+ | Il.Mem (a, b) -> Il.Mem (qp.qp_mem qp a, b)
+ in
+ let qp = { Il.identity_processor with
+ Il.qp_reg = qp_reg;
+ Il.qp_cell_write = qp_cell_write }
+ in
+ ignore (Il.process_quad qp q);
+ !vregs
+;;
+
+let quad_defined_vregs (q:quad) : Il.vreg list =
+ let vregs = ref [] in
+ let qp_cell_write _ c =
+ match c with
+ Il.Reg (Il.Vreg v, _) -> (vregs := (v :: (!vregs)); c)
+ | _ -> c
+ in
+ let qp = { Il.identity_processor with
+ Il.qp_cell_write = qp_cell_write }
+ in
+ ignore (Il.process_quad qp q);
+ !vregs
+;;
+
+let quad_is_unconditional_jump (q:quad) : bool =
+ match q.Il.quad_body with
+ Il.Jmp { jmp_op = Il.JMP } -> true
+ | Il.Ret -> true
+ | _ -> false
+;;
+
+let calculate_live_bitvectors
+ (cx:ctxt)
+ : ((Bits.t array) * (Bits.t array)) =
+
+ log cx "calculating live bitvectors";
+
+ let quads = cx.ctxt_quads in
+ let n_quads = Array.length quads in
+ let n_vregs = cx.ctxt_n_vregs in
+ let new_bitv _ = Bits.create n_vregs false in
+ let (live_in_vregs:Bits.t array) = Array.init n_quads new_bitv in
+ let (live_out_vregs:Bits.t array) = Array.init n_quads new_bitv in
+
+ let (quad_used_vrs:Bits.t array) = Array.init n_quads new_bitv in
+ let (quad_defined_vrs:Bits.t array) = Array.init n_quads new_bitv in
+ let (quad_uncond_jmp:bool array) = Array.make n_quads false in
+ let (quad_jmp_targs:(Il.label list) array) = Array.make n_quads [] in
+
+ let outer_changed = ref true in
+
+ (* Working bit-vector. *)
+ let scratch = new_bitv() in
+
+ (* bit-vector helpers. *)
+ (* Setup pass. *)
+ for i = 0 to n_quads - 1 do
+ let q = quads.(i) in
+ quad_uncond_jmp.(i) <- quad_is_unconditional_jump q;
+ quad_jmp_targs.(i) <- quad_jump_target_labels q;
+ List.iter
+ (fun v -> Bits.set quad_used_vrs.(i) v true)
+ (quad_used_vregs q);
+ List.iter
+ (fun v -> Bits.set quad_defined_vrs.(i) v true)
+ (quad_defined_vregs q)
+ done;
+
+ while !outer_changed do
+ iflog cx (fun _ -> log cx "iterating outer bitvector calculation");
+ outer_changed := false;
+ for i = 0 to n_quads - 1 do
+ Bits.clear live_in_vregs.(i);
+ Bits.clear live_out_vregs.(i)
+ done;
+ let inner_changed = ref true in
+ while !inner_changed do
+ inner_changed := false;
+ iflog cx
+ (fun _ ->
+ log cx "iterating inner bitvector calculation over %d quads"
+ n_quads);
+ for i = n_quads - 1 downto 0 do
+
+ let note_change b = if b then inner_changed := true in
+ let live_in = live_in_vregs.(i) in
+ let live_out = live_out_vregs.(i) in
+ let used = quad_used_vrs.(i) in
+ let defined = quad_defined_vrs.(i) in
+
+ (* Union in the vregs we use. *)
+ note_change (Bits.union live_in used);
+
+ (* Union in all our jump targets. *)
+ List.iter
+ (fun i -> note_change (Bits.union live_out live_in_vregs.(i)))
+ (quad_jmp_targs.(i));
+
+ (* Union in our block successor if we have one *)
+ if i < (n_quads - 1) && (not (quad_uncond_jmp.(i)))
+ then note_change (Bits.union live_out live_in_vregs.(i+1));
+
+ (* Propagate live-out to live-in on anything we don't define. *)
+ ignore (Bits.copy scratch defined);
+ Bits.invert scratch;
+ ignore (Bits.intersect scratch live_out);
+ note_change (Bits.union live_in scratch);
+
+ done
+ done;
+ let kill_mov_to_dead_target i q =
+ match q.Il.quad_body with
+ Il.Unary { Il.unary_op=uop;
+ Il.unary_dst=Il.Reg (Il.Vreg v, _) }
+ when
+ ((Il.is_mov uop) &&
+ not (Bits.get live_out_vregs.(i) v)) ->
+ begin
+ kill_quad i cx;
+ outer_changed := true;
+ end
+ | _ -> ()
+ in
+ Array.iteri kill_mov_to_dead_target quads
+ done;
+ iflog cx
+ begin
+ fun _ ->
+ log cx "finished calculating live bitvectors";
+ log cx "=========================";
+ for q = 0 to n_quads - 1 do
+ let buf = Buffer.create 128 in
+ for v = 0 to (n_vregs - 1)
+ do
+ if ((Bits.get live_in_vregs.(q) v)
+ && (Bits.get live_out_vregs.(q) v))
+ then Printf.bprintf buf " %-2d" v
+ else Buffer.add_string buf " "
+ done;
+ log cx "[%6d] live vregs: %s" q (Buffer.contents buf)
+ done;
+ log cx "========================="
+ end;
+ (live_in_vregs, live_out_vregs)
+;;
+
+
+let is_end_of_basic_block (q:quad) : bool =
+ match q.Il.quad_body with
+ Il.Jmp _ -> true
+ | Il.Ret -> true
+ | _ -> false
+;;
+
+let is_beginning_of_basic_block (q:quad) : bool =
+ match q.Il.quad_fixup with
+ None -> false
+ | Some _ -> true
+;;
+
+let dump_quads cx =
+ let f = cx.ctxt_abi.Abi.abi_str_of_hardreg in
+ let len = (Array.length cx.ctxt_quads) - 1 in
+ let ndigits_of n = (int_of_float (log10 (float_of_int n))) in
+ let padded_num n maxnum =
+ let ndigits = ndigits_of n in
+ let maxdigits = ndigits_of maxnum in
+ let pad = String.make (maxdigits - ndigits) ' ' in
+ Printf.sprintf "%s%d" pad n
+ in
+ let padded_str str maxlen =
+ let pad = String.make (maxlen - (String.length str)) ' ' in
+ Printf.sprintf "%s%s" pad str
+ in
+ let maxlablen = ref 0 in
+ for i = 0 to len
+ do
+ let q = cx.ctxt_quads.(i) in
+ match q.quad_fixup with
+ None -> ()
+ | Some f ->
+ maxlablen := max (!maxlablen) ((String.length f.fixup_name) + 1)
+ done;
+ for i = 0 to len
+ do
+ let q = cx.ctxt_quads.(i) in
+ let qs = (string_of_quad f q) in
+ let lab = match q.quad_fixup with
+ None -> ""
+ | Some f -> f.fixup_name ^ ":"
+ in
+ log cx "[%s] %s %s" (padded_num i len) (padded_str lab (!maxlablen)) qs
+ done
+;;
+
+let calculate_vreg_constraints (cx:ctxt) : Bits.t array =
+ let abi = cx.ctxt_abi in
+ let n_vregs = cx.ctxt_n_vregs in
+ let n_hregs = abi.Abi.abi_n_hardregs in
+ let constraints = Array.init n_vregs (fun _ -> Bits.create n_hregs true) in
+ Array.iteri
+ begin
+ fun i q ->
+ abi.Abi.abi_constrain_vregs q constraints;
+ iflog cx
+ begin
+ fun _ ->
+ let hr_str = cx.ctxt_abi.Abi.abi_str_of_hardreg in
+ log cx "constraints for quad %d = %s"
+ i (string_of_quad hr_str q);
+ let qp_reg _ r =
+ begin
+ match r with
+ Il.Hreg _ -> ()
+ | Il.Vreg v ->
+ let hregs = Bits.to_list constraints.(v) in
+ log cx "<v%d> constrained to hregs: [%s]"
+ v (list_to_str hregs hr_str)
+ end;
+ r
+ in
+ ignore (Il.process_quad { Il.identity_processor with
+ Il.qp_reg = qp_reg } q)
+ end;
+ end
+ cx.ctxt_quads;
+ constraints
+;;
+
+(* Simple local register allocator. Nothing fancy. *)
+let reg_alloc
+ (sess:Session.sess)
+ (quads:Il.quads)
+ (vregs:int)
+ (abi:Abi.abi) =
+ try
+ let cx = new_ctxt sess quads vregs abi in
+ let _ =
+ iflog cx
+ begin
+ fun _ ->
+ log cx "un-allocated quads:";
+ dump_quads cx
+ end
+ in
+
+ (* Work out pre-spilled slots and allocate 'em. *)
+ let spill_slot (s:Il.spill) = abi.Abi.abi_spill_slot s in
+ let n_pre_spills = convert_pre_spills cx spill_slot in
+
+ let (live_in_vregs, live_out_vregs) =
+ Session.time_inner "RA liveness" sess
+ (fun _ -> calculate_live_bitvectors cx)
+ in
+ let (vreg_constraints:Bits.t array) = (* vreg idx -> hreg bits.t *)
+ calculate_vreg_constraints cx
+ in
+ let inactive_hregs = ref [] in (* [hreg] *)
+ let active_hregs = ref [] in (* [hreg] *)
+ let dirty_vregs = Hashtbl.create 0 in (* vreg -> () *)
+ let hreg_to_vreg = Hashtbl.create 0 in (* hreg -> vreg *)
+ let vreg_to_hreg = Hashtbl.create 0 in (* vreg -> hreg *)
+ let vreg_to_spill = Hashtbl.create 0 in (* vreg -> spill *)
+ let (word_ty:Il.scalar_ty) = Il.ValTy abi.Abi.abi_word_bits in
+ let vreg_spill_cell v =
+ Il.Mem ((spill_slot (Hashtbl.find vreg_to_spill v)),
+ Il.ScalarTy word_ty)
+ in
+ let newq = ref [] in
+ let fixup = ref None in
+ let prepend q =
+ newq := {q with quad_fixup = !fixup} :: (!newq);
+ fixup := None
+ in
+ let hr h = Il.Reg (Il.Hreg h, Il.voidptr_t) in
+ let hr_str = cx.ctxt_abi.Abi.abi_str_of_hardreg in
+ let clean_hreg i hreg =
+ if (Hashtbl.mem hreg_to_vreg hreg) &&
+ (hreg < cx.ctxt_abi.Abi.abi_n_hardregs)
+ then
+ let vreg = Hashtbl.find hreg_to_vreg hreg in
+ if Hashtbl.mem dirty_vregs vreg
+ then
+ begin
+ Hashtbl.remove dirty_vregs vreg;
+ if (Bits.get (live_out_vregs.(i)) vreg)
+ then
+ let spill_idx =
+ if Hashtbl.mem vreg_to_spill vreg
+ then Hashtbl.find vreg_to_spill vreg
+ else
+ begin
+ let s = next_spill cx in
+ Hashtbl.replace vreg_to_spill vreg s;
+ s
+ end
+ in
+ let spill_mem = spill_slot spill_idx in
+ let spill_cell = Il.Mem (spill_mem, Il.ScalarTy word_ty) in
+ log cx "spilling <%d> from %s to %s"
+ vreg (hr_str hreg) (string_of_mem hr_str spill_mem);
+ prepend (Il.mk_quad
+ (Il.umov spill_cell (Il.Cell (hr hreg))));
+ else ()
+ end
+ else ()
+ else ()
+ in
+
+ let inactivate_hreg hreg =
+ if (Hashtbl.mem hreg_to_vreg hreg) &&
+ (hreg < cx.ctxt_abi.Abi.abi_n_hardregs)
+ then
+ let vreg = Hashtbl.find hreg_to_vreg hreg in
+ Hashtbl.remove vreg_to_hreg vreg;
+ Hashtbl.remove hreg_to_vreg hreg;
+ active_hregs := List.filter (fun x -> x != hreg) (!active_hregs);
+ inactive_hregs := hreg :: (!inactive_hregs);
+ else ()
+ in
+
+ let spill_specific_hreg i hreg =
+ clean_hreg i hreg;
+ inactivate_hreg hreg
+ in
+
+ let rec select_constrained
+ (constraints:Bits.t)
+ (hregs:Il.hreg list)
+ : Il.hreg option =
+ match hregs with
+ [] -> None
+ | h::hs ->
+ if Bits.get constraints h
+ then Some h
+ else select_constrained constraints hs
+ in
+
+ let spill_constrained constrs i =
+ match select_constrained constrs (!active_hregs) with
+ None ->
+ raise (Ra_error ("unable to spill according to constraint"));
+ | Some h ->
+ begin
+ spill_specific_hreg i h;
+ h
+ end
+ in
+
+ let all_hregs = Bits.create abi.Abi.abi_n_hardregs true in
+
+ let spill_all_regs i =
+ while (!active_hregs) != []
+ do
+ let _ = spill_constrained all_hregs i in
+ ()
+ done
+ in
+
+ let reload vreg hreg =
+ if Hashtbl.mem vreg_to_spill vreg
+ then
+ prepend (Il.mk_quad
+ (Il.umov
+ (hr hreg)
+ (Il.Cell (vreg_spill_cell vreg))))
+ else ()
+ in
+
+ let use_vreg def i vreg =
+ if Hashtbl.mem vreg_to_hreg vreg
+ then
+ begin
+ let h = Hashtbl.find vreg_to_hreg vreg in
+ iflog cx (fun _ -> log cx "found cached assignment %s for <v%d>"
+ (hr_str h) vreg);
+ h
+ end
+ else
+ let hreg =
+ let constrs = vreg_constraints.(vreg) in
+ match select_constrained constrs (!inactive_hregs) with
+ None ->
+ let h = spill_constrained constrs i in
+ iflog cx
+ (fun _ -> log cx "selected %s to spill and use for <v%d>"
+ (hr_str h) vreg);
+ h
+ | Some h ->
+ iflog cx (fun _ -> log cx "selected inactive %s for <v%d>"
+ (hr_str h) vreg);
+ h
+ in
+ inactive_hregs :=
+ List.filter (fun x -> x != hreg) (!inactive_hregs);
+ active_hregs := (!active_hregs) @ [hreg];
+ Hashtbl.replace hreg_to_vreg hreg vreg;
+ Hashtbl.replace vreg_to_hreg vreg hreg;
+ if def
+ then ()
+ else
+ reload vreg hreg;
+ hreg
+ in
+ let qp_reg def i _ r =
+ match r with
+ Il.Hreg h -> (spill_specific_hreg i h; r)
+ | Il.Vreg v -> (Il.Hreg (use_vreg def i v))
+ in
+ let qp_cell def i qp c =
+ match c with
+ Il.Reg (r, b) -> Il.Reg (qp_reg def i qp r, b)
+ | Il.Mem (a, b) ->
+ let qp = { qp with Il.qp_reg = qp_reg false i } in
+ Il.Mem (qp.qp_mem qp a, b)
+ in
+ let qp i = { Il.identity_processor with
+ Il.qp_cell_read = qp_cell false i;
+ Il.qp_cell_write = qp_cell true i;
+ Il.qp_reg = qp_reg false i }
+ in
+ cx.ctxt_next_spill <- n_pre_spills;
+ convert_labels cx;
+ for i = 0 to cx.ctxt_abi.Abi.abi_n_hardregs - 1
+ do
+ inactive_hregs := i :: (!inactive_hregs)
+ done;
+ for i = 0 to (Array.length cx.ctxt_quads) - 1
+ do
+ let quad = cx.ctxt_quads.(i) in
+ let clobbers = cx.ctxt_abi.Abi.abi_clobbers quad in
+ let used = quad_used_vregs quad in
+ let defined = quad_defined_vregs quad in
+ begin
+ if List.exists (fun def -> List.mem def clobbers) defined
+ then raise (Ra_error ("clobber and defined sets overlap"));
+ iflog cx
+ begin
+ fun _ ->
+ let hr (v:int) : string =
+ if Hashtbl.mem vreg_to_hreg v
+ then hr_str (Hashtbl.find vreg_to_hreg v)
+ else "??"
+ in
+ let vr_str (v:int) : string =
+ Printf.sprintf "v%d=%s" v (hr v)
+ in
+ let lstr lab ls fn =
+ if List.length ls = 0
+ then ()
+ else log cx "\t%s: [%s]" lab (list_to_str ls fn)
+ in
+ log cx "processing quad %d = %s"
+ i (string_of_quad hr_str quad);
+ (lstr "dirt" (htab_keys dirty_vregs) vr_str);
+ (lstr "clob" clobbers hr_str);
+ (lstr "in" (Bits.to_list live_in_vregs.(i)) vr_str);
+ (lstr "out" (Bits.to_list live_out_vregs.(i)) vr_str);
+ (lstr "use" used vr_str);
+ (lstr "def" defined vr_str);
+ end;
+ List.iter (clean_hreg i) clobbers;
+ if is_beginning_of_basic_block quad
+ then
+ begin
+ spill_all_regs i;
+ fixup := quad.quad_fixup;
+ prepend (Il.process_quad (qp i) quad)
+ end
+ else
+ begin
+ fixup := quad.quad_fixup;
+ let newq = (Il.process_quad (qp i) quad) in
+ begin
+ if is_end_of_basic_block quad
+ then spill_all_regs i
+ else ()
+ end;
+ prepend newq
+ end
+ end;
+ List.iter inactivate_hreg clobbers;
+ List.iter (fun i -> Hashtbl.replace dirty_vregs i ()) defined;
+ done;
+ cx.ctxt_quads <- Array.of_list (List.rev (!newq));
+ kill_redundant_moves cx;
+
+ iflog cx
+ begin
+ fun _ ->
+ log cx "spills: %d pre-spilled, %d total"
+ n_pre_spills cx.ctxt_next_spill;
+ log cx "register-allocated quads:";
+ dump_quads cx;
+ end;
+ (cx.ctxt_quads, cx.ctxt_next_spill)
+
+ with
+ Ra_error s ->
+ Session.fail sess "RA Error: %s" s;
+ (quads, 0)
+
+;;
+
+
+(*
+ * 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/be/x86.ml b/src/boot/be/x86.ml
new file mode 100644
index 00000000..01b7e299
--- /dev/null
+++ b/src/boot/be/x86.ml
@@ -0,0 +1,2205 @@
+(*
+ * x86/ia32 instructions have 6 parts:
+ *
+ * [pre][op][modrm][sib][disp][imm]
+ *
+ * [pre] = 0..4 bytes of prefix
+ * [op] = 1..3 byte opcode
+ * [modrm] = 0 or 1 byte: [mod:2][reg/op:3][r/m:3]
+ * [sib] = 0 or 1 byte: [scale:2][index:3][base:3]
+ * [disp] = 1, 2 or 4 byte displacement
+ * [imm] = 1, 2 or 4 byte immediate
+ *
+ * So between 1 and 17 bytes total.
+ *
+ * We're not going to use sib, but modrm is worth discussing.
+ *
+ * The high two bits of modrm denote an addressing mode. The modes are:
+ *
+ * 00 - "mostly" *(reg)
+ * 01 - "mostly" *(reg) + disp8
+ * 10 - "mostly" *(reg) + disp32
+ * 11 - reg
+ *
+ * The next-lowest 3 bits denote a specific register, or a subopcode if
+ * there is a fixed register or only one operand. The instruction format
+ * reference will say "/<n>" for some number n, if a fixed subopcode is used.
+ * It'll say "/r" if the instruction uses this field to specify a register.
+ *
+ * The registers specified in this field are:
+ *
+ * 000 - EAX or XMM0
+ * 001 - ECX or XMM1
+ * 010 - EDX or XMM2
+ * 011 - EBX or XMM3
+ * 100 - ESP or XMM4
+ * 101 - EBP or XMM5
+ * 110 - ESI or XMM6
+ * 111 - EDI or XMM7
+ *
+ * The final low 3 bits denote sub-modes of the primary mode selected
+ * with the top 2 bits. In particular, they "mostly" select the reg that is
+ * to be used for effective address calculation.
+ *
+ * For the most part, these follow the same numbering order: EAX, ECX, EDX,
+ * EBX, ESP, EBP, ESI, EDI. There are two unusual deviations from the rule
+ * though:
+ *
+ * - In primary modes 00, 01 and 10, r/m=100 means "use SIB byte". You can
+ * use (unscaled) ESP as the base register in these modes by appending the
+ * SIB byte 0x24. We do that in our rm_r operand-encoder function.
+ *
+ * - In primary mode 00, r/m=101 means "just disp32", no register is
+ * involved. There is no way to use EBP in primary mode 00. If you try, we
+ * just decay into a mode 01 with an appended 8-bit immediate displacement.
+ *
+ * Some opcodes are written 0xNN +rd. This means "we decided to chew up a
+ * whole pile of opcodes here, with each opcode including a hard-wired
+ * reference to a register". For example, POP is "0x58 +rd", which means that
+ * the 1-byte insns 0x58..0x5f are chewed up for "POP EAX" ... "POP EDI"
+ * (again, the canonical order of register numberings)
+ *)
+
+(*
+ * Notes on register availability of x86:
+ *
+ * There are 8 GPRs but we use 2 of them for specific purposes:
+ *
+ * - ESP always points to the current stack frame.
+ * - EBP always points to the current frame base.
+ *
+ * We tell IL that we have 6 GPRs then, and permit most register-register ops
+ * on any of these 6, mostly-unconstrained.
+ *
+ *)
+
+open Common;;
+
+exception Unrecognized
+;;
+
+let modrm m rm reg_or_subopcode =
+ if (((m land 0b11) != m) or
+ ((rm land 0b111) != rm) or
+ ((reg_or_subopcode land 0b111) != reg_or_subopcode))
+ then raise (Invalid_argument "X86.modrm_deref")
+ else
+ ((((m land 0b11) lsl 6)
+ lor
+ (rm land 0b111))
+ lor
+ ((reg_or_subopcode land 0b111) lsl 3))
+;;
+
+let modrm_deref_reg = modrm 0b00 ;;
+let modrm_deref_disp32 = modrm 0b00 0b101 ;;
+let modrm_deref_reg_plus_disp8 = modrm 0b01 ;;
+let modrm_deref_reg_plus_disp32 = modrm 0b10 ;;
+let modrm_reg = modrm 0b11 ;;
+
+let slash0 = 0;;
+let slash1 = 1;;
+let slash2 = 2;;
+let slash3 = 3;;
+let slash4 = 4;;
+let slash5 = 5;;
+let slash6 = 6;;
+let slash7 = 7;;
+
+
+(*
+ * Translate an IL-level hwreg number from 0..nregs into the 3-bit code number
+ * used through the mod r/m byte and /r sub-register specifiers of the x86
+ * ISA.
+ *
+ * See "Table 2-2: 32-Bit Addressing Forms with the ModR/M Byte", in the IA32
+ * Architecture Software Developer's Manual, volume 2a.
+ *)
+
+let eax = 0
+let ecx = 1
+let ebx = 2
+let esi = 3
+let edi = 4
+let edx = 5
+let ebp = 6
+let esp = 7
+
+let code_eax = 0b000;;
+let code_ecx = 0b001;;
+let code_edx = 0b010;;
+let code_ebx = 0b011;;
+let code_esp = 0b100;;
+let code_ebp = 0b101;;
+let code_esi = 0b110;;
+let code_edi = 0b111;;
+
+let reg r =
+ match r with
+ 0 -> code_eax
+ | 1 -> code_ecx
+ | 2 -> code_ebx
+ | 3 -> code_esi
+ | 4 -> code_edi
+ | 5 -> code_edx
+ (* Never assigned by the register allocator, but synthetic code uses
+ them *)
+ | 6 -> code_ebp
+ | 7 -> code_esp
+ | _ -> raise (Invalid_argument "X86.reg")
+;;
+
+
+let dwarf_eax = 0;;
+let dwarf_ecx = 1;;
+let dwarf_edx = 2;;
+let dwarf_ebx = 3;;
+let dwarf_esp = 4;;
+let dwarf_ebp = 5;;
+let dwarf_esi = 6;;
+let dwarf_edi = 7;;
+
+let dwarf_reg r =
+ match r with
+ 0 -> dwarf_eax
+ | 1 -> dwarf_ecx
+ | 2 -> dwarf_ebx
+ | 3 -> dwarf_esi
+ | 4 -> dwarf_edi
+ | 5 -> dwarf_edx
+ | 6 -> dwarf_ebp
+ | 7 -> dwarf_esp
+ | _ -> raise (Invalid_argument "X86.dwarf_reg")
+
+let reg_str r =
+ match r with
+ 0 -> "eax"
+ | 1 -> "ecx"
+ | 2 -> "ebx"
+ | 3 -> "esi"
+ | 4 -> "edi"
+ | 5 -> "edx"
+ | 6 -> "ebp"
+ | 7 -> "esp"
+ | _ -> raise (Invalid_argument "X86.reg_str")
+;;
+
+(* This is a basic ABI. You might need to customize it by platform. *)
+let (n_hardregs:int) = 6;;
+let (n_callee_saves:int) = 4;;
+
+
+let is_ty32 (ty:Il.scalar_ty) : bool =
+ match ty with
+ Il.ValTy (Il.Bits32) -> true
+ | Il.AddrTy _ -> true
+ | _ -> false
+;;
+
+let is_r32 (c:Il.cell) : bool =
+ match c with
+ Il.Reg (_, st) -> is_ty32 st
+ | _ -> false
+;;
+
+let is_rm32 (c:Il.cell) : bool =
+ match c with
+ Il.Mem (_, Il.ScalarTy st) -> is_ty32 st
+ | Il.Reg (_, st) -> is_ty32 st
+ | _ -> false
+;;
+
+let is_ty8 (ty:Il.scalar_ty) : bool =
+ match ty with
+ Il.ValTy (Il.Bits8) -> true
+ | _ -> false
+;;
+
+let is_m32 (c:Il.cell) : bool =
+ match c with
+ Il.Mem (_, Il.ScalarTy st) -> is_ty32 st
+ | _ -> false
+;;
+
+let is_m8 (c:Il.cell) : bool =
+ match c with
+ Il.Mem (_, Il.ScalarTy st) -> is_ty8 st
+ | _ -> false
+;;
+
+let is_ok_r8 (r:Il.hreg) : bool =
+ (r == eax || r == ebx || r == ecx || r == edx)
+;;
+
+let is_r8 (c:Il.cell) : bool =
+ match c with
+ Il.Reg (Il.Hreg r, st) when is_ok_r8 r -> is_ty8 st
+ | _ -> false
+;;
+
+let is_rm8 (c:Il.cell) : bool =
+ match c with
+ Il.Mem (_, Il.ScalarTy st) -> is_ty8 st
+ | _ -> is_r8 c
+;;
+
+let prealloc_quad (quad':Il.quad') : Il.quad' =
+ let target_cell reg c =
+ Il.Reg (Il.Hreg reg, Il.cell_scalar_ty c)
+ in
+ let target_operand reg op =
+ Il.Cell (Il.Reg (Il.Hreg reg, Il.operand_scalar_ty op))
+ in
+
+ let target_bin_to_hreg bin dst src =
+ { bin with
+ Il.binary_rhs = target_operand src bin.Il.binary_rhs;
+ Il.binary_lhs = target_operand dst bin.Il.binary_lhs;
+ Il.binary_dst = target_cell dst bin.Il.binary_dst }
+ in
+
+ let target_cmp cmp =
+ match cmp.Il.cmp_lhs with
+ (* Immediate LHS we force to eax. *)
+ Il.Imm _ ->
+ { cmp with
+ Il.cmp_lhs = target_operand eax cmp.Il.cmp_lhs }
+ | _ -> cmp
+ in
+
+ match quad' with
+ Il.Binary bin ->
+ begin
+ Il.Binary
+ begin
+ match bin.Il.binary_op with
+ Il.IMUL | Il.UMUL
+ | Il.IDIV | Il.UDIV -> target_bin_to_hreg bin eax ecx
+ | Il.IMOD | Il.UMOD -> target_bin_to_hreg bin eax ecx
+ | _ -> bin
+ end
+ end
+
+ | Il.Cmp cmp -> Il.Cmp (target_cmp cmp)
+
+ | Il.Call c ->
+ let ty = Il.cell_scalar_ty c.Il.call_dst in
+ Il.Call { c with
+ Il.call_dst = Il.Reg ((Il.Hreg eax), ty) }
+
+ | Il.Lea le ->
+ begin
+ match (le.Il.lea_dst, le.Il.lea_src) with
+ (Il.Reg (_, dst_ty), Il.ImmPtr _)
+ when is_ty32 dst_ty ->
+ Il.Lea { le with
+ Il.lea_dst = Il.Reg (Il.Hreg eax, dst_ty) }
+ | _ -> quad'
+ end
+
+ | x -> x
+;;
+
+let constrain_vregs (q:Il.quad) (hregs:Bits.t array) : unit =
+
+ let involves_8bit_cell =
+ let b = ref false in
+ let qp_cell _ c =
+ match c with
+ Il.Reg (_, Il.ValTy Il.Bits8)
+ | Il.Mem (_, Il.ScalarTy (Il.ValTy Il.Bits8)) ->
+ (b := true; c)
+ | _ -> c
+ in
+ ignore (Il.process_quad { Il.identity_processor with
+ Il.qp_cell_read = qp_cell;
+ Il.qp_cell_write = qp_cell } q);
+ !b
+ in
+
+ let qp_mem _ m = m in
+ let qp_cell _ c =
+ begin
+ match c with
+ Il.Reg (Il.Vreg v, _) when involves_8bit_cell ->
+ (* 8-bit register cells must only be al, cl, dl, bl.
+ * Not esi/edi. *)
+ let hv = hregs.(v) in
+ List.iter (fun bad -> Bits.set hv bad false) [esi; edi]
+ | _ -> ()
+ end;
+ c
+ in
+ begin
+ match q.Il.quad_body with
+ Il.Binary b ->
+ begin
+ match b.Il.binary_op with
+ (* Shifts *)
+ | Il.LSL | Il.LSR | Il.ASR ->
+ begin
+ match b.Il.binary_rhs with
+ Il.Cell (Il.Reg (Il.Vreg v, _)) ->
+ let hv = hregs.(v) in
+ (* Shift src has to be ecx. *)
+ List.iter
+ (fun bad -> Bits.set hv bad false)
+ [eax; edx; ebx; esi; edi]
+ | _ -> ()
+ end
+ | _ -> ()
+ end
+ | _ -> ()
+ end;
+ ignore
+ (Il.process_quad { Il.identity_processor with
+ Il.qp_mem = qp_mem;
+ Il.qp_cell_read = qp_cell;
+ Il.qp_cell_write = qp_cell } q)
+;;
+
+
+let clobbers (quad:Il.quad) : Il.hreg list =
+ match quad.Il.quad_body with
+ Il.Binary bin ->
+ begin
+ match bin.Il.binary_op with
+ Il.IMUL | Il.UMUL
+ | Il.IDIV | Il.UDIV -> [ edx ]
+ | Il.IMOD | Il.UMOD -> [ edx ]
+ | _ -> []
+ end
+ | Il.Unary un ->
+ begin
+ match un.Il.unary_op with
+ Il.ZERO -> [ eax; edi; ecx ]
+ | _ -> [ ]
+ end
+ | Il.Call _ -> [ eax; ecx; edx; ]
+ | Il.Regfence -> [ eax; ecx; ebx; edx; edi; esi; ]
+ | _ -> []
+;;
+
+
+let word_sz = 4L
+;;
+
+let word_bits = Il.Bits32
+;;
+
+let word_ty = TY_u32
+;;
+
+let annotate (e:Il.emitter) (str:string) =
+ Hashtbl.add e.Il.emit_annotations e.Il.emit_pc str
+;;
+
+let c (c:Il.cell) : Il.operand = Il.Cell c ;;
+let r (r:Il.reg) : Il.cell = Il.Reg ( r, (Il.ValTy word_bits) ) ;;
+let h (x:Il.hreg) : Il.reg = Il.Hreg x ;;
+let rc (x:Il.hreg) : Il.cell = r (h x) ;;
+let ro (x:Il.hreg) : Il.operand = c (rc x) ;;
+let vreg (e:Il.emitter) : (Il.reg * Il.cell) =
+ let vr = Il.next_vreg e in
+ (vr, (Il.Reg (vr, (Il.ValTy word_bits))))
+;;
+let imm (x:Asm.expr64) : Il.operand =
+ Il.Imm (x, word_ty)
+;;
+let immi (x:int64) : Il.operand =
+ imm (Asm.IMM x)
+;;
+
+let imm_byte (x:Asm.expr64) : Il.operand =
+ Il.Imm (x, TY_u8)
+;;
+let immi_byte (x:int64) : Il.operand =
+ imm_byte (Asm.IMM x)
+;;
+
+
+let byte_off_n (i:int) : Asm.expr64 =
+ Asm.IMM (Int64.of_int i)
+;;
+
+let byte_n (reg:Il.reg) (i:int) : Il.cell =
+ let imm = byte_off_n i in
+ let mem = Il.RegIn (reg, Some imm) in
+ Il.Mem (mem, Il.ScalarTy (Il.ValTy Il.Bits8))
+;;
+
+let word_off_n (i:int) : Asm.expr64 =
+ Asm.IMM (Int64.mul (Int64.of_int i) word_sz)
+;;
+
+let word_at (reg:Il.reg) : Il.cell =
+ let mem = Il.RegIn (reg, None) in
+ Il.Mem (mem, Il.ScalarTy (Il.ValTy word_bits))
+;;
+
+let word_at_off (reg:Il.reg) (off:Asm.expr64) : Il.cell =
+ let mem = Il.RegIn (reg, Some off) in
+ Il.Mem (mem, Il.ScalarTy (Il.ValTy word_bits))
+;;
+
+let word_n (reg:Il.reg) (i:int) : Il.cell =
+ word_at_off reg (word_off_n i)
+;;
+
+let reg_codeptr (reg:Il.reg) : Il.code =
+ Il.CodePtr (Il.Cell (Il.Reg (reg, Il.AddrTy Il.CodeTy)))
+;;
+
+let word_n_low_byte (reg:Il.reg) (i:int) : Il.cell =
+ let imm = word_off_n i in
+ let mem = Il.RegIn (reg, Some imm) in
+ Il.Mem (mem, Il.ScalarTy (Il.ValTy Il.Bits8))
+;;
+
+let wordptr_n (reg:Il.reg) (i:int) : Il.cell =
+ let imm = word_off_n i in
+ let mem = Il.RegIn (reg, Some imm) in
+ Il.Mem (mem, Il.ScalarTy (Il.AddrTy (Il.ScalarTy (Il.ValTy word_bits))))
+;;
+
+let get_element_ptr = Il.get_element_ptr word_bits reg_str ;;
+
+let save_callee_saves (e:Il.emitter) : unit =
+ Il.emit e (Il.Push (ro ebp));
+ Il.emit e (Il.Push (ro edi));
+ Il.emit e (Il.Push (ro esi));
+ Il.emit e (Il.Push (ro ebx));
+;;
+
+
+let restore_callee_saves (e:Il.emitter) : unit =
+ Il.emit e (Il.Pop (rc ebx));
+ Il.emit e (Il.Pop (rc esi));
+ Il.emit e (Il.Pop (rc edi));
+ Il.emit e (Il.Pop (rc ebp));
+;;
+
+
+(* restores registers from the frame base without updating esp:
+ * - sets ebp, edi, esi, ebx to stored values from frame base
+ * - sets `retpc' register to stored retpc from frame base
+ * - sets `base' register to current fp
+ *)
+let restore_frame_base (e:Il.emitter) (base:Il.reg) (retpc:Il.reg) : unit =
+ let emit = Il.emit e in
+ let mov dst src = emit (Il.umov dst src) in
+ mov (r base) (ro ebp);
+ mov (rc ebx) (c (word_at base));
+ mov (rc esi) (c (word_n base 1));
+ mov (rc edi) (c (word_n base 2));
+ mov (rc ebp) (c (word_n base 3));
+ mov (r retpc) (c (word_n base 4));
+;;
+
+
+(*
+ * Our arrangement on x86 is this:
+ *
+ * *ebp+20+(4*N) = [argN ]
+ * ...
+ * *ebp+24 = [arg1 ] = task ptr
+ * *ebp+20 = [arg0 ] = out ptr
+ * *ebp+16 = [retpc ]
+ * *ebp+12 = [old_ebp]
+ * *ebp+8 = [old_edi]
+ * *ebp+4 = [old_esi]
+ * *ebp = [old_ebx]
+ *
+ * For x86-cdecl:
+ *
+ * %eax, %ecx, %edx are "caller save" registers
+ * %ebp, %ebx, %esi, %edi are "callee save" registers
+ *
+ *)
+
+let frame_base_words = 5 (* eip,ebp,edi,esi,ebx *) ;;
+let frame_base_sz = Int64.mul (Int64.of_int frame_base_words) word_sz;;
+
+let frame_info_words = 2 (* crate ptr, crate-rel frame info disp *) ;;
+let frame_info_sz = Int64.mul (Int64.of_int frame_info_words) word_sz;;
+
+let implicit_arg_words = 2 (* task ptr,out ptr *);;
+let implicit_args_sz = Int64.mul (Int64.of_int implicit_arg_words) word_sz;;
+
+let out_ptr = wordptr_n (Il.Hreg ebp) (frame_base_words);;
+let task_ptr = wordptr_n (Il.Hreg ebp) (frame_base_words+1);;
+let ty_param_n i =
+ wordptr_n (Il.Hreg ebp) (frame_base_words + implicit_arg_words + i);;
+
+let spill_slot (i:Il.spill) : Il.mem =
+ let imm = (Asm.IMM
+ (Int64.neg
+ (Int64.add frame_info_sz
+ (Int64.mul word_sz
+ (Int64.of_int (i+1))))))
+ in
+ Il.RegIn ((Il.Hreg ebp), Some imm)
+;;
+
+
+let get_next_pc_thunk_fixup = new_fixup "glue$get_next_pc"
+;;
+
+let emit_get_next_pc_thunk (e:Il.emitter) : unit =
+ let sty = Il.AddrTy Il.CodeTy in
+ let rty = Il.ScalarTy sty in
+ let deref_esp = Il.Mem (Il.RegIn (Il.Hreg esp, None), rty) in
+ let eax = (Il.Reg (Il.Hreg eax, sty)) in
+ Il.emit_full e (Some get_next_pc_thunk_fixup) []
+ (Il.umov eax (Il.Cell deref_esp));
+ Il.emit e Il.Ret;
+;;
+
+let get_next_pc_thunk : (Il.reg * fixup * (Il.emitter -> unit)) =
+ (Il.Hreg eax, get_next_pc_thunk_fixup, emit_get_next_pc_thunk)
+;;
+
+let emit_c_call
+ (e:Il.emitter)
+ (ret:Il.cell)
+ (tmp1:Il.reg)
+ (tmp2:Il.reg)
+ (nabi:nabi)
+ (in_prologue:bool)
+ (fptr:Il.code)
+ (args:Il.operand array)
+ : unit =
+
+ let emit = Il.emit e in
+ let mov dst src = emit (Il.umov dst src) in
+ let binary op dst imm = emit (Il.binary op dst (c dst) (immi imm)) in
+
+ (* rust calls get task as arg0 *)
+ let args =
+ if nabi.nabi_convention = CONV_rust
+ then Array.append [| c task_ptr |] args
+ else args
+ in
+ let nargs = Array.length args in
+ let arg_sz = Int64.mul (Int64.of_int nargs) word_sz
+ in
+
+ mov (r tmp1) (c task_ptr); (* tmp1 = task from argv[-1] *)
+ mov (r tmp2) (ro esp); (* tmp2 = esp *)
+ mov (* task->rust_sp = tmp2 *)
+ (word_n tmp1 Abi.task_field_rust_sp)
+ (c (r tmp2));
+ mov (* esp = task->runtime_sp *)
+ (rc esp)
+ (c (word_n tmp1 Abi.task_field_runtime_sp));
+
+ binary Il.SUB (rc esp) arg_sz; (* make room on the stack *)
+ binary Il.AND (rc esp) (* and 16-byte align sp *)
+ 0xfffffffffffffff0L;
+
+ Array.iteri
+ begin
+ fun i (arg:Il.operand) -> (* write args to C stack *)
+ match arg with
+ Il.Cell (Il.Mem (a, ty)) ->
+ begin
+ match a with
+ Il.RegIn (Il.Hreg base, off) when base == esp ->
+ mov (r tmp1) (c (Il.Mem (Il.RegIn (tmp2, off), ty)));
+ mov (word_n (h esp) i) (c (r tmp1));
+ | _ ->
+ mov (r tmp1) arg;
+ mov (word_n (h esp) i) (c (r tmp1));
+ end
+ | _ ->
+ mov (word_n (h esp) i) arg
+ end
+ args;
+
+ match ret with
+ Il.Mem (Il.RegIn (Il.Hreg base, _), _) when base == esp ->
+ assert (not in_prologue);
+
+ (* If ret is esp-relative, use a temporary register until we
+ switched stacks. *)
+
+ emit (Il.call (r tmp1) fptr);
+ mov (r tmp2) (c task_ptr);
+ mov (rc esp) (c (word_n tmp2 Abi.task_field_rust_sp));
+ mov ret (c (r tmp1));
+
+ | _ when in_prologue ->
+ (*
+ * We have to do something a little surprising here:
+ * we're doing a 'grow' call so ebp is going to point
+ * into a dead stack frame on call-return. So we
+ * temporarily store task-ptr into ebp and then reload
+ * esp *and* ebp via ebp->rust_sp on the other side of
+ * the call.
+ *)
+ mov (rc ebp) (c task_ptr);
+ emit (Il.call ret fptr);
+ mov (rc esp) (c (word_n (h ebp) Abi.task_field_rust_sp));
+ mov (rc ebp) (ro esp);
+
+ | _ ->
+ emit (Il.call ret fptr);
+ mov (r tmp2) (c task_ptr);
+ mov (rc esp) (c (word_n tmp2 Abi.task_field_rust_sp));
+;;
+
+let emit_void_prologue_call
+ (e:Il.emitter)
+ (nabi:nabi)
+ (fn:fixup)
+ (args:Il.operand array)
+ : unit =
+ let callee = Abi.load_fixup_codeptr e (h eax) fn true nabi.nabi_indirect in
+ emit_c_call e (rc eax) (h edx) (h ecx) nabi true callee args
+;;
+
+let emit_native_call
+ (e:Il.emitter)
+ (ret:Il.cell)
+ (nabi:nabi)
+ (fn:fixup)
+ (args:Il.operand array)
+ : unit =
+
+ let (tmp1, _) = vreg e in
+ let (tmp2, _) = vreg e in
+ let (freg, _) = vreg e in
+ let callee = Abi.load_fixup_codeptr e freg fn true nabi.nabi_indirect in
+ emit_c_call e ret tmp1 tmp2 nabi false callee args
+;;
+
+let emit_native_void_call
+ (e:Il.emitter)
+ (nabi:nabi)
+ (fn:fixup)
+ (args:Il.operand array)
+ : unit =
+
+ let (ret, _) = vreg e in
+ emit_native_call e (r ret) nabi fn args
+;;
+
+let emit_native_call_in_thunk
+ (e:Il.emitter)
+ (ret:Il.cell)
+ (nabi:nabi)
+ (fn:Il.operand)
+ (args:Il.operand array)
+ : unit =
+
+ let emit = Il.emit e in
+ let mov dst src = emit (Il.umov dst src) in
+
+ begin
+ match fn with
+ (*
+ * NB: old path, remove when/if you're sure you don't
+ * want native-linker-symbol-driven requirements.
+ *)
+ Il.ImmPtr (fix, _) ->
+ let code =
+ Abi.load_fixup_codeptr e (h eax) fix true nabi.nabi_indirect
+ in
+ emit_c_call e (rc eax) (h edx) (h ecx) nabi false code args;
+
+ | _ ->
+ (*
+ * NB: new path, ignores nabi_indirect, assumes
+ * indirect via pointer from upcall_require_c_sym
+ * or crate cache.
+ *)
+ mov (rc eax) fn;
+ let cell = Il.Reg (h eax, Il.AddrTy Il.CodeTy) in
+ let fptr = Il.CodePtr (Il.Cell cell) in
+ emit_c_call e (rc eax) (h edx) (h ecx) nabi false fptr args;
+ end;
+
+ match ret with
+ Il.Reg (r, _) -> mov (word_at r) (ro eax)
+ | _ -> mov (rc edx) (c ret);
+ mov (word_at (h edx)) (ro eax)
+;;
+
+let unwind_glue
+ (e:Il.emitter)
+ (nabi:nabi)
+ (exit_task_fixup:fixup)
+ : unit =
+
+ let fp_n = word_n (Il.Hreg ebp) in
+ let edx_n = word_n (Il.Hreg edx) in
+ let emit = Il.emit e in
+ let mov dst src = emit (Il.umov dst src) in
+ let push x = emit (Il.Push x) in
+ let pop x = emit (Il.Pop x) in
+ let add x y = emit (Il.binary Il.ADD (rc x) (ro x) (ro y)) in
+ let codefix fix = Il.CodePtr (Il.ImmPtr (fix, Il.CodeTy)) in
+ let mark fix = Il.emit_full e (Some fix) [] Il.Dead in
+ let glue_field = Abi.frame_glue_fns_field_drop in
+
+ let repeat_jmp_fix = new_fixup "repeat jump" in
+ let skip_jmp_fix = new_fixup "skip jump" in
+ let exit_jmp_fix = new_fixup "exit jump" in
+
+ mov (rc edx) (c task_ptr); (* switch back to rust stack *)
+ mov
+ (rc esp)
+ (c (edx_n Abi.task_field_rust_sp));
+
+ mark repeat_jmp_fix;
+
+ mov (rc esi) (c (fp_n (-1))); (* esi <- crate ptr *)
+ mov (rc edx) (c (fp_n (-2))); (* edx <- frame glue functions. *)
+ emit (Il.cmp (ro edx) (immi 0L));
+
+ emit
+ (Il.jmp Il.JE
+ (codefix skip_jmp_fix)); (* if struct* is nonzero *)
+ add edx esi; (* add crate ptr to disp. *)
+ mov
+ (rc ecx)
+ (c (edx_n glue_field)); (* ecx <- drop glue *)
+ emit (Il.cmp (ro ecx) (immi 0L));
+
+ emit
+ (Il.jmp Il.JE
+ (codefix skip_jmp_fix)); (* if glue-fn is nonzero *)
+ add ecx esi; (* add crate ptr to disp. *)
+ push (ro ebp); (* frame-to-drop *)
+ push (c task_ptr); (* form usual call to glue *)
+ push (immi 0L); (* outptr *)
+ emit (Il.call (rc eax)
+ (reg_codeptr (h ecx))); (* call glue_fn, trashing eax. *)
+ pop (rc eax);
+ pop (rc eax);
+ pop (rc eax);
+
+ mark skip_jmp_fix;
+ mov (rc edx) (c (fp_n 3)); (* load next fp (callee-saves[3]) *)
+ emit (Il.cmp (ro edx) (immi 0L));
+ emit (Il.jmp Il.JE
+ (codefix exit_jmp_fix)); (* if nonzero *)
+ mov (rc ebp) (ro edx); (* move to next frame *)
+ emit (Il.jmp Il.JMP
+ (codefix repeat_jmp_fix)); (* loop *)
+
+ (* exit path. *)
+ mark exit_jmp_fix;
+
+ let callee =
+ Abi.load_fixup_codeptr
+ e (h eax) exit_task_fixup false nabi.nabi_indirect
+ in
+ emit_c_call
+ e (rc eax) (h edx) (h ecx) nabi false callee [| (c task_ptr) |];
+;;
+
+(* Puts result in eax; clobbers ecx, edx in the process. *)
+let rec calculate_sz (e:Il.emitter) (size:size) : 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
+ let pop x = emit (Il.Pop x) in
+ let neg x = emit (Il.unary Il.NEG (rc x) (ro x)) in
+ let bnot x = emit (Il.unary Il.NOT (rc x) (ro x)) in
+ let band x y = emit (Il.binary Il.AND (rc x) (ro x) (ro y)) in
+ let add x y = emit (Il.binary Il.ADD (rc x) (ro x) (ro y)) in
+ 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;
+ push (ro eax);
+ calculate_sz e a;
+ pop (rc ecx);
+ in
+ match size with
+ SIZE_fixed i ->
+ mov (rc eax) (immi i)
+
+ | SIZE_fixup_mem_sz f ->
+ mov (rc eax) (imm (Asm.M_SZ f))
+
+ | SIZE_fixup_mem_pos f ->
+ mov (rc eax) (imm (Asm.M_POS f))
+
+ | SIZE_param_size i ->
+ 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));
+ mov (rc eax) (Il.Cell (word_n (h eax) Abi.tydesc_field_align))
+
+ | SIZE_rt_neg a ->
+ calculate_sz e a;
+ neg eax
+
+ | SIZE_rt_add (a, b) ->
+ eax_gets_a_and_ecx_gets_b a b;
+ add eax ecx
+
+ | SIZE_rt_mul (a, b) ->
+ eax_gets_a_and_ecx_gets_b a b;
+ mul eax ecx
+
+ | SIZE_rt_max (a, b) ->
+ eax_gets_a_and_ecx_gets_b a b;
+ emit (Il.cmp (ro eax) (ro ecx));
+ let jmp_pc = e.Il.emit_pc in
+ emit (Il.jmp Il.JAE Il.CodeNone);
+ mov (rc eax) (ro ecx);
+ Il.patch_jump e jmp_pc e.Il.emit_pc;
+
+ | SIZE_rt_align (align, off) ->
+ (*
+ * calculate off + pad where:
+ *
+ * pad = (align - (off mod align)) mod align
+ *
+ * In our case it's always a power of two,
+ * so we can just do:
+ *
+ * mask = align-1
+ * off += mask
+ * off &= ~mask
+ *
+ *)
+ eax_gets_a_and_ecx_gets_b off align;
+ subi ecx 1L;
+ add eax ecx;
+ bnot ecx;
+ band eax ecx;
+;;
+
+let rec size_calculation_stack_highwater (size:size) : int =
+ match size with
+ SIZE_fixed _
+ | SIZE_fixup_mem_sz _
+ | SIZE_fixup_mem_pos _
+ | SIZE_param_size _
+ | SIZE_param_align _ -> 0
+ | SIZE_rt_neg a ->
+ (size_calculation_stack_highwater a)
+ | SIZE_rt_max (a, b) ->
+ (size_calculation_stack_highwater a)
+ + (size_calculation_stack_highwater b)
+ | SIZE_rt_add (a, b)
+ | SIZE_rt_mul (a, b)
+ | SIZE_rt_align (a, b) ->
+ (size_calculation_stack_highwater a)
+ + (size_calculation_stack_highwater b)
+ + 1
+;;
+
+let boundary_sz =
+ (Asm.IMM
+ (Int64.add (* Extra non-frame room: *)
+ frame_base_sz (* to safely enter the next frame, *)
+ frame_base_sz)) (* and make a 'grow' upcall there. *)
+;;
+
+let stack_growth_check
+ (e:Il.emitter)
+ (nabi:nabi)
+ (grow_task_fixup:fixup)
+ (growsz:Il.operand)
+ (grow_jmp:Il.label option)
+ (restart_pc:Il.label)
+ (end_reg:Il.reg) (*
+ * stack limit on entry,
+ * new stack pointer on exit
+ *)
+ (tmp_reg:Il.reg) (* temporary (trashed) *)
+ : unit =
+ let emit = Il.emit e in
+ let mov dst src = emit (Il.umov dst src) in
+ let add dst src = emit (Il.binary Il.ADD dst (Il.Cell dst) src) in
+ let sub dst src = emit (Il.binary Il.SUB dst (Il.Cell dst) src) in
+ mov (r tmp_reg) (ro esp); (* tmp = esp *)
+ sub (r tmp_reg) growsz; (* tmp -= size-request *)
+ emit (Il.cmp (c (r end_reg)) (c (r tmp_reg)));
+ (*
+ * Jump *over* 'grow' upcall on non-underflow:
+ * if end_reg <= tmp_reg
+ *)
+
+ let bypass_grow_upcall_jmp_pc = e.Il.emit_pc in
+ emit (Il.jmp Il.JBE Il.CodeNone);
+
+ begin
+ match grow_jmp with
+ None -> ()
+ | Some j -> Il.patch_jump e j e.Il.emit_pc
+ end;
+ (* Extract growth-amount from tmp_reg. *)
+ mov (r end_reg) (ro esp);
+ sub (r end_reg) (c (r tmp_reg));
+ add (r end_reg) (Il.Imm (boundary_sz, word_ty));
+ (* Perform 'grow' upcall, then restart frame-entry. *)
+ emit_void_prologue_call e nabi grow_task_fixup [| c (r end_reg) |];
+ emit (Il.jmp Il.JMP (Il.CodeLabel restart_pc));
+ Il.patch_jump e bypass_grow_upcall_jmp_pc e.Il.emit_pc
+;;
+
+let fn_prologue
+ (e:Il.emitter)
+ (framesz:size)
+ (callsz:size)
+ (nabi:nabi)
+ (grow_task_fixup:fixup)
+ : unit =
+
+ let esi_n = word_n (h esi) in
+ let emit = Il.emit e in
+ let mov dst src = emit (Il.umov dst src) in
+ let add dst src = emit (Il.binary Il.ADD dst (Il.Cell dst) src) in
+ let sub dst src = emit (Il.binary Il.SUB dst (Il.Cell dst) src) in
+
+ (* We may be in a dynamic-sized frame. This makes matters complex,
+ * as we can't just perform a simple growth check in terms of a
+ * static size. The check is against a dynamic size, and we need to
+ * calculate that size.
+ *
+ * Unlike size-calculations in 'trans', we do not use vregs to
+ * calculate the frame size; instead we use a PUSH/POP stack-machine
+ * translation that doesn't disturb the registers we're
+ * somewhat-carefully *using* during frame setup.
+ *
+ * This only pushes the problem back a little ways though: we still
+ * need to be sure we have enough room to do the PUSH/POP
+ * calculation. We refer to this amount of space as the 'primordial'
+ * frame size, which can *thankfully* be calculated exactly from the
+ * arithmetic expression we're aiming to calculate. So we make room
+ * for the primordial frame, run the calculation of the full dynamic
+ * frame size, then make room *again* for this dynamic size.
+ *
+ * Our caller reserved enough room for us to push our own frame-base,
+ * as well as the frame-base that it will cost to do an upcall.
+ *)
+
+ (*
+ * After we save callee-saves, We have a stack like this:
+ *
+ * | ... |
+ * | caller frame |
+ * | + spill |
+ * | caller arg K |
+ * | ... |
+ * | caller arg 0 |
+ * | retpc | <-- sp we received, top of callee frame
+ * | callee save 1 |
+ * | ... |
+ * | callee save N | <-- ebp and esp after saving callee-saves
+ * | ... |
+ * | callee frame |
+ * | + spill |
+ * | callee arg J |
+ * | ... |
+ * | callee arg 0 | <-- bottom of callee frame
+ * | next retpc |
+ * | next save 1 |
+ * | ... |
+ * | next save N | <-- bottom of region we must reserve
+ * | ... |
+ *
+ * A "frame base" is the retpc and set of callee-saves.
+ *
+ * We need to reserve room for our frame *and* the next frame-base, because
+ * we're going to be blindly entering the next frame-base (pushing eip and
+ * callee-saves) before we perform the next check.
+ *)
+
+ (*
+ * We double the reserved callsz because we need a 'temporary tail-call
+ * region' above the actual call region, in case there's a drop call at the
+ * end of assembling the tail-call args and before copying them to callee
+ * position.
+ *)
+
+ let callsz = add_sz callsz callsz in
+ let n_glue_args = Int64.of_int Abi.worst_case_glue_call_args in
+ let n_glue_words = Int64.mul word_sz n_glue_args in
+
+ (*
+ * Add in *another* word to handle an extra-awkward spill of the
+ * callee address that might occur during an indirect tail call.
+ *)
+ let callsz = add_sz (SIZE_fixed word_sz) callsz in
+
+ (*
+ * Add in enough words for a glue-call (these occur underneath esp)
+ *)
+ let callsz = add_sz (SIZE_fixed n_glue_words) callsz in
+
+ (*
+ * Cumulative dynamic-frame size.
+ *)
+ let call_and_frame_sz = add_sz callsz framesz in
+
+ (* Already have room to save regs on entry. *)
+ save_callee_saves e;
+
+ let restart_pc = e.Il.emit_pc in
+
+ mov (rc ebp) (ro esp); (* Establish frame base. *)
+ mov (rc esi) (c task_ptr); (* esi = task *)
+ mov
+ (rc esi)
+ (c (esi_n Abi.task_field_stk)); (* esi = task->stk *)
+ add (rc esi) (imm
+ (Asm.ADD
+ ((word_off_n Abi.stk_field_data),
+ boundary_sz)));
+
+ let (dynamic_frame_sz, dynamic_grow_jmp) =
+ match Il.size_to_expr64 call_and_frame_sz with
+ None ->
+ begin
+ let primordial_frame_sz =
+ Asm.IMM
+ (Int64.mul word_sz
+ (Int64.of_int
+ (size_calculation_stack_highwater
+ call_and_frame_sz)))
+ in
+ (* Primordial size-check. *)
+ mov (rc edi) (ro esp); (* edi = esp *)
+ sub (* edi -= size-request *)
+ (rc edi)
+ (imm primordial_frame_sz);
+ emit (Il.cmp (ro esi) (ro edi));
+
+ (* Jump to 'grow' upcall on underflow: if esi (bottom) is >
+ edi (proposed-esp) *)
+
+ let primordial_underflow_jmp_pc = e.Il.emit_pc in
+ emit (Il.jmp Il.JA Il.CodeNone);
+
+ (* Calculate dynamic frame size. *)
+ calculate_sz e call_and_frame_sz;
+ ((ro eax), Some primordial_underflow_jmp_pc)
+ end
+ | Some e -> ((imm e), None)
+ in
+
+ (* "Full" frame size-check. *)
+ stack_growth_check e nabi grow_task_fixup
+ dynamic_frame_sz dynamic_grow_jmp restart_pc (h esi) (h edi);
+
+
+ (* Establish a frame, wherever we landed. *)
+ sub (rc esp) dynamic_frame_sz;
+
+ (* Zero the frame.
+ *
+ * FIXME: this is awful, will go away when we have proper CFI.
+ *)
+
+ mov (rc edi) (ro esp);
+ mov (rc ecx) dynamic_frame_sz;
+ emit (Il.unary Il.ZERO (word_at (h edi)) (ro ecx));
+
+ (* Move esp back up over the glue region. *)
+ add (rc esp) (immi n_glue_words);
+;;
+
+
+let fn_epilogue (e:Il.emitter) : unit =
+
+ (* Tear down existing frame. *)
+ let emit = Il.emit e in
+ let mov dst src = emit (Il.umov dst src) in
+ mov (rc esp) (ro ebp);
+ restore_callee_saves e;
+ emit Il.Ret;
+;;
+
+let inline_memcpy
+ (e:Il.emitter)
+ (n_bytes:int64)
+ (dst_ptr:Il.reg)
+ (src_ptr:Il.reg)
+ (tmp_reg:Il.reg)
+ (ascending:bool)
+ : unit =
+ let emit = Il.emit e in
+ let mov dst src = emit (Il.umov dst src) in
+ let bpw = Int64.to_int word_sz in
+ let w = Int64.to_int (Int64.div n_bytes word_sz) in
+ let b = Int64.to_int (Int64.rem n_bytes word_sz) in
+ if ascending
+ then
+ begin
+ for i = 0 to (w-1) do
+ mov (r tmp_reg) (c (word_n src_ptr i));
+ mov (word_n dst_ptr i) (c (r tmp_reg));
+ done;
+ for i = 0 to (b-1) do
+ let off = (w*bpw) + i in
+ mov (r tmp_reg) (c (byte_n src_ptr off));
+ mov (byte_n dst_ptr off) (c (r tmp_reg));
+ done;
+ end
+ else
+ begin
+ for i = (b-1) downto 0 do
+ let off = (w*bpw) + i in
+ mov (r tmp_reg) (c (byte_n src_ptr off));
+ mov (byte_n dst_ptr off) (c (r tmp_reg));
+ done;
+ for i = (w-1) downto 0 do
+ mov (r tmp_reg) (c (word_n src_ptr i));
+ mov (word_n dst_ptr i) (c (r tmp_reg));
+ done;
+ end
+;;
+
+
+
+let fn_tail_call
+ (e:Il.emitter)
+ (caller_callsz:int64)
+ (caller_argsz:int64)
+ (callee_code:Il.code)
+ (callee_argsz:int64)
+ : unit =
+ let emit = Il.emit e in
+ let binary op dst imm = emit (Il.binary op dst (c dst) (immi imm)) in
+ let mov dst src = emit (Il.umov dst src) in
+ let argsz_diff = Int64.sub caller_argsz callee_argsz in
+ let callee_spill_cell = word_at_off (h esp) (Asm.IMM caller_callsz) in
+
+ (*
+ * Our outgoing arguments were prepared in a region above the call region;
+ * this is reserved for the purpose of making tail-calls *only*, so we do
+ * not collide with glue calls we had to make while dropping the frame,
+ * after assembling our arg region.
+ *
+ * Thus, esp points to the "normal" arg region, and we need to move it
+ * to point to the tail-call arg region. To make matters simple, both
+ * regions are the same size, one atop the other.
+ *)
+
+ annotate e "tail call: move esp to temporary tail call arg-prep area";
+ binary Il.ADD (rc esp) caller_callsz;
+
+ (*
+ * If we're given a non-ImmPtr callee, we may need to move it to a known
+ * cell to avoid clobbering its register while we do the argument shuffle
+ * below.
+ *
+ * Sadly, we are too register-starved to just flush our callee to a reg;
+ * so we carve out an extra word of the temporary call-region and use
+ * it.
+ *
+ * This is ridiculous, but works.
+ *)
+ begin
+ match callee_code with
+ Il.CodePtr (Il.Cell c) ->
+ annotate e "tail call: spill callee-ptr to temporary memory";
+ mov callee_spill_cell (Il.Cell c);
+
+ | _ -> ()
+ end;
+
+ (* edx <- ebp; restore ebp, edi, esi, ebx; ecx <- retpc *)
+ annotate e "tail call: restore callee-saves from frame base";
+ restore_frame_base e (h edx) (h ecx);
+ (* move edx past frame base and adjust for difference in call sizes *)
+ annotate e "tail call: adjust temporary fp";
+ binary Il.ADD (rc edx) (Int64.add frame_base_sz argsz_diff);
+
+ (*
+ * stack grows downwards; copy from high to low
+ *
+ * bpw = word_sz
+ * w = floor(callee_argsz / word_sz)
+ * b = callee_argsz % word_sz
+ *
+ * byte copies:
+ * +------------------------+
+ * | |
+ * +------------------------+ <-- base + (w * word_sz) + (b - 1)
+ * . .
+ * +------------------------+
+ * | |
+ * +------------------------+ <-- base + (w * word_sz) + (b - b)
+ * word copies: =
+ * +------------------------+ <-- base + ((w-0) * word_sz)
+ * | bytes |
+ * | (w-1)*bpw..w*bpw-1 |
+ * +------------------------+ <-- base + ((w-1) * word_sz)
+ * | bytes |
+ * | (w-2)*bpw..(w-1)*bpw-1 |
+ * +------------------------+ <-- base + ((w-2) * word_sz)
+ * . .
+ * . .
+ * . .
+ * +------------------------+
+ * | bytes |
+ * | 0..bpw - 1 |
+ * +------------------------+ <-- base + ((w-w) * word_sz)
+ *)
+
+ annotate e "tail call: move arg-tuple up to top of frame";
+ (* NOTE: must copy top-to-bottom in case the regions overlap *)
+ inline_memcpy e callee_argsz (h edx) (h esp) (h eax) false;
+
+ (*
+ * We're done with eax now; so in the case where we had to spill
+ * our callee codeptr, we can reload it into eax here and rewrite
+ * our callee into *eax.
+ *)
+ let callee_code =
+ match callee_code with
+ Il.CodePtr (Il.Cell _) ->
+ annotate e "tail call: reload callee-ptr from temporary memory";
+ mov (rc eax) (Il.Cell callee_spill_cell);
+ reg_codeptr (h eax)
+
+ | _ -> callee_code
+ in
+
+
+ (* esp <- edx *)
+ annotate e "tail call: adjust stack pointer";
+ mov (rc esp) (ro edx);
+ (* PUSH ecx (retpc) *)
+ annotate e "tail call: push retpc";
+ emit (Il.Push (ro ecx));
+ (* JMP callee_code *)
+ emit (Il.jmp Il.JMP callee_code);
+;;
+
+
+let loop_info_field_retpc = 0;;
+let loop_info_field_sp = 1;;
+let loop_info_field_fp = 2;;
+
+let self_args_cell (self_args_rty:Il.referent_ty) : Il.cell =
+ Il.Mem (Il.RegIn (h ebp, Some (Asm.IMM frame_base_sz)), self_args_rty)
+;;
+
+let activate_glue (e:Il.emitter) : unit =
+ (*
+ * This is a bit of glue-code. It should be emitted once per
+ * compilation unit.
+ *
+ * - save regs on C stack
+ * - align sp on a 16-byte boundary
+ * - save sp to task.runtime_sp (runtime_sp is thus always aligned)
+ * - load saved task sp (switch stack)
+ * - restore saved task regs
+ * - return to saved task pc
+ *
+ * Our incoming stack looks like this:
+ *
+ * *esp+4 = [arg1 ] = task ptr
+ * *esp = [retpc ]
+ *)
+
+ let sp_n = word_n (Il.Hreg esp) in
+ let edx_n = word_n (Il.Hreg edx) in
+ let emit = Il.emit e in
+ let mov dst src = emit (Il.umov dst src) in
+ let binary op dst imm = emit (Il.binary op dst (c dst) (immi imm)) in
+
+ mov (rc edx) (c (sp_n 1)); (* edx <- task *)
+ save_callee_saves e;
+ mov
+ (edx_n Abi.task_field_runtime_sp)
+ (ro esp); (* task->runtime_sp <- esp *)
+ mov
+ (rc esp)
+ (c (edx_n Abi.task_field_rust_sp)); (* esp <- task->rust_sp *)
+
+ (*
+ * There are two paths we can arrive at this code from:
+ *
+ *
+ * 1. We are activating a task for the first time. When we switch into
+ * the task stack and 'ret' to its first instruction, we'll start
+ * doing whatever the first instruction says. Probably saving
+ * registers and starting to establish a frame. Harmless stuff,
+ * doesn't look at task->rust_sp again except when it clobbers it
+ * during a later upcall.
+ *
+ *
+ * 2. We are resuming a task that was descheduled by the yield glue
+ * below. When we switch into the task stack and 'ret', we'll be
+ * ret'ing to a very particular instruction:
+ *
+ * "esp <- task->rust_sp"
+ *
+ * this is the first instruction we 'ret' to after this glue, because
+ * it is the first instruction following *any* upcall, and the task
+ * we are activating was descheduled mid-upcall.
+ *
+ * Unfortunately for us, we have already restored esp from
+ * task->rust_sp and are about to eat the 5 words off the top of it.
+ *
+ *
+ * | ... | <-- where esp will be once we restore + ret, below,
+ * | retpc | and where we'd *like* task->rust_sp to wind up.
+ * | ebp |
+ * | edi |
+ * | esi |
+ * | ebx | <-- current task->rust_sp == current esp
+ *
+ *
+ * This is a problem. If we return to "esp <- task->rust_sp" it will
+ * push esp back down by 5 words. This manifests as a rust stack that
+ * grows by 5 words on each yield/reactivate. Not good.
+ *
+ * So what we do here is just adjust task->rust_sp up 5 words as
+ * well, to mirror the movement in esp we're about to perform. That
+ * way the "esp <- task->rust_sp" we 'ret' to below will be a
+ * no-op. Esp won't move, and the task's stack won't grow.
+ *)
+
+ binary Il.ADD (edx_n Abi.task_field_rust_sp)
+ (Int64.mul (Int64.of_int (n_callee_saves + 1)) word_sz);
+
+ (**** IN TASK STACK ****)
+ restore_callee_saves e;
+ emit Il.Ret;
+ (***********************)
+ ()
+;;
+
+let yield_glue (e:Il.emitter) : unit =
+
+ (* More glue code, this time the 'bottom half' of yielding.
+ *
+ * We arrived here because an upcall decided to deschedule the
+ * running task. So the upcall's return address got patched to the
+ * first instruction of this glue code.
+ *
+ * When the upcall does 'ret' it will come here, and its esp will be
+ * pointing to the last argument pushed on the C stack before making
+ * the upcall: the 0th argument to the upcall, which is always the
+ * task ptr performing the upcall. That's where we take over.
+ *
+ * Our goal is to complete the descheduling
+ *
+ * - Switch over to the task stack temporarily.
+ *
+ * - Save the task's callee-saves onto the task stack.
+ * (the task is now 'descheduled', safe to set aside)
+ *
+ * - Switch *back* to the C stack.
+ *
+ * - Restore the C-stack callee-saves.
+ *
+ * - Return to the caller on the C stack that activated the task.
+ *
+ *)
+ let esp_n = word_n (Il.Hreg esp) in
+ let edx_n = word_n (Il.Hreg edx) in
+ let emit = Il.emit e in
+ let mov dst src = emit (Il.umov dst src) in
+
+ mov
+ (rc edx) (c (esp_n 0)); (* edx <- arg0 (task) *)
+ mov
+ (rc esp)
+ (c (edx_n Abi.task_field_rust_sp)); (* esp <- task->rust_sp *)
+ save_callee_saves e;
+ mov (* task->rust_sp <- esp *)
+ (edx_n Abi.task_field_rust_sp)
+ (ro esp);
+ mov
+ (rc esp)
+ (c (edx_n Abi.task_field_runtime_sp)); (* esp <- task->runtime_sp *)
+
+ (**** IN C STACK ****)
+ restore_callee_saves e;
+ emit Il.Ret;
+ (***********************)
+ ()
+;;
+
+
+let push_pos32 (e:Il.emitter) (fix:fixup) : unit =
+ let (reg, _, _) = get_next_pc_thunk in
+ Abi.load_fixup_addr e reg fix Il.OpaqueTy;
+ Il.emit e (Il.Push (Il.Cell (Il.Reg (reg, Il.AddrTy Il.OpaqueTy))))
+;;
+
+let objfile_start
+ (e:Il.emitter)
+ ~(start_fixup:fixup)
+ ~(rust_start_fixup:fixup)
+ ~(main_fn_fixup:fixup)
+ ~(crate_fixup:fixup)
+ ~(indirect_start:bool)
+ : unit =
+ let ebp_n = word_n (Il.Hreg ebp) in
+ let emit = Il.emit e in
+ let mov dst src = emit (Il.umov dst src) in
+ let push_pos32 = push_pos32 e in
+ Il.emit_full e (Some start_fixup) [] Il.Dead;
+ save_callee_saves e;
+ mov (rc ebp) (ro esp);
+
+ (* If we're very lucky, the platform will have left us with
+ * something sensible in the startup stack like so:
+ *
+ * *ebp+24 = [arg1 ] = argv
+ * *ebp+20 = [arg0 ] = argc
+ * *ebp+16 = [retpc ]
+ * *ebp+12 = [old_ebp]
+ * *ebp+8 = [old_edi]
+ * *ebp+4 = [old_esi]
+ * *ebp = [old_ebx]
+ *
+ * This is not the case everywhere, but we start with this
+ * assumption and correct it in the runtime library.
+ *)
+
+ (* Copy argv. *)
+ mov (rc eax) (c (ebp_n (2 + n_callee_saves)));
+ Il.emit e (Il.Push (ro eax));
+
+ (* Copy argc. *)
+ mov (rc eax) (c (ebp_n (1 + n_callee_saves)));
+ Il.emit e (Il.Push (ro eax));
+
+ push_pos32 crate_fixup;
+ push_pos32 main_fn_fixup;
+ let fptr =
+ Abi.load_fixup_codeptr e (h eax) rust_start_fixup true indirect_start
+ in
+ Il.emit e (Il.call (rc eax) fptr);
+ Il.emit e (Il.Pop (rc ecx));
+ Il.emit e (Il.Pop (rc ecx));
+ Il.emit e (Il.Pop (rc ecx));
+ Il.emit e (Il.Pop (rc ecx));
+ Il.emit e (Il.umov (rc esp) (ro ebp));
+ restore_callee_saves e;
+ Il.emit e Il.Ret;
+;;
+
+let (abi:Abi.abi) =
+ {
+ Abi.abi_word_sz = word_sz;
+ Abi.abi_word_bits = word_bits;
+ Abi.abi_word_ty = word_ty;
+
+ Abi.abi_is_2addr_machine = true;
+ Abi.abi_has_pcrel_data = false;
+ Abi.abi_has_pcrel_code = true;
+
+ Abi.abi_n_hardregs = n_hardregs;
+ Abi.abi_str_of_hardreg = reg_str;
+ Abi.abi_prealloc_quad = prealloc_quad;
+ Abi.abi_constrain_vregs = constrain_vregs;
+
+ Abi.abi_emit_fn_prologue = fn_prologue;
+ Abi.abi_emit_fn_epilogue = fn_epilogue;
+ Abi.abi_emit_fn_tail_call = fn_tail_call;
+ Abi.abi_clobbers = clobbers;
+
+ Abi.abi_emit_native_call = emit_native_call;
+ Abi.abi_emit_native_void_call = emit_native_void_call;
+ Abi.abi_emit_native_call_in_thunk = emit_native_call_in_thunk;
+ Abi.abi_emit_inline_memcpy = inline_memcpy;
+
+ Abi.abi_activate = activate_glue;
+ Abi.abi_yield = yield_glue;
+ Abi.abi_unwind = unwind_glue;
+ Abi.abi_get_next_pc_thunk = Some get_next_pc_thunk;
+
+ Abi.abi_sp_reg = (Il.Hreg esp);
+ Abi.abi_fp_reg = (Il.Hreg ebp);
+ Abi.abi_dwarf_fp_reg = dwarf_ebp;
+ Abi.abi_tp_cell = task_ptr;
+ Abi.abi_frame_base_sz = frame_base_sz;
+ Abi.abi_frame_info_sz = frame_info_sz;
+ Abi.abi_implicit_args_sz = implicit_args_sz;
+ Abi.abi_spill_slot = spill_slot;
+ }
+
+
+(*
+ * NB: factor the instruction selector often. There's lots of
+ * semi-redundancy in the ISA.
+ *)
+
+
+let imm_is_signed_byte (n:int64) : bool =
+ (i64_le (-128L) n) && (i64_le n 127L)
+;;
+
+let imm_is_unsigned_byte (n:int64) : bool =
+ (i64_le (0L) n) && (i64_le n 255L)
+;;
+
+
+let rm_r (c:Il.cell) (r:int) : Asm.frag =
+ let reg_ebp = 6 in
+ let reg_esp = 7 in
+
+ (*
+ * We do a little contortion here to accommodate the special case of
+ * being asked to form esp-relative addresses; these require SIB
+ * bytes on x86. Of course!
+ *)
+ let sib_esp_base = Asm.BYTE 0x24 in
+ let seq1 rm modrm =
+ if rm = reg_esp
+ then Asm.SEQ [| modrm; sib_esp_base |]
+ else modrm
+ in
+ let seq2 rm modrm disp =
+ if rm = reg_esp
+ then Asm.SEQ [| modrm; sib_esp_base; disp |]
+ else Asm.SEQ [| modrm; disp |]
+ in
+
+ match c with
+ Il.Reg ((Il.Hreg rm), _) ->
+ Asm.BYTE (modrm_reg (reg rm) r)
+ | Il.Mem (a, _) ->
+ begin
+ match a with
+ Il.Abs disp ->
+ Asm.SEQ [| Asm.BYTE (modrm_deref_disp32 r);
+ Asm.WORD (TY_i32, disp) |]
+
+ | Il.RegIn ((Il.Hreg rm), None) when rm != reg_ebp ->
+ seq1 rm (Asm.BYTE (modrm_deref_reg (reg rm) r))
+
+ | Il.RegIn ((Il.Hreg rm), Some (Asm.IMM 0L))
+ when rm != reg_ebp ->
+ seq1 rm (Asm.BYTE (modrm_deref_reg (reg rm) r))
+
+ (* The next two are just to save the relaxation system some
+ * churn.
+ *)
+
+ | Il.RegIn ((Il.Hreg rm), Some (Asm.IMM n))
+ when imm_is_signed_byte n ->
+ seq2 rm
+ (Asm.BYTE (modrm_deref_reg_plus_disp8 (reg rm) r))
+ (Asm.WORD (TY_i8, Asm.IMM n))
+
+ | Il.RegIn ((Il.Hreg rm), Some (Asm.IMM n)) ->
+ seq2 rm
+ (Asm.BYTE (modrm_deref_reg_plus_disp32 (reg rm) r))
+ (Asm.WORD (TY_i32, Asm.IMM n))
+
+ | Il.RegIn ((Il.Hreg rm), Some disp) ->
+ Asm.new_relaxation
+ [|
+ seq2 rm
+ (Asm.BYTE (modrm_deref_reg_plus_disp32 (reg rm) r))
+ (Asm.WORD (TY_i32, disp));
+ seq2 rm
+ (Asm.BYTE (modrm_deref_reg_plus_disp8 (reg rm) r))
+ (Asm.WORD (TY_i8, disp))
+ |]
+ | _ -> raise Unrecognized
+ end
+ | _ -> raise Unrecognized
+;;
+
+
+let insn_rm_r (op:int) (c:Il.cell) (r:int) : Asm.frag =
+ Asm.SEQ [| Asm.BYTE op; rm_r c r |]
+;;
+
+
+let insn_rm_r_imm
+ (op:int)
+ (c:Il.cell)
+ (r:int)
+ (ty:ty_mach)
+ (i:Asm.expr64)
+ : Asm.frag =
+ Asm.SEQ [| Asm.BYTE op; rm_r c r; Asm.WORD (ty, i) |]
+;;
+
+let insn_rm_r_imm_s8_s32
+ (op8:int)
+ (op32:int)
+ (c:Il.cell)
+ (r:int)
+ (i:Asm.expr64)
+ : Asm.frag =
+ match i with
+ Asm.IMM n when imm_is_signed_byte n ->
+ insn_rm_r_imm op8 c r TY_i8 i
+ | _ ->
+ Asm.new_relaxation
+ [|
+ insn_rm_r_imm op32 c r TY_i32 i;
+ insn_rm_r_imm op8 c r TY_i8 i
+ |]
+;;
+
+let insn_rm_r_imm_u8_u32
+ (op8:int)
+ (op32:int)
+ (c:Il.cell)
+ (r:int)
+ (i:Asm.expr64)
+ : Asm.frag =
+ match i with
+ Asm.IMM n when imm_is_unsigned_byte n ->
+ insn_rm_r_imm op8 c r TY_u8 i
+ | _ ->
+ Asm.new_relaxation
+ [|
+ insn_rm_r_imm op32 c r TY_u32 i;
+ insn_rm_r_imm op8 c r TY_u8 i
+ |]
+;;
+
+
+let insn_pcrel_relax
+ (op8_frag:Asm.frag)
+ (op32_frag:Asm.frag)
+ (fix:fixup)
+ : Asm.frag =
+ let pcrel_mark_fixup = new_fixup "pcrel mark fixup" in
+ let def = Asm.DEF (pcrel_mark_fixup, Asm.MARK) in
+ let pcrel_expr = (Asm.SUB (Asm.M_POS fix,
+ Asm.M_POS pcrel_mark_fixup))
+ in
+ Asm.new_relaxation
+ [|
+ Asm.SEQ [| op32_frag; Asm.WORD (TY_i32, pcrel_expr); def |];
+ Asm.SEQ [| op8_frag; Asm.WORD (TY_i8, pcrel_expr); def |];
+ |]
+;;
+
+let insn_pcrel_simple (op32:int) (fix:fixup) : Asm.frag =
+ let pcrel_mark_fixup = new_fixup "pcrel mark fixup" in
+ let def = Asm.DEF (pcrel_mark_fixup, Asm.MARK) in
+ let pcrel_expr = (Asm.SUB (Asm.M_POS fix,
+ Asm.M_POS pcrel_mark_fixup))
+ in
+ Asm.SEQ [| Asm.BYTE op32; Asm.WORD (TY_i32, pcrel_expr); def |]
+;;
+
+let insn_pcrel (op8:int) (op32:int) (fix:fixup) : Asm.frag =
+ insn_pcrel_relax (Asm.BYTE op8) (Asm.BYTE op32) fix
+;;
+
+let insn_pcrel_prefix32
+ (op8:int)
+ (prefix32:int)
+ (op32:int)
+ (fix:fixup)
+ : Asm.frag =
+ insn_pcrel_relax (Asm.BYTE op8) (Asm.BYTES [| prefix32; op32 |]) fix
+;;
+
+(* FIXME: tighten imm-based dispatch by imm type. *)
+let cmp (a:Il.operand) (b:Il.operand) : Asm.frag =
+ match (a,b) with
+ (Il.Cell c, Il.Imm (i, TY_i8)) when is_rm8 c ->
+ insn_rm_r_imm 0x80 c slash7 TY_i8 i
+ | (Il.Cell c, Il.Imm (i, TY_u8)) when is_rm8 c ->
+ insn_rm_r_imm 0x80 c slash7 TY_u8 i
+ | (Il.Cell c, Il.Imm (i, _)) when is_rm32 c ->
+ (*
+ * NB: We can't switch on signed-ness here, as 'cmp' is
+ * defined to sign-extend its operand; i.e. we have to treat
+ * it as though you're emitting a signed byte (in the sense of
+ * immediate-size selection) even if the incoming value is
+ * unsigned.
+ *)
+ insn_rm_r_imm_s8_s32 0x83 0x81 c slash7 i
+ | (Il.Cell c, Il.Cell (Il.Reg (Il.Hreg r, _))) ->
+ insn_rm_r 0x39 c (reg r)
+ | (Il.Cell (Il.Reg (Il.Hreg r, _)), Il.Cell c) ->
+ insn_rm_r 0x3b c (reg r)
+ | _ -> raise Unrecognized
+;;
+
+let zero (dst:Il.cell) (count:Il.operand) : Asm.frag =
+ match (dst, count) with
+
+ ((Il.Mem (Il.RegIn ((Il.Hreg dst_ptr), None), _)),
+ Il.Cell (Il.Reg ((Il.Hreg count), _)))
+ when dst_ptr = edi && count = ecx ->
+ Asm.BYTES [|
+ 0xb0; 0x0; (* mov %eax, 0 : move a zero into al. *)
+ 0xf3; 0xaa; (* rep stos m8 : fill ecx bytes at [edi] with al *)
+ |]
+
+ | _ -> raise Unrecognized
+;;
+
+let mov (signed:bool) (dst:Il.cell) (src:Il.operand) : Asm.frag =
+ if is_ty8 (Il.cell_scalar_ty dst) || is_ty8 (Il.operand_scalar_ty src)
+ then
+ begin
+ (match dst with
+ Il.Reg (Il.Hreg r, _)
+ -> assert (is_ok_r8 r) | _ -> ());
+ (match src with
+ Il.Cell (Il.Reg (Il.Hreg r, _))
+ -> assert (is_ok_r8 r) | _ -> ());
+ end;
+
+ match (signed, dst, src) with
+
+ (* m8 <- r??, r8 or truncate(r32). *)
+ (_, _, Il.Cell (Il.Reg ((Il.Hreg r), _)))
+ when is_m8 dst ->
+ insn_rm_r 0x88 dst (reg r)
+
+ (* r8 <- r8: treat as r32 <- r32. *)
+ | (_, Il.Reg ((Il.Hreg r), _), Il.Cell src_cell)
+ when is_r8 dst && is_r8 src_cell ->
+ insn_rm_r 0x8b src_cell (reg r)
+
+ (* rm32 <- r32 *)
+ | (_, _, Il.Cell (Il.Reg ((Il.Hreg r), src_ty)))
+ when (is_r8 dst || is_rm32 dst) && is_ty32 src_ty ->
+ insn_rm_r 0x89 dst (reg r)
+
+ (* r32 <- rm32 *)
+ | (_, (Il.Reg ((Il.Hreg r), dst_ty)), Il.Cell src_cell)
+ when is_ty32 dst_ty && is_rm32 src_cell ->
+ insn_rm_r 0x8b src_cell (reg r)
+
+ (* MOVZX: r8/r32 <- zx(rm8) *)
+ | (false, Il.Reg ((Il.Hreg r, _)), Il.Cell src_cell)
+ when (is_r8 dst || is_r32 dst) && is_rm8 src_cell ->
+ Asm.SEQ [| Asm.BYTE 0x0f;
+ insn_rm_r 0xb6 src_cell (reg r) |]
+
+ (* MOVZX: m32 <- zx(r8) *)
+ | (false, _, (Il.Cell (Il.Reg ((Il.Hreg r), _) as src_cell)))
+ when (is_m32 dst) && is_r8 src_cell ->
+ (* Fake with 2 insns:
+ *
+ * movzx r32 <- r8; (in-place zero-extension)
+ * mov m32 <- r32; (NB: must happen in AL/CL/DL/BL)
+ *)
+ Asm.SEQ [| Asm.BYTE 0x0f;
+ insn_rm_r 0xb6 src_cell (reg r);
+ insn_rm_r 0x89 dst (reg r);
+ |]
+
+ (* MOVSX: r8/r32 <- sx(rm8) *)
+ | (true, Il.Reg ((Il.Hreg r), _), Il.Cell src_cell)
+ when (is_r8 dst || is_r32 dst) && is_rm8 src_cell ->
+ Asm.SEQ [| Asm.BYTE 0x0f;
+ insn_rm_r 0xbe src_cell (reg r) |]
+
+ (* MOVSX: m32 <- sx(r8) *)
+ | (true, _, (Il.Cell (Il.Reg ((Il.Hreg r), _) as src_cell)))
+ when (is_m32 dst) && is_r8 src_cell ->
+ (* Fake with 2 insns:
+ *
+ * movsx r32 <- r8; (in-place sign-extension)
+ * mov m32 <- r32; (NB: must happen in AL/CL/DL/BL)
+ *)
+ Asm.SEQ [| Asm.BYTE 0x0f;
+ insn_rm_r 0xbe src_cell (reg r);
+ insn_rm_r 0x89 dst (reg r);
+ |]
+
+ (* m8 <- imm8 (signed) *)
+ | (_, _, Il.Imm ((Asm.IMM n), _))
+ when is_m8 dst && imm_is_signed_byte n && signed ->
+ insn_rm_r_imm 0xc6 dst slash0 TY_i8 (Asm.IMM n)
+
+ (* m8 <- imm8 (unsigned) *)
+ | (_, _, Il.Imm ((Asm.IMM n), _))
+ when is_m8 dst && imm_is_unsigned_byte n && (not signed) ->
+ insn_rm_r_imm 0xc6 dst slash0 TY_u8 (Asm.IMM n)
+
+ (* rm32 <- imm32 *)
+ | (_, _, Il.Imm (i, _)) when is_rm32 dst || is_r8 dst ->
+ let t = if signed then TY_u32 else TY_i32 in
+ insn_rm_r_imm 0xc7 dst slash0 t i
+
+ | _ -> raise Unrecognized
+;;
+
+
+let lea (dst:Il.cell) (src:Il.operand) : Asm.frag =
+ match (dst, src) with
+ (Il.Reg ((Il.Hreg r), dst_ty),
+ Il.Cell (Il.Mem (mem, _)))
+ when is_ty32 dst_ty ->
+ insn_rm_r 0x8d (Il.Mem (mem, Il.OpaqueTy)) (reg r)
+
+ | (Il.Reg ((Il.Hreg r), dst_ty),
+ Il.ImmPtr (fix, _))
+ when is_ty32 dst_ty && r = eax ->
+ let anchor = new_fixup "anchor" in
+ let fix_off = Asm.SUB ((Asm.M_POS fix),
+ (Asm.M_POS anchor))
+ in
+ (* NB: These instructions must come as a
+ * cluster, w/o any separation.
+ *)
+ Asm.SEQ [|
+ insn_pcrel_simple 0xe8 get_next_pc_thunk_fixup;
+ Asm.DEF (anchor, insn_rm_r_imm 0x81 dst slash0 TY_i32 fix_off);
+ |]
+
+ | _ -> raise Unrecognized
+;;
+
+
+let select_insn_misc (q:Il.quad') : Asm.frag =
+
+ match q with
+ Il.Call c ->
+ begin
+ match c.Il.call_dst with
+ Il.Reg ((Il.Hreg dst), _) when dst = eax ->
+ begin
+ match c.Il.call_targ with
+
+ Il.CodePtr (Il.Cell c)
+ when Il.cell_referent_ty c
+ = Il.ScalarTy (Il.AddrTy Il.CodeTy) ->
+ insn_rm_r 0xff c slash2
+
+ | Il.CodePtr (Il.ImmPtr (f, Il.CodeTy)) ->
+ insn_pcrel_simple 0xe8 f
+
+ | _ -> raise Unrecognized
+ end
+ | _ -> raise Unrecognized
+ end
+
+ | Il.Push (Il.Cell (Il.Reg ((Il.Hreg r), t))) when is_ty32 t ->
+ Asm.BYTE (0x50 + (reg r))
+
+ | Il.Push (Il.Cell c) when is_rm32 c ->
+ insn_rm_r 0xff c slash6
+
+ | Il.Push (Il.Imm (Asm.IMM i, _)) when imm_is_unsigned_byte i ->
+ Asm.SEQ [| Asm.BYTE 0x6a; Asm.WORD (TY_u8, (Asm.IMM i)) |]
+
+ | Il.Push (Il.Imm (i, _)) ->
+ Asm.SEQ [| Asm.BYTE 0x68; Asm.WORD (TY_u32, i) |]
+
+ | Il.Pop (Il.Reg ((Il.Hreg r), t)) when is_ty32 t ->
+ Asm.BYTE (0x58 + (reg r))
+
+ | Il.Pop c when is_rm32 c ->
+ insn_rm_r 0x8f c slash0
+
+ | Il.Ret -> Asm.BYTE 0xc3
+
+ | Il.Jmp j ->
+ begin
+ match (j.Il.jmp_op, j.Il.jmp_targ) with
+
+ (Il.JMP, Il.CodePtr (Il.ImmPtr (f, Il.CodeTy))) ->
+ insn_pcrel 0xeb 0xe9 f
+
+ | (Il.JMP, Il.CodePtr (Il.Cell c))
+ when Il.cell_referent_ty c
+ = Il.ScalarTy (Il.AddrTy Il.CodeTy) ->
+ insn_rm_r 0xff c slash4
+
+ (* FIXME: refactor this to handle cell-based jumps
+ * if we ever need them. So far not. *)
+ | (_, Il.CodePtr (Il.ImmPtr (f, Il.CodeTy))) ->
+ let (op8, op32) =
+ match j.Il.jmp_op with
+ | Il.JC -> (0x72, 0x82)
+ | Il.JNC -> (0x73, 0x83)
+ | Il.JZ -> (0x74, 0x84)
+ | Il.JNZ -> (0x75, 0x85)
+ | Il.JO -> (0x70, 0x80)
+ | Il.JNO -> (0x71, 0x81)
+ | Il.JE -> (0x74, 0x84)
+ | Il.JNE -> (0x75, 0x85)
+
+ | Il.JL -> (0x7c, 0x8c)
+ | Il.JLE -> (0x7e, 0x8e)
+ | Il.JG -> (0x7f, 0x8f)
+ | Il.JGE -> (0x7d, 0x8d)
+
+ | Il.JB -> (0x72, 0x82)
+ | Il.JBE -> (0x76, 0x86)
+ | Il.JA -> (0x77, 0x87)
+ | Il.JAE -> (0x73, 0x83)
+ | _ -> raise Unrecognized
+ in
+ let prefix32 = 0x0f in
+ insn_pcrel_prefix32 op8 prefix32 op32 f
+
+ | _ -> raise Unrecognized
+ end
+
+ | Il.Dead -> Asm.MARK
+ | Il.Debug -> Asm.BYTES [| 0xcc |] (* int 3 *)
+ | Il.Regfence -> Asm.MARK
+ | Il.End -> Asm.BYTES [| 0x90 |]
+ | Il.Nop -> Asm.BYTES [| 0x90 |]
+ | _ -> raise Unrecognized
+;;
+
+
+type alu_binop_codes =
+ {
+ insn: string;
+ immslash: int; (* mod/rm "slash" code for imm-src variant *)
+ rm_dst_op8: int; (* opcode for 8-bit r/m dst variant *)
+ rm_dst_op32: int; (* opcode for 32-bit r/m dst variant *)
+ rm_src_op8: int; (* opcode for 8-bit r/m src variant *)
+ rm_src_op32: int; (* opcode for 32-bit r/m src variant *)
+ }
+;;
+
+let alu_binop
+ (dst:Il.cell) (src:Il.operand) (codes:alu_binop_codes)
+ : Asm.frag =
+ match (dst, src) with
+ (Il.Reg ((Il.Hreg r), dst_ty), Il.Cell c)
+ when (is_ty32 dst_ty && is_rm32 c) || (is_ty8 dst_ty && is_rm8 c)
+ -> insn_rm_r codes.rm_src_op32 c (reg r)
+
+ | (_, Il.Cell (Il.Reg ((Il.Hreg r), src_ty)))
+ when (is_rm32 dst && is_ty32 src_ty) || (is_rm8 dst && is_ty8 src_ty)
+ -> insn_rm_r codes.rm_dst_op32 dst (reg r)
+
+ | (_, Il.Imm (i, _)) when is_rm32 dst || is_rm8 dst
+ -> insn_rm_r_imm_s8_s32 0x83 0x81 dst codes.immslash i
+
+ | _ -> raise Unrecognized
+;;
+
+
+let mul_like (src:Il.operand) (signed:bool) (slash:int)
+ : Asm.frag =
+ match src with
+ Il.Cell src when is_rm32 src ->
+ insn_rm_r 0xf7 src slash
+
+ | Il.Cell src when is_rm8 src ->
+ insn_rm_r 0xf6 src slash
+
+ | Il.Imm (_, TY_u32)
+ | Il.Imm (_, TY_i32) ->
+ let tmp = Il.Reg ((Il.Hreg edx), Il.ValTy Il.Bits32) in
+ Asm.SEQ [| mov signed tmp src;
+ insn_rm_r 0xf7 tmp slash |]
+
+ | Il.Imm (_, TY_u8)
+ | Il.Imm (_, TY_i8) ->
+ let tmp = Il.Reg ((Il.Hreg edx), Il.ValTy Il.Bits8) in
+ Asm.SEQ [| mov signed tmp src;
+ insn_rm_r 0xf6 tmp slash |]
+
+ | _ -> raise Unrecognized
+;;
+
+
+let select_insn (q:Il.quad) : Asm.frag =
+ match q.Il.quad_body with
+ Il.Unary u ->
+ let unop s =
+ if u.Il.unary_src = Il.Cell u.Il.unary_dst
+ then insn_rm_r 0xf7 u.Il.unary_dst s
+ else raise Unrecognized
+ in
+ begin
+ match u.Il.unary_op with
+ Il.UMOV -> mov false u.Il.unary_dst u.Il.unary_src
+ | Il.IMOV -> mov true u.Il.unary_dst u.Il.unary_src
+ | Il.NEG -> unop slash3
+ | Il.NOT -> unop slash2
+ | Il.ZERO -> zero u.Il.unary_dst u.Il.unary_src
+ end
+
+ | Il.Lea le -> lea le.Il.lea_dst le.Il.lea_src
+
+ | Il.Cmp c -> cmp c.Il.cmp_lhs c.Il.cmp_rhs
+
+ | Il.Binary b ->
+ begin
+ if Il.Cell b.Il.binary_dst = b.Il.binary_lhs
+ then
+ let binop = alu_binop b.Il.binary_dst b.Il.binary_rhs in
+ let mulop = mul_like b.Il.binary_rhs in
+
+ let divop signed slash =
+ Asm.SEQ [|
+ (* xor edx edx, then mul_like. *)
+ insn_rm_r 0x33 (rc edx) (reg edx);
+ mul_like b.Il.binary_rhs signed slash
+ |]
+ in
+
+ let modop signed slash =
+ Asm.SEQ [|
+ (* divop, then mov remainder to eax instead. *)
+ divop signed slash;
+ mov false (rc eax) (ro edx)
+ |]
+ in
+
+ let shiftop slash =
+ let src = b.Il.binary_rhs in
+ let dst = b.Il.binary_dst in
+ let mask i = Asm.AND (i, Asm.IMM 0xffL) in
+ if is_rm8 dst
+ then
+ match src with
+ Il.Imm (i, _) ->
+ insn_rm_r_imm 0xC0 dst slash TY_u8 (mask i)
+ | Il.Cell (Il.Reg ((Il.Hreg r), _))
+ when r = ecx ->
+ Asm.SEQ [| Asm.BYTE 0xD2; rm_r dst slash |]
+ | _ -> raise Unrecognized
+ else
+ match src with
+ Il.Imm (i, _) ->
+ insn_rm_r_imm 0xC1 dst slash TY_u8 (mask i)
+ | Il.Cell (Il.Reg ((Il.Hreg r), _))
+ when r = ecx ->
+ Asm.SEQ [| Asm.BYTE 0xD3; rm_r dst slash |]
+ | _ -> raise Unrecognized
+ in
+
+ match (b.Il.binary_dst, b.Il.binary_op) with
+ (_, Il.ADD) -> binop { insn="ADD";
+ immslash=slash0;
+ rm_dst_op8=0x0;
+ rm_dst_op32=0x1;
+ rm_src_op8=0x2;
+ rm_src_op32=0x3; }
+ | (_, Il.SUB) -> binop { insn="SUB";
+ immslash=slash5;
+ rm_dst_op8=0x28;
+ rm_dst_op32=0x29;
+ rm_src_op8=0x2a;
+ rm_src_op32=0x2b; }
+ | (_, Il.AND) -> binop { insn="AND";
+ immslash=slash4;
+ rm_dst_op8=0x20;
+ rm_dst_op32=0x21;
+ rm_src_op8=0x22;
+ rm_src_op32=0x23; }
+ | (_, Il.OR) -> binop { insn="OR";
+ immslash=slash1;
+ rm_dst_op8=0x08;
+ rm_dst_op32=0x09;
+ rm_src_op8=0x0a;
+ rm_src_op32=0x0b; }
+ | (_, Il.XOR) -> binop { insn="XOR";
+ immslash=slash6;
+ rm_dst_op8=0x30;
+ rm_dst_op32=0x31;
+ rm_src_op8=0x32;
+ rm_src_op32=0x33; }
+
+ | (_, Il.LSL) -> shiftop slash4
+ | (_, Il.LSR) -> shiftop slash5
+ | (_, Il.ASR) -> shiftop slash7
+
+ | (Il.Reg (Il.Hreg r, t), Il.UMUL)
+ when (is_ty32 t || is_ty8 t) && r = eax ->
+ mulop false slash4
+
+ | (Il.Reg (Il.Hreg r, t), Il.IMUL)
+ when (is_ty32 t || is_ty8 t) && r = eax ->
+ mulop true slash5
+
+ | (Il.Reg (Il.Hreg r, t), Il.UDIV)
+ when (is_ty32 t || is_ty8 t) && r = eax ->
+ divop false slash6
+
+ | (Il.Reg (Il.Hreg r, t), Il.IDIV)
+ when (is_ty32 t || is_ty8 t) && r = eax ->
+ divop true slash7
+
+ | (Il.Reg (Il.Hreg r, t), Il.UMOD)
+ when (is_ty32 t || is_ty8 t) && r = eax ->
+ modop false slash6
+
+ | (Il.Reg (Il.Hreg r, t), Il.IMOD)
+ when (is_ty32 t || is_ty8 t) && r = eax ->
+ modop true slash7
+
+ | _ -> raise Unrecognized
+ else raise Unrecognized
+ end
+ | _ -> select_insn_misc q.Il.quad_body
+;;
+
+
+let new_emitter_without_vregs _ : Il.emitter =
+ Il.new_emitter
+ abi.Abi.abi_prealloc_quad
+ abi.Abi.abi_is_2addr_machine
+ false None
+;;
+
+let select_insns (sess:Session.sess) (q:Il.quads) : Asm.frag =
+ let scopes = Stack.create () in
+ let fixups = Stack.create () in
+ let pop_frags _ =
+ Asm.SEQ (Array.of_list
+ (List.rev
+ (!(Stack.pop scopes))))
+ in
+ ignore (Stack.push (ref []) scopes);
+ for i = 0 to (Array.length q) - 1 do
+ let append frag =
+ let frags = Stack.top scopes in
+ frags := frag :: (!frags)
+ in
+ begin
+ match q.(i).Il.quad_fixup with
+ None -> ()
+ | Some f -> append (Asm.DEF (f, Asm.MARK))
+ end;
+ begin
+ match q.(i).Il.quad_body with
+ Il.Enter f ->
+ Stack.push f fixups;
+ Stack.push (ref []) scopes;
+ | Il.Leave ->
+ append (Asm.DEF (Stack.pop fixups, pop_frags ()))
+ | _ ->
+ try
+ append (select_insn q.(i))
+ with
+ Unrecognized ->
+ Session.fail sess
+ "E:Assembly error: unrecognized quad: %s\n%!"
+ (Il.string_of_quad reg_str q.(i));
+ ()
+ end
+ done;
+ pop_frags()
+;;
+
+let frags_of_emitted_quads (sess:Session.sess) (e:Il.emitter) : Asm.frag =
+ let frag = select_insns sess e.Il.emit_quads in
+ if sess.Session.sess_failed
+ then raise Unrecognized
+ else frag
+;;
+
+
+(*
+ * 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/driver/lib.ml b/src/boot/driver/lib.ml
new file mode 100644
index 00000000..e0391c65
--- /dev/null
+++ b/src/boot/driver/lib.ml
@@ -0,0 +1,232 @@
+open Common;;
+
+let log (sess:Session.sess) =
+ Session.log "lib"
+ sess.Session.sess_log_lib
+ sess.Session.sess_log_out
+;;
+
+let iflog (sess:Session.sess) (thunk:(unit -> unit)) : unit =
+ if sess.Session.sess_log_lib
+ then thunk ()
+ else ()
+;;
+
+(* FIXME: move these to sess. *)
+let ar_cache = Hashtbl.create 0 ;;
+let sects_cache = Hashtbl.create 0;;
+let meta_cache = Hashtbl.create 0;;
+let die_cache = Hashtbl.create 0;;
+
+let get_ar
+ (sess:Session.sess)
+ (filename:filename)
+ : Asm.asm_reader option =
+ htab_search_or_add ar_cache filename
+ begin
+ fun _ ->
+ let sniff =
+ match sess.Session.sess_targ with
+ Win32_x86_pe -> Pe.sniff
+ | MacOS_x86_macho -> Macho.sniff
+ | Linux_x86_elf -> Elf.sniff
+ in
+ sniff sess filename
+ end
+;;
+
+
+let get_sects
+ (sess:Session.sess)
+ (filename:filename) :
+ (Asm.asm_reader * ((string,(int*int)) Hashtbl.t)) option =
+ htab_search_or_add sects_cache filename
+ begin
+ fun _ ->
+ match get_ar sess filename with
+ None -> None
+ | Some ar ->
+ let get_sections =
+ match sess.Session.sess_targ with
+ Win32_x86_pe -> Pe.get_sections
+ | MacOS_x86_macho -> Macho.get_sections
+ | Linux_x86_elf -> Elf.get_sections
+ in
+ Some (ar, (get_sections sess ar))
+ end
+;;
+
+let get_meta
+ (sess:Session.sess)
+ (filename:filename)
+ : Ast.meta option =
+ htab_search_or_add meta_cache filename
+ begin
+ fun _ ->
+ match get_sects sess filename with
+ None -> None
+ | Some (ar, sects) ->
+ match htab_search sects ".note.rust" with
+ Some (off, _) ->
+ ar.Asm.asm_seek off;
+ Some (Asm.read_rust_note ar)
+ | None -> None
+ end
+;;
+
+let get_dies_opt
+ (sess:Session.sess)
+ (filename:filename)
+ : (Dwarf.rooted_dies option) =
+ htab_search_or_add die_cache filename
+ begin
+ fun _ ->
+ match get_sects sess filename with
+ None -> None
+ | Some (ar, sects) ->
+ let debug_abbrev = Hashtbl.find sects ".debug_abbrev" in
+ let debug_info = Hashtbl.find sects ".debug_info" in
+ let abbrevs = Dwarf.read_abbrevs sess ar debug_abbrev in
+ let dies = Dwarf.read_dies sess ar debug_info abbrevs in
+ ar.Asm.asm_close ();
+ Hashtbl.remove ar_cache filename;
+ Some dies
+ end
+;;
+
+let get_dies
+ (sess:Session.sess)
+ (filename:filename)
+ : Dwarf.rooted_dies =
+ match get_dies_opt sess filename with
+ None ->
+ Printf.fprintf stderr "Error: bad crate file: %s\n%!" filename;
+ exit 1
+ | Some dies -> dies
+;;
+
+let get_file_mod
+ (sess:Session.sess)
+ (abi:Abi.abi)
+ (filename:filename)
+ (nref:node_id ref)
+ (oref:opaque_id ref)
+ : Ast.mod_items =
+ let dies = get_dies sess filename in
+ let items = Hashtbl.create 0 in
+ Dwarf.extract_mod_items nref oref abi items dies;
+ items
+;;
+
+let get_mod
+ (sess:Session.sess)
+ (abi:Abi.abi)
+ (meta:Ast.meta_pat)
+ (use_id:node_id)
+ (nref:node_id ref)
+ (oref:opaque_id ref)
+ : (filename * Ast.mod_items) =
+ let found = Queue.create () in
+ let suffix =
+ match sess.Session.sess_targ with
+ Win32_x86_pe -> ".dll"
+ | MacOS_x86_macho -> ".dylib"
+ | Linux_x86_elf -> ".so"
+ in
+ let rec meta_matches i f_meta =
+ if i >= (Array.length meta)
+ then true
+ else
+ match meta.(i) with
+ (* FIXME: bind the wildcards. *)
+ (_, None) -> meta_matches (i+1) f_meta
+ | (k, Some v) ->
+ match atab_search f_meta k with
+ None -> false
+ | Some v' ->
+ if v = v'
+ then meta_matches (i+1) f_meta
+ else false
+ in
+ let file_matches file =
+ log sess "searching for metadata in %s" file;
+ match get_meta sess file with
+ None -> false
+ | Some f_meta ->
+ log sess "matching metadata in %s" file;
+ meta_matches 0 f_meta
+ in
+ iflog sess
+ begin
+ fun _ ->
+ log sess "searching for library matching:";
+ Array.iter
+ begin
+ fun (k,vo) ->
+ match vo with
+ None -> ()
+ | Some v ->
+ log sess "%s = %S" k v
+ end
+ meta;
+ end;
+ Queue.iter
+ begin
+ fun dir ->
+ let dh = Unix.opendir dir in
+ let rec scan _ =
+ try
+ let file = Unix.readdir dh in
+ log sess "considering file %s" file;
+ if (Filename.check_suffix file suffix) &&
+ (file_matches file)
+ then
+ begin
+ iflog sess
+ begin
+ fun _ ->
+ log sess "matched against library %s" file;
+ match get_meta sess file with
+ None -> ()
+ | Some meta ->
+ Array.iter
+ (fun (k,v) -> log sess "%s = %S" k v)
+ meta;
+ end;
+ Queue.add file found;
+ end;
+ scan()
+ with
+ End_of_file -> ()
+ in
+ scan ()
+ end
+ sess.Session.sess_lib_dirs;
+ match Queue.length found with
+ 0 -> Common.err (Some use_id) "unsatisfied 'use' clause"
+ | 1 ->
+ let filename = Queue.pop found in
+ let items = get_file_mod sess abi filename nref oref in
+ (filename, items)
+ | _ -> Common.err (Some use_id) "multiple crates match 'use' clause"
+;;
+
+let infer_lib_name
+ (sess:Session.sess)
+ (ident:filename)
+ : filename =
+ match sess.Session.sess_targ with
+ Win32_x86_pe -> ident ^ ".dll"
+ | MacOS_x86_macho -> "lib" ^ ident ^ ".dylib"
+ | Linux_x86_elf -> "lib" ^ ident ^ ".so"
+;;
+
+
+(*
+ * 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/driver/llvm/glue.ml b/src/boot/driver/llvm/glue.ml
new file mode 100644
index 00000000..ef5c1c86
--- /dev/null
+++ b/src/boot/driver/llvm/glue.ml
@@ -0,0 +1,37 @@
+(*
+ * Glue for the LLVM backend.
+ *)
+
+let alt_argspecs sess = [
+ ("-llvm", Arg.Unit (fun _ -> sess.Session.sess_alt_backend <- true),
+ "emit LLVM bitcode")
+];;
+
+let alt_pipeline sess sem_cx crate =
+ let process processor =
+ processor sem_cx crate;
+ if sess.Session.sess_failed then exit 1 else ()
+ in
+ Array.iter process
+ [|
+ Resolve.process_crate;
+ Type.process_crate;
+ Effect.process_crate;
+ Typestate.process_crate;
+ Loop.process_crate;
+ Alias.process_crate;
+ Dead.process_crate;
+ Layout.process_crate
+ |];
+ Llemit.trans_and_process_crate sess sem_cx crate
+;;
+
+(*
+ * 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/driver/main.ml b/src/boot/driver/main.ml
new file mode 100644
index 00000000..c5199a82
--- /dev/null
+++ b/src/boot/driver/main.ml
@@ -0,0 +1,421 @@
+
+open Common;;
+
+let _ =
+ Gc.set { (Gc.get()) with
+ Gc.space_overhead = 400; }
+;;
+
+let (targ:Common.target) =
+ match Sys.os_type with
+ "Unix" ->
+ (* FIXME: this is an absurd heuristic. *)
+ if Sys.file_exists "/System/Library"
+ then MacOS_x86_macho
+ else Linux_x86_elf
+ | "Win32" -> Win32_x86_pe
+ | "Cygwin" -> Win32_x86_pe
+ | _ -> Linux_x86_elf
+;;
+
+let (abi:Abi.abi) = X86.abi;;
+
+let (sess:Session.sess) =
+ {
+ Session.sess_in = None;
+ Session.sess_out = None;
+ Session.sess_library_mode = false;
+ Session.sess_alt_backend = false;
+ (* FIXME: need something fancier here for unix sub-flavours. *)
+ Session.sess_targ = targ;
+ Session.sess_log_lex = false;
+ Session.sess_log_parse = false;
+ Session.sess_log_ast = false;
+ Session.sess_log_resolve = false;
+ Session.sess_log_type = false;
+ Session.sess_log_effect = false;
+ Session.sess_log_typestate = false;
+ Session.sess_log_loop = false;
+ Session.sess_log_alias = false;
+ Session.sess_log_dead = false;
+ Session.sess_log_layout = false;
+ Session.sess_log_itype = false;
+ Session.sess_log_trans = false;
+ Session.sess_log_dwarf = false;
+ Session.sess_log_ra = false;
+ Session.sess_log_insn = false;
+ Session.sess_log_asm = false;
+ Session.sess_log_obj = false;
+ Session.sess_log_lib = false;
+ Session.sess_log_out = stdout;
+ Session.sess_trace_block = false;
+ Session.sess_trace_drop = false;
+ Session.sess_trace_tag = false;
+ Session.sess_trace_gc = false;
+ Session.sess_failed = false;
+ Session.sess_spans = Hashtbl.create 0;
+ Session.sess_report_timing = false;
+ Session.sess_report_gc = false;
+ Session.sess_report_deps = false;
+ Session.sess_timings = Hashtbl.create 0;
+ Session.sess_lib_dirs = Queue.create ();
+ }
+;;
+
+let default_output_filename (sess:Session.sess) : filename option =
+ match sess.Session.sess_in with
+ None -> None
+ | Some fname ->
+ let base = Filename.chop_extension (Filename.basename fname) in
+ let out =
+ if sess.Session.sess_library_mode
+ then
+ Lib.infer_lib_name sess base
+ else
+ base ^ (match sess.Session.sess_targ with
+ Linux_x86_elf -> ""
+ | MacOS_x86_macho -> ""
+ | Win32_x86_pe -> ".exe")
+ in
+ Some out
+;;
+
+let set_default_output_filename (sess:Session.sess) : unit =
+ match sess.Session.sess_out with
+ None -> (sess.Session.sess_out <- default_output_filename sess)
+ | _ -> ()
+;;
+
+
+let dump_sig (filename:filename) : unit =
+ let items =
+ Lib.get_file_mod sess abi filename (ref (Node 0)) (ref (Opaque 0)) in
+ Printf.fprintf stdout "%s\n" (Ast.fmt_to_str Ast.fmt_mod_items items);
+ exit 0
+;;
+
+let dump_meta (filename:filename) : unit =
+ begin
+ match Lib.get_meta sess filename with
+ None -> Printf.fprintf stderr "Error: bad crate file: %s\n" filename
+ | Some meta ->
+ Array.iter
+ begin
+ fun (k,v) ->
+ Printf.fprintf stdout "%s = %S\n" k v;
+ end
+ meta
+ end;
+ exit 0
+;;
+
+let flag f opt desc =
+ (opt, Arg.Unit f, desc)
+;;
+
+let argspecs =
+ [
+ ("-t", Arg.Symbol (["linux-x86-elf"; "win32-x86-pe"; "macos-x86-macho"],
+ fun s -> (sess.Session.sess_targ <-
+ (match s with
+ "win32-x86-pe" -> Win32_x86_pe
+ | "macos-x86-macho" -> MacOS_x86_macho
+ | _ -> Linux_x86_elf))),
+ (" target (default: " ^ (match sess.Session.sess_targ with
+ Win32_x86_pe -> "win32-x86-pe"
+ | Linux_x86_elf -> "linux-x86-elf"
+ | MacOS_x86_macho -> "macos-x86-macho"
+ ) ^ ")"));
+ ("-o", Arg.String (fun s -> sess.Session.sess_out <- Some s),
+ "file to output (default: "
+ ^ (Session.filename_of sess.Session.sess_out) ^ ")");
+ ("-shared", Arg.Unit (fun _ -> sess.Session.sess_library_mode <- true),
+ "compile a shared-library crate");
+ ("-L", Arg.String (fun s -> Queue.add s sess.Session.sess_lib_dirs),
+ "dir to add to library path");
+ ("-litype", Arg.Unit (fun _ -> sess.Session.sess_log_itype <- true;
+ Il.log_iltypes := true), "log IL types");
+ (flag (fun _ -> sess.Session.sess_log_lex <- true)
+ "-llex" "log lexing");
+ (flag (fun _ -> sess.Session.sess_log_parse <- true)
+ "-lparse" "log parsing");
+ (flag (fun _ -> sess.Session.sess_log_ast <- true)
+ "-last" "log AST");
+ (flag (fun _ -> sess.Session.sess_log_resolve <- true)
+ "-lresolve" "log resolution");
+ (flag (fun _ -> sess.Session.sess_log_type <- true)
+ "-ltype" "log type checking");
+ (flag (fun _ -> sess.Session.sess_log_effect <- true)
+ "-leffect" "log effect checking");
+ (flag (fun _ -> sess.Session.sess_log_typestate <- true)
+ "-ltypestate" "log typestate pass");
+ (flag (fun _ -> sess.Session.sess_log_loop <- true)
+ "-lloop" "log loop analysis");
+ (flag (fun _ -> sess.Session.sess_log_alias <- true)
+ "-lalias" "log alias analysis");
+ (flag (fun _ -> sess.Session.sess_log_dead <- true)
+ "-ldead" "log dead analysis");
+ (flag (fun _ -> sess.Session.sess_log_layout <- true)
+ "-llayout" "log frame layout");
+ (flag (fun _ -> sess.Session.sess_log_trans <- true)
+ "-ltrans" "log IR translation");
+ (flag (fun _ -> sess.Session.sess_log_dwarf <- true)
+ "-ldwarf" "log DWARF generation");
+ (flag (fun _ -> sess.Session.sess_log_ra <- true)
+ "-lra" "log register allocation");
+ (flag (fun _ -> sess.Session.sess_log_insn <- true)
+ "-linsn" "log instruction selection");
+ (flag (fun _ -> sess.Session.sess_log_asm <- true)
+ "-lasm" "log assembly");
+ (flag (fun _ -> sess.Session.sess_log_obj <- true)
+ "-lobj" "log object-file generation");
+ (flag (fun _ -> sess.Session.sess_log_lib <- true)
+ "-llib" "log library search");
+
+ (flag (fun _ -> sess.Session.sess_trace_block <- true)
+ "-tblock" "emit block-boundary tracing code");
+ (flag (fun _ -> sess.Session.sess_trace_drop <- true)
+ "-tdrop" "emit slot-drop tracing code");
+ (flag (fun _ -> sess.Session.sess_trace_tag <- true)
+ "-ttag" "emit tag-construction tracing code");
+ (flag (fun _ -> sess.Session.sess_trace_gc <- true)
+ "-tgc" "emit GC tracing code");
+
+ ("-tall", Arg.Unit (fun _ ->
+ sess.Session.sess_trace_block <- true;
+ sess.Session.sess_trace_drop <- true;
+ sess.Session.sess_trace_tag <- true ),
+ "emit all tracing code");
+
+ (flag (fun _ -> sess.Session.sess_report_timing <- true)
+ "-rtime" "report timing of compiler phases");
+ (flag (fun _ -> sess.Session.sess_report_gc <- true)
+ "-rgc" "report gc behavior of compiler");
+ ("-rsig", Arg.String dump_sig,
+ "report type-signature from DWARF info in compiled file, then exit");
+ ("-rmeta", Arg.String dump_meta,
+ "report metadata from DWARF info in compiled file, then exit");
+ ("-rdeps", Arg.Unit (fun _ -> sess.Session.sess_report_deps <- true),
+ "report dependencies of input, then exit");
+ ] @ (Glue.alt_argspecs sess)
+;;
+
+let exit_if_failed _ =
+ if sess.Session.sess_failed
+ then exit 1
+ else ()
+;;
+
+Arg.parse
+ argspecs
+ (fun arg -> sess.Session.sess_in <- (Some arg))
+ ("usage: " ^ Sys.argv.(0) ^ " [options] (CRATE_FILE.rc|SOURCE_FILE.rs)\n")
+;;
+
+let _ = set_default_output_filename sess
+;;
+
+let _ =
+ if sess.Session.sess_out = None
+ then (Printf.fprintf stderr "Error: no output file specified\n"; exit 1)
+ else ()
+;;
+
+let _ =
+ if sess.Session.sess_in = None
+ then (Printf.fprintf stderr "Error: empty input filename\n"; exit 1)
+ else ()
+;;
+
+
+let (crate:Ast.crate) =
+ Session.time_inner "parse" sess
+ begin
+ fun _ ->
+ let infile = Session.filename_of sess.Session.sess_in in
+ let crate =
+ if Filename.check_suffix infile ".rc"
+ then
+ Cexp.parse_crate_file sess
+ (Lib.get_mod sess abi)
+ (Lib.infer_lib_name sess)
+ else
+ if Filename.check_suffix infile ".rs"
+ then
+ Cexp.parse_src_file sess
+ (Lib.get_mod sess abi)
+ (Lib.infer_lib_name sess)
+ else
+ begin
+ Printf.fprintf stderr
+ "Error: unrecognized input file type: %s\n"
+ infile;
+ exit 1
+ end
+ in
+ if sess.Session.sess_report_deps
+ then
+ let outfile = (Session.filename_of sess.Session.sess_out) in
+ let depfile =
+ match sess.Session.sess_targ with
+ Linux_x86_elf
+ | MacOS_x86_macho -> outfile ^ ".d"
+ | Win32_x86_pe -> (Filename.chop_extension outfile) ^ ".d"
+ in
+ begin
+ Array.iter
+ begin
+ fun out ->
+ Printf.fprintf stdout "%s: \\\n" out;
+ Hashtbl.iter
+ (fun _ file ->
+ Printf.fprintf stdout " %s \\\n" file)
+ crate.node.Ast.crate_files;
+ Printf.fprintf stdout "\n"
+ end
+ [| outfile; depfile|];
+ exit 0
+ end
+ else
+ crate
+ end
+;;
+
+exit_if_failed ()
+;;
+
+if sess.Session.sess_log_ast
+then
+ begin
+ Printf.fprintf stdout "Post-parse AST:\n";
+ Format.set_margin 80;
+ Printf.fprintf stdout "%s\n" (Ast.fmt_to_str Ast.fmt_crate crate)
+ end
+
+let list_to_seq ls = Asm.SEQ (Array.of_list ls);;
+let select_insns (quads:Il.quads) : Asm.frag =
+ Session.time_inner "insn" sess
+ (fun _ -> X86.select_insns sess quads)
+;;
+
+
+(* Semantic passes. *)
+let sem_cx = Semant.new_ctxt sess abi crate.node
+;;
+
+
+let main_pipeline _ =
+ let _ =
+ Array.iter
+ (fun proc ->
+ proc sem_cx crate;
+ exit_if_failed ())
+ [| Resolve.process_crate;
+ Type.process_crate;
+ Effect.process_crate;
+ Typestate.process_crate;
+ Loop.process_crate;
+ Alias.process_crate;
+ Dead.process_crate;
+ Layout.process_crate;
+ Trans.process_crate |]
+ in
+
+ (* Tying up various knots, allocating registers and selecting
+ * instructions.
+ *)
+ let process_code _ (code:Semant.code) : Asm.frag =
+ let frag =
+ match code.Semant.code_vregs_and_spill with
+ None -> select_insns code.Semant.code_quads
+ | Some (n_vregs, spill_fix) ->
+ let (quads', n_spills) =
+ (Session.time_inner "RA" sess
+ (fun _ ->
+ Ra.reg_alloc sess
+ code.Semant.code_quads
+ n_vregs abi))
+ in
+ let insns = select_insns quads' in
+ begin
+ spill_fix.fixup_mem_sz <-
+ Some (Int64.mul
+ (Int64.of_int n_spills)
+ abi.Abi.abi_word_sz);
+ insns
+ end
+ in
+ Asm.ALIGN_FILE (Abi.general_code_alignment,
+ Asm.DEF (code.Semant.code_fixup, frag))
+ in
+
+ let (file_frags:Asm.frag) =
+ let process_file file_id frag_code =
+ let file_fix = Hashtbl.find sem_cx.Semant.ctxt_file_fixups file_id in
+ Asm.DEF (file_fix,
+ list_to_seq (reduce_hash_to_list process_code frag_code))
+ in
+ list_to_seq (reduce_hash_to_list
+ process_file sem_cx.Semant.ctxt_file_code)
+ in
+
+ exit_if_failed ();
+ let (glue_frags:Asm.frag) =
+ list_to_seq (reduce_hash_to_list
+ process_code sem_cx.Semant.ctxt_glue_code)
+ in
+
+ exit_if_failed ();
+ let code = Asm.SEQ [| file_frags; glue_frags |] in
+ let data = list_to_seq (reduce_hash_to_list
+ (fun _ (_, i) -> i) sem_cx.Semant.ctxt_data)
+ in
+ (* Emitting Dwarf and PE/ELF/Macho. *)
+ let (dwarf:Dwarf.debug_records) =
+ Session.time_inner "dwarf" sess
+ (fun _ -> Dwarf.process_crate sem_cx crate)
+ in
+
+ exit_if_failed ();
+ let emitter =
+ match sess.Session.sess_targ with
+ Win32_x86_pe -> Pe.emit_file
+ | MacOS_x86_macho -> Macho.emit_file
+ | Linux_x86_elf -> Elf.emit_file
+ in
+ Session.time_inner "emit" sess
+ (fun _ -> emitter sess crate code data sem_cx dwarf);
+ exit_if_failed ()
+;;
+
+if sess.Session.sess_alt_backend
+then Glue.alt_pipeline sess sem_cx crate
+else main_pipeline ()
+;;
+
+if sess.Session.sess_report_timing
+then
+ begin
+ Printf.fprintf stdout "timing:\n\n";
+ Array.iter
+ begin
+ fun name ->
+ Printf.fprintf stdout "%20s: %f\n" name
+ (Hashtbl.find sess.Session.sess_timings name)
+ end
+ (sorted_htab_keys sess.Session.sess_timings)
+ end;
+;;
+
+if sess.Session.sess_report_gc
+then Gc.print_stat stdout;;
+
+
+(*
+ * 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/driver/session.ml b/src/boot/driver/session.ml
new file mode 100644
index 00000000..80253f44
--- /dev/null
+++ b/src/boot/driver/session.ml
@@ -0,0 +1,111 @@
+(*
+ * This module goes near the bottom of the dependency DAG, and holds option,
+ * and global-state machinery for a single run of the compiler.
+ *)
+
+open Common;;
+
+type sess =
+{
+ mutable sess_in: filename option;
+ mutable sess_out: filename option;
+ mutable sess_library_mode: bool;
+ mutable sess_alt_backend: bool;
+ mutable sess_targ: target;
+ mutable sess_log_lex: bool;
+ mutable sess_log_parse: bool;
+ mutable sess_log_ast: bool;
+ mutable sess_log_resolve: bool;
+ mutable sess_log_type: bool;
+ mutable sess_log_effect: bool;
+ mutable sess_log_typestate: bool;
+ mutable sess_log_dead: bool;
+ mutable sess_log_loop: bool;
+ mutable sess_log_alias: bool;
+ mutable sess_log_layout: bool;
+ mutable sess_log_trans: bool;
+ mutable sess_log_itype: bool;
+ mutable sess_log_dwarf: bool;
+ mutable sess_log_ra: bool;
+ mutable sess_log_insn: bool;
+ mutable sess_log_asm: bool;
+ mutable sess_log_obj: bool;
+ mutable sess_log_lib: bool;
+ mutable sess_log_out: out_channel;
+ mutable sess_trace_block: bool;
+ mutable sess_trace_drop: bool;
+ mutable sess_trace_tag: bool;
+ mutable sess_trace_gc: bool;
+ mutable sess_failed: bool;
+ mutable sess_report_timing: bool;
+ mutable sess_report_gc: bool;
+ mutable sess_report_deps: bool;
+ sess_timings: (string, float) Hashtbl.t;
+ sess_spans: (node_id,span) Hashtbl.t;
+ sess_lib_dirs: filename Queue.t;
+}
+;;
+
+let add_time sess name amt =
+ let existing =
+ if Hashtbl.mem sess.sess_timings name
+ then Hashtbl.find sess.sess_timings name
+ else 0.0
+ in
+ (Hashtbl.replace sess.sess_timings name (existing +. amt))
+;;
+
+let time_inner name sess thunk =
+ let t0 = Unix.gettimeofday() in
+ let x = thunk() in
+ let t1 = Unix.gettimeofday() in
+ add_time sess name (t1 -. t0);
+ x
+;;
+
+let get_span sess id =
+ if Hashtbl.mem sess.sess_spans id
+ then (Some (Hashtbl.find sess.sess_spans id))
+ else None
+;;
+
+let log name flag chan =
+ let k1 s =
+ Printf.fprintf chan "%s: %s\n%!" name s
+ in
+ let k2 _ = () in
+ Printf.ksprintf (if flag then k1 else k2)
+;;
+
+let fail sess =
+ sess.sess_failed <- true;
+ Printf.fprintf sess.sess_log_out
+;;
+
+
+let string_of_pos (p:pos) =
+ let (filename, line, col) = p in
+ Printf.sprintf "%s:%d:%d" filename line col
+;;
+
+
+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
+;;
+
+let filename_of (fo:filename option) : filename =
+ match fo with
+ None -> "<none>"
+ | Some f -> f
+;;
+
+(*
+ * 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/driver/x86/glue.ml b/src/boot/driver/x86/glue.ml
new file mode 100644
index 00000000..4fc74480
--- /dev/null
+++ b/src/boot/driver/x86/glue.ml
@@ -0,0 +1,16 @@
+(*
+ * Glue, or lack thereof, for the standard x86 backend.
+ *)
+
+let alt_argspecs _ = [];;
+let alt_pipeline _ _ _ = ();;
+
+(*
+ * 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/ast.ml b/src/boot/fe/ast.ml
new file mode 100644
index 00000000..bf7a11ff
--- /dev/null
+++ b/src/boot/fe/ast.ml
@@ -0,0 +1,1360 @@
+(*
+ * There are two kinds of rust files:
+ *
+ * .rc files, containing crates.
+ * .rs files, containing source.
+ *
+ *)
+
+open Common;;
+
+(*
+ * Slot names are given by a dot-separated path within the current
+ * module namespace.
+ *)
+
+type ident = string
+;;
+
+type slot_key =
+ KEY_ident of ident
+ | KEY_temp of temp_id
+;;
+
+(* "names" are statically computable references to particular items;
+ they never involve dynamic indexing (nor even static tuple-indexing;
+ you could add it but there are few contexts that need names that would
+ benefit from it).
+
+ Each component of a name may also be type-parametric; you must
+ supply type parameters to reference through a type-parametric name
+ component. So for example if foo is parametric in 2 types, you can
+ write foo[int,int].bar but not foo.bar.
+ *)
+
+type effect =
+ PURE
+ | IO
+ | STATE
+ | UNSAFE
+;;
+
+type name_base =
+ BASE_ident of ident
+ | BASE_temp of temp_id
+ | BASE_app of (ident * (ty array))
+
+and name_component =
+ COMP_ident of ident
+ | COMP_app of (ident * (ty array))
+ | COMP_idx of int
+
+and name =
+ NAME_base of name_base
+ | NAME_ext of (name * name_component)
+
+(*
+ * Type expressions are transparent to type names, their equality is
+ * structural. (after normalization)
+ *)
+and ty =
+
+ TY_any
+ | TY_nil
+ | TY_bool
+ | TY_mach of ty_mach
+ | TY_int
+ | TY_uint
+ | TY_char
+ | TY_str
+
+ | TY_tup of ty_tup
+ | TY_vec of slot
+ | TY_rec of ty_rec
+
+ (*
+ * Note that ty_idx is only valid inside a slot of a ty_iso group, not
+ * in a general type term.
+ *)
+ | TY_tag of ty_tag
+ | TY_iso of ty_iso
+ | TY_idx of int
+
+ | TY_fn of ty_fn
+ | TY_chan of ty
+ | TY_port of ty
+
+ | TY_obj of ty_obj
+ | TY_task
+
+ | TY_native of opaque_id
+ | TY_param of (ty_param_idx * effect)
+ | TY_named of name
+ | TY_type
+
+ | TY_constrained of (ty * constrs)
+
+and mode =
+ MODE_exterior
+ | MODE_interior
+ | MODE_alias
+
+and slot = { slot_mode: mode;
+ slot_mutable: bool;
+ slot_ty: ty option; }
+
+and ty_tup = slot array
+
+(* In closed type terms a constraint may refer to components of the term by
+ * anchoring off the "formal symbol" '*', which represents "the term this
+ * constraint is attached to".
+ *
+ *
+ * For example, if I have a tuple type tup(int,int), I may wish to enforce the
+ * lt predicate on it; I can write this as a constrained type term like:
+ *
+ * tup(int,int) : lt( *._0, *._1 )
+ *
+ * In fact all tuple types are converted to this form for purpose of
+ * type-compatibility testing; the argument tuple in a function
+ *
+ * fn (int x, int y) : lt(x, y) -> int
+ *
+ * desugars to
+ *
+ * fn (tup(int, int) : lt( *._1, *._2 )) -> int
+ *
+ *)
+
+and carg_base =
+ BASE_formal
+ | BASE_named of name_base
+
+and carg_path =
+ CARG_base of carg_base
+ | CARG_ext of (carg_path * name_component)
+
+and carg =
+ CARG_path of carg_path
+ | CARG_lit of lit
+
+and constr =
+ {
+ constr_name: name;
+ constr_args: carg array;
+ }
+
+and constrs = constr array
+
+and ty_rec = (ident * slot) array
+
+(* ty_tag is a sum type.
+ *
+ * a tag type expression either normalizes to a TY_tag or a TY_iso,
+ * which (like in ocaml) is an indexed projection from an iso-recursive
+ * group of TY_tags.
+ *)
+
+and ty_tag = (name, ty_tup) Hashtbl.t
+
+and ty_iso =
+ {
+ iso_index: int;
+ iso_group: ty_tag array
+ }
+
+and ty_sig =
+ {
+ sig_input_slots: slot array;
+ sig_input_constrs: constrs;
+ sig_output_slot: slot;
+ }
+
+and ty_fn_aux =
+ {
+ fn_is_iter: bool;
+ fn_effect: effect;
+ }
+
+and ty_fn = (ty_sig * ty_fn_aux)
+
+and ty_obj_header = (slot array * constrs)
+
+and ty_obj = (effect * ((ident,ty_fn) Hashtbl.t))
+
+and check_calls = (lval * (atom array)) array
+
+and rec_input = (ident * mode * bool * atom)
+
+and tup_input = (mode * bool * atom)
+
+and stmt' =
+
+ (* lval-assigning stmts. *)
+ STMT_spawn of (lval * domain * lval * (atom array))
+ | STMT_init_rec of (lval * (rec_input array) * lval option)
+ | STMT_init_tup of (lval * (tup_input array))
+ | STMT_init_vec of (lval * slot * (atom array))
+ | STMT_init_str of (lval * string)
+ | STMT_init_port of lval
+ | STMT_init_chan of (lval * (lval option))
+ | STMT_copy of (lval * expr)
+ | STMT_copy_binop of (lval * binop * atom)
+ | STMT_call of (lval * lval * (atom array))
+ | STMT_bind of (lval * lval * ((atom option) array))
+ | STMT_recv of (lval * lval)
+ | STMT_slice of (lval * lval * slice)
+
+ (* control-flow stmts. *)
+ | STMT_while of stmt_while
+ | STMT_do_while of stmt_while
+ | STMT_for of stmt_for
+ | STMT_for_each of stmt_for_each
+ | STMT_if of stmt_if
+ | STMT_put of (atom option)
+ | STMT_put_each of (lval * (atom array))
+ | STMT_ret of (atom option)
+ | STMT_be of (lval * (atom array))
+ | STMT_alt_tag of stmt_alt_tag
+ | STMT_alt_type of stmt_alt_type
+ | STMT_alt_port of stmt_alt_port
+
+ (* structural and misc stmts. *)
+ | STMT_fail
+ | STMT_yield
+ | STMT_join of lval
+ | STMT_send of (lval * lval)
+ | STMT_log of atom
+ | STMT_note of atom
+ | STMT_prove of (constrs)
+ | STMT_check of (constrs * check_calls)
+ | STMT_check_expr of expr
+ | STMT_check_if of (constrs * check_calls * block)
+ | STMT_block of block
+ | STMT_decl of stmt_decl
+
+and stmt = stmt' identified
+
+and stmt_alt_tag =
+ {
+ alt_tag_lval: lval;
+ alt_tag_arms: arm array;
+ }
+
+and stmt_alt_type =
+ {
+ alt_type_lval: lval;
+ alt_type_arms: (ident * slot * stmt) array;
+ alt_type_else: stmt option;
+ }
+
+and block' = stmt array
+and block = block' identified
+
+and stmt_decl =
+ DECL_mod_item of (ident * mod_item)
+ | DECL_slot of (slot_key * (slot identified))
+
+and stmt_alt_port =
+ {
+ (* else lval is a timeout value. *)
+ alt_port_arms: (lval * lval) array;
+ alt_port_else: (lval * stmt) option;
+ }
+
+and stmt_while =
+ {
+ while_lval: ((stmt array) * expr);
+ while_body: block;
+ }
+
+and stmt_for_each =
+ {
+ for_each_slot: (slot identified * ident);
+ for_each_call: (lval * atom array);
+ for_each_head: block;
+ for_each_body: block;
+ }
+
+and stmt_for =
+ {
+ for_slot: (slot identified * ident);
+ for_seq: ((stmt array) * lval);
+ for_body: block;
+ }
+
+and stmt_if =
+ {
+ if_test: expr;
+ if_then: block;
+ if_else: block option;
+ }
+
+and slice =
+ { slice_start: atom option;
+ slice_len: atom option; }
+
+and domain =
+ DOMAIN_local
+ | DOMAIN_thread
+
+and pat =
+ PAT_lit of lit
+ | PAT_tag of ident * (pat array)
+ | PAT_slot of ((slot identified) * ident)
+ | PAT_wild
+
+and arm' = pat * block
+and arm = arm' identified
+
+and atom =
+ ATOM_literal of (lit identified)
+ | ATOM_lval of lval
+
+and expr =
+ EXPR_binary of (binop * atom * atom)
+ | EXPR_unary of (unop * atom)
+ | EXPR_atom of atom
+
+and lit =
+ | LIT_nil
+ | LIT_bool of bool
+ | LIT_mach of (ty_mach * int64 * string)
+ | LIT_int of (int64 * string)
+ | LIT_uint of (int64 * string)
+ | LIT_char of int
+
+
+and lval_component =
+ COMP_named of name_component
+ | COMP_atom of atom
+
+
+and lval =
+ LVAL_base of name_base identified
+ | LVAL_ext of (lval * lval_component)
+
+and binop =
+ BINOP_or
+ | BINOP_and
+ | BINOP_xor
+
+ | BINOP_eq
+ | BINOP_ne
+
+ | BINOP_lt
+ | BINOP_le
+ | BINOP_ge
+ | BINOP_gt
+
+ | BINOP_lsl
+ | BINOP_lsr
+ | BINOP_asr
+
+ | BINOP_add
+ | BINOP_sub
+ | BINOP_mul
+ | BINOP_div
+ | BINOP_mod
+ | BINOP_send
+
+and unop =
+ UNOP_not
+ | UNOP_bitnot
+ | UNOP_neg
+ | UNOP_cast of ty identified
+
+
+and header_slots = ((slot identified) * ident) array
+
+and header_tup = (slot identified) array
+
+and fn =
+ {
+ fn_input_slots: header_slots;
+ fn_input_constrs: constrs;
+ fn_output_slot: slot identified;
+ fn_aux: ty_fn_aux;
+ fn_body: block;
+ }
+
+and obj =
+ {
+ obj_state: header_slots;
+ obj_effect: effect;
+ obj_constrs: constrs;
+ obj_fns: (ident,fn identified) Hashtbl.t;
+ obj_drop: block option;
+ }
+
+(*
+ * An 'a decl is a sort-of-thing that represents a parametric (generative)
+ * declaration. Every reference to one of these involves applying 0 or more
+ * type arguments, as part of *name resolution*.
+ *
+ * Slots are *not* parametric declarations. A slot has a specific type
+ * even if it's a type that's bound by a quantifier in its environment.
+ *)
+
+and ty_param = ident * (ty_param_idx * effect)
+
+and mod_item' =
+ MOD_ITEM_type of ty
+ | MOD_ITEM_tag of (header_tup * ty_tag * node_id)
+ | MOD_ITEM_mod of (mod_view * mod_items)
+ | MOD_ITEM_fn of fn
+ | MOD_ITEM_obj of obj
+
+and mod_item_decl =
+ {
+ decl_params: (ty_param identified) array;
+ decl_item: mod_item';
+ }
+
+and mod_item = mod_item_decl identified
+and mod_items = (ident, mod_item) Hashtbl.t
+
+and export =
+ EXPORT_all_decls
+ | EXPORT_ident of ident
+
+and mod_view =
+ {
+ view_imports: (ident, name) Hashtbl.t;
+ view_exports: (export, unit) Hashtbl.t;
+ }
+
+and meta = (ident * string) array
+
+and meta_pat = (ident * string option) array
+
+and crate' =
+ {
+ crate_items: (mod_view * mod_items);
+ crate_meta: meta;
+ crate_auth: (name, effect) Hashtbl.t;
+ crate_required: (node_id, (required_lib * nabi_conv)) Hashtbl.t;
+ crate_required_syms: (node_id, string) Hashtbl.t;
+ crate_files: (node_id,filename) Hashtbl.t;
+ crate_main: name option;
+ }
+and crate = crate' identified
+;;
+
+(*
+ * NB: names can only be type-parametric in their *last* path-entry.
+ * All path-entries before that must be ident or idx (non-parametric).
+ *)
+let sane_name (n:name) : bool =
+ let rec sane_prefix (n:name) : bool =
+ match n with
+ NAME_base (BASE_ident _)
+ | NAME_base (BASE_temp _) -> true
+ | NAME_ext (prefix, COMP_ident _)
+ | NAME_ext (prefix, COMP_idx _) -> sane_prefix prefix
+ | _ -> false
+ in
+ match n with
+ NAME_base _ -> true
+ | NAME_ext (prefix, _) -> sane_prefix prefix
+;;
+
+
+(***********************************************************************)
+
+(* FIXME (issue #19): finish all parts with ?foo? as their output. *)
+
+let fmt = Format.fprintf;;
+
+let fmt_ident (ff:Format.formatter) (i:ident) : unit =
+ fmt ff "%s" i
+
+let fmt_temp (ff:Format.formatter) (t:temp_id) : unit =
+ fmt ff ".t%d" (int_of_temp t)
+
+let fmt_slot_key ff (s:slot_key) : unit =
+ match s with
+ KEY_ident i -> fmt_ident ff i
+ | KEY_temp t -> fmt_temp ff t
+
+let rec fmt_app (ff:Format.formatter) (i:ident) (tys:ty array) : unit =
+ fmt ff "%s" i;
+ fmt_app_args ff tys
+
+and fmt_app_args (ff:Format.formatter) (tys:ty array) : unit =
+ fmt ff "[@[";
+ for i = 0 to (Array.length tys) - 1;
+ do
+ if i != 0
+ then fmt ff ",@ ";
+ fmt_ty ff tys.(i);
+ done;
+ fmt ff "@]]"
+
+and fmt_name_base (ff:Format.formatter) (nb:name_base) : unit =
+ match nb with
+ BASE_ident i -> fmt_ident ff i
+ | BASE_temp t -> fmt_temp ff t
+ | BASE_app (id, tys) -> fmt_app ff id tys
+
+and fmt_name_component (ff:Format.formatter) (nc:name_component) : unit =
+ match nc with
+ COMP_ident i -> fmt_ident ff i
+ | COMP_app (id, tys) -> fmt_app ff id tys
+ | COMP_idx i -> fmt ff "_%d" i
+
+and fmt_name (ff:Format.formatter) (n:name) : unit =
+ match n with
+ NAME_base nb -> fmt_name_base ff nb
+ | NAME_ext (n, nc) ->
+ fmt_name ff n;
+ fmt ff ".";
+ fmt_name_component ff nc
+
+and fmt_mutable (ff:Format.formatter) (m:bool) : unit =
+ if m
+ then fmt ff "mutable ";
+
+and fmt_mode (ff:Format.formatter) (m:mode) : unit =
+ match m with
+ MODE_exterior -> fmt ff "@@"
+ | MODE_alias -> fmt ff "&"
+ | MODE_interior -> ()
+
+and fmt_slot (ff:Format.formatter) (s:slot) : unit =
+ match s.slot_ty with
+ None -> fmt ff "auto"
+ | Some t ->
+ fmt_mutable ff s.slot_mutable;
+ fmt_mode ff s.slot_mode;
+ fmt_ty ff t
+
+and fmt_slots
+ (ff:Format.formatter)
+ (slots:slot array)
+ (idents:(ident array) option)
+ : unit =
+ fmt ff "(@[";
+ for i = 0 to (Array.length slots) - 1
+ do
+ if i != 0
+ then fmt ff ",@ ";
+ fmt_slot ff slots.(i);
+ begin
+ match idents with
+ None -> ()
+ | Some ids -> (fmt ff " "; fmt_ident ff ids.(i))
+ end;
+ done;
+ fmt ff "@])"
+
+and fmt_effect
+ (ff:Format.formatter)
+ (effect:effect)
+ : unit =
+ match effect with
+ PURE -> ()
+ | IO -> fmt ff "io"
+ | STATE -> fmt ff "state"
+ | UNSAFE -> fmt ff "unsafe"
+
+and fmt_ty_fn
+ (ff:Format.formatter)
+ (ident_and_params:(ident * ty_param array) option)
+ (tf:ty_fn)
+ : unit =
+ let (tsig, ta) = tf in
+ fmt_effect ff ta.fn_effect;
+ if ta.fn_effect <> PURE then fmt ff " ";
+ fmt ff "%s" (if ta.fn_is_iter then "iter" else "fn");
+ begin
+ match ident_and_params with
+ Some (id, params) ->
+ fmt ff " ";
+ fmt_ident_and_params ff id params
+ | None -> ()
+ end;
+ fmt_slots ff tsig.sig_input_slots None;
+ fmt_decl_constrs ff tsig.sig_input_constrs;
+ fmt ff " -> ";
+ fmt_slot ff tsig.sig_output_slot;
+
+and fmt_tag (ff:Format.formatter) (ttag:ty_tag) : unit =
+ fmt ff "@[tag(@[";
+ let first = ref true in
+ Hashtbl.iter
+ begin
+ fun name ttup ->
+ (if !first
+ then first := false
+ else fmt ff ",@ ");
+ fmt_name ff name;
+ fmt_slots ff ttup None
+ end
+ ttag;
+ fmt ff "@])@]"
+
+and fmt_iso (ff:Format.formatter) (tiso:ty_iso) : unit =
+ fmt ff "@[iso [@[";
+ for i = 0 to (Array.length tiso.iso_group) - 1
+ do
+ if i != 0
+ then fmt ff ",@ ";
+ if i == tiso.iso_index
+ then fmt ff "<%d>: " i
+ else fmt ff "%d: " i;
+ fmt_tag ff tiso.iso_group.(i);
+ done;
+ fmt ff "@]]@]"
+
+and fmt_ty (ff:Format.formatter) (t:ty) : unit =
+ match t with
+ TY_any -> fmt ff "any"
+ | TY_nil -> fmt ff "()"
+ | TY_bool -> fmt ff "bool"
+ | TY_mach m -> fmt_mach ff m
+ | TY_int -> fmt ff "int"
+ | TY_uint -> fmt ff "uint"
+ | TY_char -> fmt ff "char"
+ | TY_str -> fmt ff "str"
+
+ | TY_tup slots -> (fmt ff "tup"; fmt_slots ff slots None)
+ | TY_vec s -> (fmt ff "vec["; fmt_slot ff s; fmt ff "]")
+ | TY_chan t -> (fmt ff "chan["; fmt_ty ff t; fmt ff "]")
+ | TY_port t -> (fmt ff "port["; fmt_ty ff t; fmt ff "]")
+
+ | TY_rec slots ->
+ let (idents, slots) =
+ let (idents, slots) = List.split (Array.to_list slots) in
+ (Array.of_list idents, Array.of_list slots)
+ in
+ fmt ff "@[rec";
+ fmt_slots ff slots (Some idents);
+ fmt ff "@]"
+
+ | TY_param (i, e) -> (fmt_effect ff e;
+ if e <> PURE then fmt ff " ";
+ fmt ff "<p#%d>" i)
+ | TY_native oid -> fmt ff "<native#%d>" (int_of_opaque oid)
+ | TY_named n -> fmt_name ff n
+ | TY_type -> fmt ff "type"
+
+ | TY_fn tfn -> fmt_ty_fn ff None tfn
+ | TY_task -> fmt ff "task"
+ | TY_tag ttag -> fmt_tag ff ttag
+ | TY_iso tiso -> fmt_iso ff tiso
+ | TY_idx idx -> fmt ff "<idx#%d>" idx
+ | TY_constrained _ -> fmt ff "?constrained?"
+
+ | TY_obj (effect, fns) ->
+ fmt_obox ff;
+ fmt_effect ff effect;
+ if effect <> PURE then fmt ff " ";
+ fmt ff "obj ";
+ fmt_obr ff;
+ Hashtbl.iter
+ begin
+ fun id fn ->
+ fmt ff "@\n";
+ fmt_ty_fn ff (Some (id, [||])) fn;
+ fmt ff ";"
+ end
+ fns;
+ fmt_cbb ff
+
+
+and fmt_constrs (ff:Format.formatter) (cc:constr array) : unit =
+ Array.iter (fmt_constr ff) cc
+
+and fmt_decl_constrs (ff:Format.formatter) (cc:constr array) : unit =
+ if Array.length cc = 0
+ then ()
+ else
+ begin
+ fmt ff " : ";
+ fmt_constrs ff cc
+ end
+
+and fmt_constr (ff:Format.formatter) (c:constr) : unit =
+ fmt_name ff c.constr_name;
+ fmt ff "(@[";
+ for i = 0 to (Array.length c.constr_args) - 1
+ do
+ if i != 0
+ then fmt ff ",@ ";
+ fmt_carg ff c.constr_args.(i);
+ done;
+ fmt ff "@])"
+
+and fmt_carg_path (ff:Format.formatter) (cp:carg_path) : unit =
+ match cp with
+ CARG_base BASE_formal -> fmt ff "*"
+ | CARG_base (BASE_named nb) -> fmt_name_base ff nb
+ | CARG_ext (base, nc) ->
+ fmt_carg_path ff base;
+ fmt ff ".";
+ fmt_name_component ff nc
+
+and fmt_carg (ff:Format.formatter) (ca:carg) : unit =
+ match ca with
+ CARG_path cp -> fmt_carg_path ff cp
+ | CARG_lit lit -> fmt_lit ff lit
+
+and fmt_obox ff = Format.pp_open_box ff 4
+and fmt_obox_3 ff = Format.pp_open_box ff 3
+and fmt_cbox ff = Format.pp_close_box ff ()
+and fmt_obr ff = fmt ff "{"
+and fmt_cbr ff = fmt ff "@\n}"
+and fmt_cbb ff = (fmt_cbox ff; fmt_cbr ff)
+
+and fmt_stmts (ff:Format.formatter) (ss:stmt array) : unit =
+ Array.iter (fmt_stmt ff) ss;
+
+and fmt_block (ff:Format.formatter) (b:stmt array) : unit =
+ fmt_obox ff;
+ fmt_obr ff;
+ fmt_stmts ff b;
+ fmt_cbb ff;
+
+and fmt_binop (ff:Format.formatter) (b:binop) : unit =
+ fmt ff "%s"
+ begin
+ match b with
+ BINOP_or -> "|"
+ | BINOP_and -> "&"
+ | BINOP_xor -> "^"
+
+ | BINOP_eq -> "=="
+ | BINOP_ne -> "!="
+
+ | BINOP_lt -> "<"
+ | BINOP_le -> "<="
+ | BINOP_ge -> ">="
+ | BINOP_gt -> ">"
+
+ | BINOP_lsl -> "<<"
+ | BINOP_lsr -> ">>"
+ | BINOP_asr -> ">>>"
+
+ | BINOP_add -> "+"
+ | BINOP_sub -> "-"
+ | BINOP_mul -> "*"
+ | BINOP_div -> "/"
+ | BINOP_mod -> "%"
+ | BINOP_send -> "<|"
+ end
+
+
+and fmt_unop (ff:Format.formatter) (u:unop) (a:atom) : unit =
+ begin
+ match u with
+ UNOP_not ->
+ fmt ff "!";
+ fmt_atom ff a
+
+ | UNOP_bitnot ->
+ fmt ff "~";
+ fmt_atom ff a
+
+ | UNOP_neg ->
+ fmt ff "-";
+ fmt_atom ff a
+
+ | UNOP_cast t ->
+ fmt_atom ff a;
+ fmt ff " as ";
+ fmt_ty ff t.node;
+ end
+
+and fmt_expr (ff:Format.formatter) (e:expr) : unit =
+ match e with
+ EXPR_binary (b,a1,a2) ->
+ begin
+ fmt_atom ff a1;
+ fmt ff " ";
+ fmt_binop ff b;
+ fmt ff " ";
+ fmt_atom ff a2
+ end
+ | EXPR_unary (u,a) ->
+ begin
+ fmt_unop ff u a;
+ end
+ | EXPR_atom a -> fmt_atom ff a
+
+and fmt_mach (ff:Format.formatter) (m:ty_mach) : unit =
+ match m with
+ TY_u8 -> fmt ff "u8"
+ | TY_u16 -> fmt ff "u16"
+ | TY_u32 -> fmt ff "u32"
+ | TY_u64 -> fmt ff "u64"
+ | TY_i8 -> fmt ff "i8"
+ | TY_i16 -> fmt ff "i16"
+ | TY_i32 -> fmt ff "i32"
+ | TY_i64 -> fmt ff "i64"
+ | TY_f32 -> fmt ff "f32"
+ | TY_f64 -> fmt ff "f64"
+
+and fmt_lit (ff:Format.formatter) (l:lit) : unit =
+ match l with
+ | LIT_nil -> fmt ff "()"
+ | LIT_bool true -> fmt ff "true"
+ | LIT_bool false -> fmt ff "false"
+ | LIT_mach (m, _, s) ->
+ begin
+ fmt_mach ff m;
+ fmt ff "(%s)" s
+ end
+ | LIT_int (_,s) -> fmt ff "%s" s
+ | LIT_uint (_,s) -> fmt ff "%s" s
+ | LIT_char c -> fmt ff "'%s'" (Common.escaped_char c)
+
+and fmt_domain (ff:Format.formatter) (d:domain) : unit =
+ match d with
+ DOMAIN_local -> ()
+ | DOMAIN_thread -> fmt ff "thread "
+
+and fmt_atom (ff:Format.formatter) (a:atom) : unit =
+ match a with
+ ATOM_literal lit -> fmt_lit ff lit.node
+ | ATOM_lval lval -> fmt_lval ff lval
+
+and fmt_atoms (ff:Format.formatter) (az:atom array) : unit =
+ fmt ff "(";
+ Array.iteri
+ begin
+ fun i a ->
+ if i != 0
+ then fmt ff ", ";
+ fmt_atom ff a;
+ end
+ az;
+ fmt ff ")"
+
+and fmt_atom_opts (ff:Format.formatter) (az:(atom option) array) : unit =
+ fmt ff "(";
+ Array.iteri
+ begin
+ fun i a ->
+ if i != 0
+ then fmt ff ", ";
+ match a with
+ None -> fmt ff "_"
+ | Some a -> fmt_atom ff a;
+ end
+ az;
+ fmt ff ")"
+
+and fmt_lval_component (ff:Format.formatter) (lvc:lval_component) : unit =
+ match lvc with
+ COMP_named nc -> fmt_name_component ff nc
+ | COMP_atom a ->
+ begin
+ fmt ff "(";
+ fmt_atom ff a;
+ fmt ff ")"
+ end
+
+and fmt_lval (ff:Format.formatter) (l:lval) : unit =
+ match l with
+ LVAL_base nbi -> fmt_name_base ff nbi.node
+ | LVAL_ext (lv, lvc) ->
+ begin
+ fmt_lval ff lv;
+ fmt ff ".";
+ fmt_lval_component ff lvc
+ end
+
+and fmt_stmt (ff:Format.formatter) (s:stmt) : unit =
+ fmt ff "@\n";
+ fmt_stmt_body ff s
+
+and fmt_stmt_body (ff:Format.formatter) (s:stmt) : unit =
+ begin
+ match s.node with
+ STMT_log at ->
+ begin
+ fmt ff "log ";
+ fmt_atom ff at;
+ fmt ff ";"
+ end
+
+ | STMT_spawn (dst, domain, fn, args) ->
+ fmt_lval ff dst;
+ fmt ff " = spawn ";
+ fmt_domain ff domain;
+ fmt_lval ff fn;
+ fmt_atoms ff args;
+ fmt ff ";";
+
+ | STMT_while sw ->
+ let (stmts, e) = sw.while_lval in
+ begin
+ fmt_obox ff;
+ fmt ff "while (";
+ if Array.length stmts != 0
+ then fmt_block ff stmts;
+ fmt_expr ff e;
+ fmt ff ") ";
+ fmt_obr ff;
+ fmt_stmts ff sw.while_body.node;
+ fmt_cbb ff
+ end
+
+ | STMT_do_while sw ->
+ let (stmts, e) = sw.while_lval in
+ begin
+ fmt_obox ff;
+ fmt ff "do ";
+ fmt_obr ff;
+ fmt_stmts ff sw.while_body.node;
+ fmt ff "while (";
+ if Array.length stmts != 0
+ then fmt_block ff stmts;
+ fmt_expr ff e;
+ fmt ff ");";
+ fmt_cbb ff
+ end
+
+ | STMT_if sif ->
+ fmt_obox ff;
+ fmt ff "if (";
+ fmt_expr ff sif.if_test;
+ fmt ff ") ";
+ fmt_obr ff;
+ fmt_stmts ff sif.if_then.node;
+ begin
+ match sif.if_else with
+ None -> ()
+ | Some e ->
+ begin
+ fmt_cbb ff;
+ fmt_obox_3 ff;
+ fmt ff " else ";
+ fmt_obr ff;
+ fmt_stmts ff e.node
+ end
+ end;
+ fmt_cbb ff
+
+ | STMT_ret (ao) ->
+ fmt ff "ret";
+ begin
+ match ao with
+ None -> ()
+ | Some at ->
+ fmt ff " ";
+ fmt_atom ff at
+ end;
+ fmt ff ";"
+
+ | STMT_be (fn, az) ->
+ fmt ff "be ";
+ fmt_lval ff fn;
+ fmt_atoms ff az;
+ fmt ff ";";
+
+ | STMT_block b -> fmt_block ff b.node
+
+ | STMT_copy (lv, ex) ->
+ fmt_lval ff lv;
+ fmt ff " = ";
+ fmt_expr ff ex;
+ fmt ff ";"
+
+ | STMT_copy_binop (lv, binop, at) ->
+ fmt_lval ff lv;
+ fmt ff " ";
+ fmt_binop ff binop;
+ fmt ff "=";
+ fmt_atom ff at;
+ fmt ff ";"
+
+ | STMT_call (dst, fn, args) ->
+ fmt_lval ff dst;
+ fmt ff " = ";
+ fmt_lval ff fn;
+ fmt_atoms ff args;
+ fmt ff ";";
+
+ | STMT_bind (dst, fn, arg_opts) ->
+ fmt_lval ff dst;
+ fmt ff " = ";
+ fmt_lval ff fn;
+ fmt_atom_opts ff arg_opts;
+ fmt ff ";";
+
+ | STMT_decl (DECL_slot (skey, sloti)) ->
+ if sloti.node.slot_ty != None then fmt ff "let ";
+ fmt_slot ff sloti.node;
+ fmt ff " ";
+ fmt_slot_key ff skey;
+ fmt ff ";"
+
+ | STMT_decl (DECL_mod_item (ident, item)) ->
+ fmt_mod_item ff ident item
+
+ | STMT_init_rec (dst, entries, base) ->
+ fmt_lval ff dst;
+ fmt ff " = rec(";
+ for i = 0 to (Array.length entries) - 1
+ do
+ if i != 0
+ then fmt ff ", ";
+ let (ident, mode, mut, atom) = entries.(i) in
+ fmt_ident ff ident;
+ fmt ff " = ";
+ fmt_mutable ff mut;
+ fmt_mode ff mode;
+ fmt_atom ff atom;
+ done;
+ begin
+ match base with
+ None -> ()
+ | Some b ->
+ fmt ff " with ";
+ fmt_lval ff b
+ end;
+ fmt ff ");"
+
+ | STMT_init_vec (dst, _, atoms) ->
+ fmt_lval ff dst;
+ fmt ff " = vec(";
+ for i = 0 to (Array.length atoms) - 1
+ do
+ if i != 0
+ then fmt ff ", ";
+ fmt_atom ff atoms.(i);
+ done;
+ fmt ff ");"
+
+ | STMT_init_tup (dst, entries) ->
+ fmt_lval ff dst;
+ fmt ff " = (";
+ for i = 0 to (Array.length entries) - 1
+ do
+ if i != 0
+ then fmt ff ", ";
+ let (mode, mut, atom) = entries.(i) in
+ fmt_mutable ff mut;
+ fmt_mode ff mode;
+ fmt_atom ff atom;
+ done;
+ fmt ff ");";
+
+ | STMT_init_str (dst, s) ->
+ fmt_lval ff dst;
+ fmt ff " = \"%s\"" (String.escaped s)
+
+ | STMT_init_port dst ->
+ fmt_lval ff dst;
+ fmt ff " = port();"
+
+ | STMT_init_chan (dst, port_opt) ->
+ fmt_lval ff dst;
+ fmt ff " = chan(";
+ begin
+ match port_opt with
+ None -> ()
+ | Some lv -> fmt_lval ff lv
+ end;
+ fmt ff ");"
+
+ | STMT_check_expr expr ->
+ fmt ff "check (";
+ fmt_expr ff expr;
+ fmt ff ");"
+
+ | STMT_check_if (constrs, _, block) ->
+ fmt_obox ff;
+ fmt ff "check if (";
+ fmt_constrs ff constrs;
+ fmt ff ")";
+ fmt_obr ff;
+ fmt_stmts ff block.node;
+ fmt_cbb ff
+
+ | STMT_check (constrs, _) ->
+ fmt ff "check ";
+ fmt_constrs ff constrs;
+ fmt ff ";"
+
+ | STMT_prove constrs ->
+ fmt ff "prove ";
+ fmt_constrs ff constrs;
+ fmt ff ";"
+
+ | STMT_for sfor ->
+ let (slot, ident) = sfor.for_slot in
+ let (stmts, lval) = sfor.for_seq in
+ begin
+ fmt_obox ff;
+ fmt ff "for (";
+ fmt_slot ff slot.node;
+ fmt ff " ";
+ fmt_ident ff ident;
+ fmt ff " in ";
+ fmt_stmts ff stmts;
+ fmt_lval ff lval;
+ fmt ff ") ";
+ fmt_obr ff;
+ fmt_stmts ff sfor.for_body.node;
+ fmt_cbb ff
+ end
+
+ | STMT_for_each sf ->
+ let (slot, ident) = sf.for_each_slot in
+ let (f, az) = sf.for_each_call in
+ begin
+ fmt_obox ff;
+ fmt ff "for each (";
+ fmt_slot ff slot.node;
+ fmt ff " ";
+ fmt_ident ff ident;
+ fmt ff " = ";
+ fmt_lval ff f;
+ fmt_atoms ff az;
+ fmt ff " ";
+ fmt_obr ff;
+ fmt_stmts ff sf.for_each_body.node;
+ fmt_cbb ff
+ end
+
+ | STMT_put (atom) ->
+ fmt ff "put ";
+ begin
+ match atom with
+ Some a -> (fmt ff " "; fmt_atom ff a)
+ | None -> ()
+ end;
+ fmt ff ";"
+
+ | STMT_put_each (f, az) ->
+ fmt ff "put each ";
+ fmt_lval ff f;
+ fmt_atoms ff az;
+ fmt ff ";"
+
+ | STMT_fail -> fmt ff "fail;"
+ | STMT_yield -> fmt ff "yield;"
+
+ | STMT_send (chan, v) ->
+ fmt_lval ff chan;
+ fmt ff " <| ";
+ fmt_lval ff v;
+ fmt ff ";";
+
+ | STMT_recv (d, port) ->
+ fmt_lval ff d;
+ fmt ff " <- ";
+ fmt_lval ff port;
+ fmt ff ";";
+
+ | STMT_join t ->
+ fmt ff "join ";
+ fmt_lval ff t;
+ fmt ff ";"
+
+ | STMT_alt_tag _ -> fmt ff "?stmt_alt_tag?"
+ | STMT_alt_type _ -> fmt ff "?stmt_alt_type?"
+ | STMT_alt_port _ -> fmt ff "?stmt_alt_port?"
+ | STMT_note _ -> fmt ff "?stmt_note?"
+ | STMT_slice _ -> fmt ff "?stmt_slice?"
+ end
+
+and fmt_decl_params (ff:Format.formatter) (params:ty_param array) : unit =
+ if Array.length params = 0
+ then ()
+ else
+ begin
+ fmt ff "[";
+ for i = 0 to (Array.length params) - 1
+ do
+ if i <> 0
+ then fmt ff ", ";
+ let (ident, (i, e)) = params.(i) in
+ fmt_effect ff e;
+ if e <> PURE then fmt ff " ";
+ fmt_ident ff ident;
+ fmt ff "=<p#%d>" i
+ done;
+ fmt ff "]"
+ end;
+
+and fmt_header_slots (ff:Format.formatter) (hslots:header_slots) : unit =
+ fmt_slots ff
+ (Array.map (fun (s,_) -> s.node) hslots)
+ (Some (Array.map (fun (_, i) -> i) hslots))
+
+and fmt_ident_and_params
+ (ff:Format.formatter)
+ (id:ident)
+ (params:ty_param array)
+ : unit =
+ fmt_ident ff id;
+ fmt_decl_params ff params
+
+and fmt_fn
+ (ff:Format.formatter)
+ (id:ident)
+ (params:ty_param array)
+ (f:fn)
+ : unit =
+ fmt_obox ff;
+ fmt_effect ff f.fn_aux.fn_effect;
+ if f.fn_aux.fn_effect <> PURE then fmt ff " ";
+ fmt ff "%s "(if f.fn_aux.fn_is_iter then "iter" else "fn");
+ fmt_ident_and_params ff id params;
+ fmt_header_slots ff f.fn_input_slots;
+ fmt_decl_constrs ff f.fn_input_constrs;
+ fmt ff " -> ";
+ fmt_slot ff f.fn_output_slot.node;
+ fmt ff " ";
+ fmt_obr ff;
+ fmt_stmts ff f.fn_body.node;
+ fmt_cbb ff
+
+
+and fmt_obj
+ (ff:Format.formatter)
+ (id:ident)
+ (params:ty_param array)
+ (obj:obj)
+ : unit =
+ fmt_obox ff;
+ fmt_effect ff obj.obj_effect;
+ if obj.obj_effect <> PURE then fmt ff " ";
+ fmt ff "obj ";
+ fmt_ident_and_params ff id params;
+ fmt_header_slots ff obj.obj_state;
+ fmt_decl_constrs ff obj.obj_constrs;
+ fmt ff " ";
+ fmt_obr ff;
+ Hashtbl.iter
+ begin
+ fun id fn ->
+ fmt ff "@\n";
+ fmt_fn ff id [||] fn.node
+ end
+ obj.obj_fns;
+ begin
+ match obj.obj_drop with
+ None -> ()
+ | Some d ->
+ begin
+ fmt ff "@\n";
+ fmt_obox ff;
+ fmt ff "drop ";
+ fmt_obr ff;
+ fmt_stmts ff d.node;
+ fmt_cbb ff;
+ end
+ end;
+ fmt_cbb ff
+
+
+and fmt_mod_item (ff:Format.formatter) (id:ident) (item:mod_item) : unit =
+ fmt ff "@\n";
+ let params = item.node.decl_params in
+ let params = Array.map (fun i -> i.node) params in
+ begin
+ match item.node.decl_item with
+ MOD_ITEM_type ty ->
+ fmt ff "type ";
+ fmt_ident_and_params ff id params;
+ fmt ff " = ";
+ fmt_ty ff ty;
+ fmt ff ";";
+
+ | MOD_ITEM_tag (hdr, ttag, _) ->
+ fmt ff "fn ";
+ fmt_ident_and_params ff id params;
+ fmt_header_slots ff
+ (Array.mapi (fun i s -> (s,(Printf.sprintf "_%d" i))) hdr);
+ fmt ff " -> ";
+ fmt_ty ff (TY_tag ttag);
+ fmt ff ";";
+
+ | MOD_ITEM_mod (view,items) ->
+ fmt_obox ff;
+ fmt ff "mod ";
+ fmt_ident_and_params ff id params;
+ fmt ff " ";
+ fmt_obr ff;
+ fmt_mod_view ff view;
+ fmt_mod_items ff items;
+ fmt_cbb ff
+
+ | MOD_ITEM_fn f ->
+ fmt_fn ff id params f
+
+ | MOD_ITEM_obj obj ->
+ fmt_obj ff id params obj
+ end
+
+and fmt_import (ff:Format.formatter) (ident:ident) (name:name) : unit =
+ fmt ff "@\n";
+ fmt ff "import ";
+ fmt ff "%s = " ident;
+ fmt_name ff name;
+
+and fmt_export (ff:Format.formatter) (export:export) _ : unit =
+ fmt ff "@\n";
+ match export with
+ EXPORT_all_decls -> fmt ff "export *;"
+ | EXPORT_ident i -> fmt ff "export %s;" i
+
+and fmt_mod_view (ff:Format.formatter) (mv:mod_view) : unit =
+ Hashtbl.iter (fmt_import ff) mv.view_imports;
+ Hashtbl.iter (fmt_export ff) mv.view_exports
+
+and fmt_mod_items (ff:Format.formatter) (mi:mod_items) : unit =
+ Hashtbl.iter (fmt_mod_item ff) mi
+
+and fmt_crate (ff:Format.formatter) (c:crate) : unit =
+ let (view,items) = c.node.crate_items in
+ fmt_mod_view ff view;
+ fmt_mod_items ff items
+
+
+let fmt_to_str (f:Format.formatter -> 'a -> unit) (v:'a) : string =
+ let buf = Buffer.create 16 in
+ let bf = Format.formatter_of_buffer buf in
+ begin
+ f bf v;
+ Format.pp_print_flush bf ();
+ Buffer.contents buf
+ end
+
+let sprintf_fmt
+ (f:Format.formatter -> 'a -> unit)
+ : (unit -> 'a -> string) =
+ (fun _ -> fmt_to_str f)
+
+
+let sprintf_expr = sprintf_fmt fmt_expr;;
+let sprintf_name = sprintf_fmt fmt_name;;
+let sprintf_lval = sprintf_fmt fmt_lval;;
+let sprintf_lval_component = sprintf_fmt fmt_lval_component;;
+let sprintf_atom = sprintf_fmt fmt_atom;;
+let sprintf_slot = sprintf_fmt fmt_slot;;
+let sprintf_slot_key = sprintf_fmt fmt_slot_key;;
+let sprintf_mutable = sprintf_fmt fmt_mutable;;
+let sprintf_ty = sprintf_fmt fmt_ty;;
+let sprintf_effect = sprintf_fmt fmt_effect;;
+let sprintf_tag = sprintf_fmt fmt_tag;;
+let sprintf_carg = sprintf_fmt fmt_carg;;
+let sprintf_constr = sprintf_fmt fmt_constr;;
+let sprintf_stmt = sprintf_fmt fmt_stmt;;
+let sprintf_mod_items = sprintf_fmt fmt_mod_items;;
+let sprintf_decl_params = sprintf_fmt fmt_decl_params;;
+let sprintf_app_args = sprintf_fmt fmt_app_args;;
+
+(*
+ * 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/cexp.ml b/src/boot/fe/cexp.ml
new file mode 100644
index 00000000..6dffdb96
--- /dev/null
+++ b/src/boot/fe/cexp.ml
@@ -0,0 +1,762 @@
+
+open Common;;
+open Token;;
+open Parser;;
+
+(* NB: cexps (crate-expressions / constant-expressions) are only used
+ * transiently during compilation: they are the outermost expression-language
+ * describing crate configuration and constants. They are completely evaluated
+ * at compile-time, in a little micro-interpreter defined here, with the
+ * results of evaluation being the sequence of directives controlling the rest
+ * of the compiler.
+ *
+ * Cexps, like pexps, do not escape the language front-end.
+ *
+ * You can think of the AST as a statement-language called "item" sandwiched
+ * between two expression-languages, "cexp" on the outside and "pexp" on the
+ * inside. The front-end evaluates cexp on the outside in order to get one big
+ * directive-list, evaluating those parts of pexp that are directly used by
+ * cexp in passing, and desugaring those remaining parts of pexp that are
+ * embedded within the items of the directives.
+ *
+ * The rest of the compiler only deals with the directives, which are mostly
+ * just a set of containers for items. Items are what most of AST describes
+ * ("most" because the type-grammar spans both items and pexps).
+ *
+ *)
+
+type meta = (Ast.ident * Pexp.pexp) array;;
+
+type meta_pat = (Ast.ident * (Pexp.pexp option)) array;;
+
+type auth = (Ast.name * Ast.effect);;
+
+type cexp =
+ CEXP_alt of cexp_alt identified
+ | CEXP_let of cexp_let identified
+ | CEXP_src_mod of cexp_src identified
+ | CEXP_dir_mod of cexp_dir identified
+ | CEXP_use_mod of cexp_use identified
+ | CEXP_nat_mod of cexp_nat identified
+ | CEXP_meta of meta identified
+ | CEXP_auth of auth identified
+
+and cexp_alt =
+ { alt_val: Pexp.pexp;
+ alt_arms: (Pexp.pexp * cexp array) array;
+ alt_else: cexp array }
+
+and cexp_let =
+ { let_ident: Ast.ident;
+ let_value: Pexp.pexp;
+ let_body: cexp array; }
+
+and cexp_src =
+ { src_ident: Ast.ident;
+ src_path: Pexp.pexp option }
+
+and cexp_dir =
+ { dir_ident: Ast.ident;
+ dir_path: Pexp.pexp option;
+ dir_body: cexp array }
+
+and cexp_use =
+ { use_ident: Ast.ident;
+ use_meta: meta_pat; }
+
+and cexp_nat =
+ { nat_abi: string;
+ nat_ident: Ast.ident;
+ nat_path: Pexp.pexp option;
+ (*
+ * FIXME: possibly support embedding optional strings as
+ * symbol-names, to handle mangling schemes that aren't
+ * Token.IDENT values
+ *)
+ nat_items: Ast.mod_items;
+ }
+;;
+
+
+(* Cexp grammar. *)
+
+let parse_meta_input (ps:pstate) : (Ast.ident * Pexp.pexp option) =
+ let lab = (ctxt "meta input: label" Pexp.parse_ident ps) in
+ match peek ps with
+ EQ ->
+ bump ps;
+ let v =
+ match peek ps with
+ UNDERSCORE -> bump ps; None
+ | _ -> Some (Pexp.parse_pexp ps)
+ in
+ (lab, v)
+ | _ -> raise (unexpected ps)
+;;
+
+let parse_meta_pat (ps:pstate) : meta_pat =
+ bracketed_zero_or_more LPAREN RPAREN
+ (Some COMMA) parse_meta_input ps
+;;
+
+let parse_meta (ps:pstate) : meta =
+ Array.map
+ begin
+ fun (id,v) ->
+ match v with
+ None ->
+ raise (err ("wildcard found in meta pattern "
+ ^ "where value expected") ps)
+ | Some v -> (id,v)
+ end
+ (parse_meta_pat ps)
+;;
+
+let parse_optional_meta_pat
+ (ps:pstate)
+ (ident:Ast.ident)
+ : meta_pat =
+ match peek ps with
+ LPAREN -> parse_meta_pat ps
+ | _ ->
+ let apos = lexpos ps in
+ [| ("name", Some (span ps apos apos (Pexp.PEXP_str ident))) |]
+;;
+
+let rec parse_cexps (ps:pstate) (term:Token.token) : cexp array =
+ let cexps = Queue.create () in
+ while ((peek ps) <> term)
+ do
+ Queue.push (parse_cexp ps) cexps
+ done;
+ expect ps term;
+ queue_to_arr cexps
+
+and parse_cexp (ps:pstate) : cexp =
+
+ let apos = lexpos ps in
+ match peek ps with
+ MOD ->
+ begin
+ bump ps;
+ let name = ctxt "mod: name" Pexp.parse_ident ps in
+ let path = ctxt "mod: path" parse_eq_pexp_opt ps
+ in
+ match peek ps with
+ SEMI ->
+ bump ps;
+ let bpos = lexpos ps in
+ CEXP_src_mod
+ (span ps apos bpos { src_ident = name;
+ src_path = path })
+ | LBRACE ->
+ let body =
+ bracketed_zero_or_more LBRACE RBRACE
+ None parse_cexp ps
+ in
+ let bpos = lexpos ps in
+ CEXP_dir_mod
+ (span ps apos bpos { dir_ident = name;
+ dir_path = path;
+ dir_body = body })
+ | _ -> raise (unexpected ps)
+ end
+
+ | NATIVE ->
+ begin
+ bump ps;
+ let abi =
+ match peek ps with
+ MOD -> "cdecl"
+ | LIT_STR s -> bump ps; s
+ | _ -> raise (unexpected ps)
+ in
+ let _ = expect ps MOD in
+ let name = ctxt "native mod: name" Pexp.parse_ident ps in
+ let path = ctxt "native mod: path" parse_eq_pexp_opt ps in
+ let items = Hashtbl.create 0 in
+ let get_item ps =
+ let (ident, item) = Item.parse_mod_item_from_signature ps in
+ htab_put items ident item;
+ in
+ ignore (bracketed_zero_or_more
+ LBRACE RBRACE None get_item ps);
+ let bpos = lexpos ps in
+ CEXP_nat_mod
+ (span ps apos bpos { nat_abi = abi;
+ nat_ident = name;
+ nat_path = path;
+ nat_items = items })
+ end
+
+ | USE ->
+ begin
+ bump ps;
+ let ident = ctxt "use mod: name" Pexp.parse_ident ps in
+ let meta =
+ ctxt "use mod: meta" parse_optional_meta_pat ps ident
+ in
+ let bpos = lexpos ps in
+ expect ps SEMI;
+ CEXP_use_mod
+ (span ps apos bpos { use_ident = ident;
+ use_meta = meta })
+ end
+
+ | LET ->
+ begin
+ bump ps;
+ expect ps LPAREN;
+ let id = Pexp.parse_ident ps in
+ expect ps EQ;
+ let v = Pexp.parse_pexp ps in
+ expect ps RPAREN;
+ expect ps LBRACE;
+ let body = parse_cexps ps RBRACE in
+ let bpos = lexpos ps in
+ CEXP_let
+ (span ps apos bpos
+ { let_ident = id;
+ let_value = v;
+ let_body = body })
+ end
+
+ | ALT ->
+ begin
+ bump ps;
+ expect ps LPAREN;
+ let v = Pexp.parse_pexp ps in
+ expect ps RPAREN;
+ expect ps LBRACE;
+ let rec consume_arms arms =
+ match peek ps with
+ CASE ->
+ begin
+ bump ps;
+ expect ps LPAREN;
+ let cond = Pexp.parse_pexp ps in
+ expect ps RPAREN;
+ expect ps LBRACE;
+ let consequent = parse_cexps ps RBRACE in
+ let arm = (cond, consequent) in
+ consume_arms (arm::arms)
+ end
+ | ELSE ->
+ begin
+ bump ps;
+ expect ps LBRACE;
+ let consequent = parse_cexps ps RBRACE in
+ expect ps RBRACE;
+ let bpos = lexpos ps in
+ span ps apos bpos
+ { alt_val = v;
+ alt_arms = Array.of_list (List.rev arms);
+ alt_else = consequent }
+ end
+
+ | _ -> raise (unexpected ps)
+ in
+ CEXP_alt (consume_arms [])
+ end
+
+ | META ->
+ bump ps;
+ let meta = parse_meta ps in
+ expect ps SEMI;
+ let bpos = lexpos ps in
+ CEXP_meta (span ps apos bpos meta)
+
+ | AUTH ->
+ bump ps;
+ let name = Pexp.parse_name ps in
+ expect ps EQ;
+ let effect = Pexp.parse_effect ps in
+ expect ps SEMI;
+ let bpos = lexpos ps in
+ CEXP_auth (span ps apos bpos (name, effect))
+
+ | _ -> raise (unexpected ps)
+
+
+and parse_eq_pexp_opt (ps:pstate) : Pexp.pexp option =
+ match peek ps with
+ EQ ->
+ begin
+ bump ps;
+ Some (Pexp.parse_pexp ps)
+ end
+ | _ -> None
+;;
+
+
+(*
+ * Dynamic-typed micro-interpreter for the cexp language.
+ *
+ * The product of evaluating a pexp is a pval.
+ *
+ * The product of evlauating a cexp is a cdir array.
+ *)
+
+type pval =
+ PVAL_str of string
+ | PVAL_num of int64
+ | PVAL_bool of bool
+;;
+
+type cdir =
+ CDIR_meta of ((Ast.ident * string) array)
+ | CDIR_syntax of Ast.name
+ | CDIR_check of (Ast.name * pval array)
+ | CDIR_mod of (Ast.ident * Ast.mod_item)
+ | CDIR_auth of auth
+
+type env = { env_bindings: (Ast.ident * pval) list;
+ env_prefix: filename list;
+ env_items: (filename, Ast.mod_items) Hashtbl.t;
+ env_files: (node_id,filename) Hashtbl.t;
+ env_required: (node_id, (required_lib * nabi_conv)) Hashtbl.t;
+ env_required_syms: (node_id, string) Hashtbl.t;
+ env_ps: pstate; }
+
+let unexpected_val (expected:string) (v:pval) =
+ let got =
+ match v with
+ PVAL_str s -> "str \"" ^ (String.escaped s) ^ "\""
+ | PVAL_num i -> "num " ^ (Int64.to_string i)
+ | PVAL_bool b -> if b then "bool true" else "bool false"
+ in
+ (* FIXME: proper error reporting, please. *)
+ bug () "expected %s, got %s" expected got
+;;
+
+let rewrap_items id items =
+ let item = decl [||] (Ast.MOD_ITEM_mod items) in
+ { id = id; node = item }
+;;
+
+
+let rec eval_cexps (env:env) (exps:cexp array) : cdir array =
+ Parser.arj (Array.map (eval_cexp env) exps)
+
+and eval_cexp (env:env) (exp:cexp) : cdir array =
+ match exp with
+ CEXP_alt {node=ca} ->
+ let v = eval_pexp env ca.alt_val in
+ let rec try_arm i =
+ if i >= Array.length ca.alt_arms
+ then ca.alt_else
+ else
+ let (arm_head, arm_body) = ca.alt_arms.(i) in
+ let v' = eval_pexp env arm_head in
+ if v' = v
+ then arm_body
+ else try_arm (i+1)
+ in
+ eval_cexps env (try_arm 0)
+
+ | 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
+
+ | CEXP_src_mod {node=s; id=id} ->
+ let name = s.src_ident in
+ let path =
+ match s.src_path with
+ None -> name ^ ".rs"
+ | Some p -> eval_pexp_to_str env p
+ in
+ let full_path =
+ List.fold_left Filename.concat ""
+ (List.rev (path :: env.env_prefix))
+ in
+ let ps = env.env_ps in
+ let p =
+ make_parser
+ ps.pstate_temp_id
+ ps.pstate_node_id
+ ps.pstate_opaque_id
+ ps.pstate_sess
+ ps.pstate_get_mod
+ ps.pstate_infer_lib_name
+ env.env_required
+ env.env_required_syms
+ full_path
+ in
+ let items = Item.parse_mod_items p EOF in
+ htab_put env.env_files id full_path;
+ [| CDIR_mod (name, rewrap_items id items) |]
+
+ | CEXP_dir_mod {node=d; id=id} ->
+ let items = Hashtbl.create 0 in
+ let name = d.dir_ident in
+ let path =
+ match d.dir_path with
+ None -> name
+ | Some p -> eval_pexp_to_str env p
+ in
+ let env = { env with
+ env_prefix = path :: env.env_prefix } in
+ let sub_directives = eval_cexps env d.dir_body in
+ let add d =
+ match d with
+ CDIR_mod (name, item) ->
+ htab_put items name item
+ | _ -> raise (err "non-'mod' directive found in 'dir' directive"
+ env.env_ps)
+ in
+ Array.iter add sub_directives;
+ [| CDIR_mod (name, rewrap_items id (Item.empty_view, items)) |]
+
+ | CEXP_use_mod {node=u; id=id} ->
+ let ps = env.env_ps in
+ let name = u.use_ident in
+ let (path, items) =
+ let meta_pat =
+ Array.map
+ begin
+ fun (k,vo) ->
+ match vo with
+ None -> (k, None)
+ | Some p -> (k, Some (eval_pexp_to_str env p))
+ end
+ u.use_meta
+ in
+ ps.pstate_get_mod meta_pat id ps.pstate_node_id ps.pstate_opaque_id
+ in
+ iflog ps
+ begin
+ fun _ ->
+ log ps "extracted mod signature from %s (binding to %s)"
+ path name;
+ log ps "%a" Ast.sprintf_mod_items items;
+ end;
+ let rlib = REQUIRED_LIB_rust { required_libname = path;
+ required_prefix = 1 }
+ in
+ let item = decl [||] (Ast.MOD_ITEM_mod (Item.empty_view, items)) in
+ let item = { id = id; node = item } in
+ let span = Hashtbl.find ps.pstate_sess.Session.sess_spans id in
+ Item.note_required_mod env.env_ps span CONV_rust rlib item;
+ [| CDIR_mod (name, item) |]
+
+ | CEXP_nat_mod {node=cn;id=id} ->
+ let conv =
+ let v = cn.nat_abi in
+ match string_to_conv v with
+ None -> unexpected_val "calling convention" (PVAL_str v)
+ | Some c -> c
+ in
+ let name = cn.nat_ident in
+ let filename =
+ match cn.nat_path with
+ None -> env.env_ps.pstate_infer_lib_name name
+ | Some p -> eval_pexp_to_str env p
+ in
+ let item =
+ decl [||] (Ast.MOD_ITEM_mod (Item.empty_view, cn.nat_items))
+ in
+ let item = { id = id; node = item } in
+ let rlib = REQUIRED_LIB_c { required_libname = filename;
+ required_prefix = 1 }
+ in
+ let ps = env.env_ps in
+ let span = Hashtbl.find ps.pstate_sess.Session.sess_spans id in
+ Item.note_required_mod env.env_ps span conv rlib item;
+ [| CDIR_mod (name, item) |]
+
+ | CEXP_meta m ->
+ [| CDIR_meta
+ begin
+ Array.map
+ begin
+ fun (id, p) -> (id, eval_pexp_to_str env p)
+ end
+ m.node
+ end |]
+
+ | CEXP_auth a -> [| CDIR_auth a.node |]
+
+
+and eval_pexp (env:env) (exp:Pexp.pexp) : pval =
+ match exp.node with
+ | Pexp.PEXP_binop (bop, a, b) ->
+ begin
+ let av = eval_pexp env a in
+ let bv = eval_pexp env b in
+ match (bop, av, bv) with
+ (Ast.BINOP_add, PVAL_str az, PVAL_str bz) ->
+ PVAL_str (az ^ bz)
+ | _ ->
+ let av = (need_num av) in
+ let bv = (need_num bv) in
+ PVAL_num
+ begin
+ match bop with
+ Ast.BINOP_add -> Int64.add av bv
+ | Ast.BINOP_sub -> Int64.sub av bv
+ | Ast.BINOP_mul -> Int64.mul av bv
+ | Ast.BINOP_div -> Int64.div av bv
+ | _ ->
+ bug ()
+ "unhandled arithmetic op in Cexp.eval_pexp"
+ end
+ end
+
+ | Pexp.PEXP_unop (uop, a) ->
+ begin
+ match uop with
+ Ast.UNOP_not ->
+ PVAL_bool (not (eval_pexp_to_bool env a))
+ | Ast.UNOP_neg ->
+ PVAL_num (Int64.neg (eval_pexp_to_num env a))
+ | _ -> bug () "Unexpected unop in Cexp.eval_pexp"
+ end
+
+ | Pexp.PEXP_lval (Pexp.PLVAL_ident ident) ->
+ begin
+ 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
+ end
+
+ | Pexp.PEXP_lit (Ast.LIT_bool b) ->
+ PVAL_bool b
+
+ | Pexp.PEXP_lit (Ast.LIT_int (i, _)) ->
+ PVAL_num i
+
+ | Pexp.PEXP_str s ->
+ PVAL_str s
+
+ | _ -> bug () "unexpected Pexp in Cexp.eval_pexp"
+
+
+and eval_pexp_to_str (env:env) (exp:Pexp.pexp) : string =
+ match eval_pexp env exp with
+ PVAL_str s -> s
+ | v -> unexpected_val "str" v
+
+and need_num (cv:pval) : int64 =
+ match cv with
+ PVAL_num n -> n
+ | v -> unexpected_val "num" v
+
+and eval_pexp_to_num (env:env) (exp:Pexp.pexp) : int64 =
+ need_num (eval_pexp env exp)
+
+and eval_pexp_to_bool (env:env) (exp:Pexp.pexp) : bool =
+ match eval_pexp env exp with
+ PVAL_bool b -> b
+ | v -> unexpected_val "bool" v
+
+;;
+
+
+let find_main_fn
+ (ps:pstate)
+ (crate_items:Ast.mod_items)
+ : Ast.name =
+ let fns = ref [] in
+ let extend prefix_name ident =
+ match prefix_name with
+ None -> Ast.NAME_base (Ast.BASE_ident ident)
+ | Some n -> Ast.NAME_ext (n, Ast.COMP_ident ident)
+ in
+ let rec dig prefix_name items =
+ Hashtbl.iter (extract_fn prefix_name) items
+ and extract_fn prefix_name ident item =
+ if not (Array.length item.node.Ast.decl_params = 0) ||
+ Hashtbl.mem ps.pstate_required item.id
+ then ()
+ else
+ match item.node.Ast.decl_item with
+ Ast.MOD_ITEM_mod (_, items) ->
+ dig (Some (extend prefix_name ident)) items
+
+ | Ast.MOD_ITEM_fn _ ->
+ if ident = "main"
+ then fns := (extend prefix_name ident) :: (!fns)
+ else ()
+
+ | _ -> ()
+ in
+ dig None crate_items;
+ match !fns with
+ [] -> raise (err "no 'main' function found" ps)
+ | [x] -> x
+ | _ -> raise (err "multiple 'main' functions found" ps)
+;;
+
+
+let with_err_handling sess thunk =
+ try
+ thunk ()
+ with
+ Parse_err (ps, str) ->
+ Session.fail sess "Parse error: %s\n%!" str;
+ List.iter
+ (fun (cx,pos) ->
+ Session.fail sess "%s:E (parse context): %s\n%!"
+ (Session.string_of_pos pos) cx)
+ ps.pstate_ctxt;
+ let apos = lexpos ps in
+ span ps apos apos
+ { Ast.crate_items = (Item.empty_view, Hashtbl.create 0);
+ Ast.crate_meta = [||];
+ Ast.crate_auth = Hashtbl.create 0;
+ Ast.crate_required = Hashtbl.create 0;
+ Ast.crate_required_syms = Hashtbl.create 0;
+ Ast.crate_main = None;
+ Ast.crate_files = Hashtbl.create 0 }
+;;
+
+
+let parse_crate_file
+ (sess:Session.sess)
+ (get_mod:get_mod_fn)
+ (infer_lib_name:(Ast.ident -> filename))
+ : Ast.crate =
+ let fname = Session.filename_of sess.Session.sess_in in
+ let tref = ref (Temp 0) in
+ let nref = ref (Node 0) in
+ 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 =
+ let (os, arch, libc) =
+ match sess.Session.sess_targ with
+ Linux_x86_elf -> ("linux", "x86", "libc.so.6")
+ | Win32_x86_pe -> ("win32", "x86", "msvcrt.dll")
+ | MacOS_x86_macho -> ("macos", "x86", "libc.dylib")
+ in
+ [
+ ("target_os", PVAL_str os);
+ ("target_arch", PVAL_str arch);
+ ("target_libc", PVAL_str libc)
+ ]
+ in
+ let build_bindings =
+ [
+ ("build_compiler", PVAL_str Sys.executable_name);
+ ("build_input", PVAL_str fname);
+ ]
+ in
+ let initial_bindings =
+ target_bindings
+ @ build_bindings
+ in
+ let env = { env_bindings = initial_bindings;
+ env_prefix = [Filename.dirname fname];
+ env_items = Hashtbl.create 0;
+ env_files = files;
+ env_required = required;
+ env_required_syms = required_syms;
+ env_ps = ps; }
+ in
+ let auth = Hashtbl.create 0 in
+ with_err_handling sess
+ begin
+ fun _ ->
+ let apos = lexpos ps in
+ let cexps = parse_cexps ps EOF in
+ let cdirs = eval_cexps env cexps in
+ let meta = Queue.create () in
+ let _ =
+ Array.iter
+ begin
+ fun d ->
+ match d with
+ CDIR_mod (name, item) -> htab_put items name item
+ | CDIR_meta metas ->
+ Array.iter (fun m -> Queue.add m meta) metas
+ | CDIR_auth (n,e) ->
+ if Hashtbl.mem auth n
+ then raise (err "duplicate 'auth' clause" ps)
+ else Hashtbl.add auth n e
+ | _ ->
+ raise
+ (err "unhandled directive at top level" ps)
+ end
+ cdirs
+ in
+ let bpos = lexpos ps in
+ let main =
+ if ps.pstate_sess.Session.sess_library_mode
+ then None
+ else Some (find_main_fn ps items) in
+ let crate = { Ast.crate_items = (Item.empty_view, items);
+ Ast.crate_meta = queue_to_arr meta;
+ Ast.crate_auth = auth;
+ Ast.crate_required = required;
+ Ast.crate_required_syms = required_syms;
+ Ast.crate_main = main;
+ Ast.crate_files = files }
+ in
+ let cratei = span ps apos bpos crate in
+ htab_put files cratei.id fname;
+ cratei
+ end
+;;
+
+let parse_src_file
+ (sess:Session.sess)
+ (get_mod:get_mod_fn)
+ (infer_lib_name:(Ast.ident -> filename))
+ : Ast.crate =
+ let fname = Session.filename_of sess.Session.sess_in in
+ let tref = ref (Temp 0) in
+ let nref = ref (Node 0) in
+ let oref = ref (Opaque 0) in
+ let required = Hashtbl.create 0 in
+ let required_syms = Hashtbl.create 0 in
+ let ps =
+ make_parser tref nref oref sess get_mod
+ infer_lib_name required required_syms fname
+ in
+ with_err_handling sess
+ begin
+ fun _ ->
+ let apos = lexpos ps in
+ let items = Item.parse_mod_items ps EOF in
+ let bpos = lexpos ps in
+ let files = Hashtbl.create 0 in
+ let main =
+ if ps.pstate_sess.Session.sess_library_mode
+ then None
+ else Some (find_main_fn ps (snd items))
+ in
+ let crate = { Ast.crate_items = items;
+ Ast.crate_required = required;
+ Ast.crate_required_syms = required_syms;
+ Ast.crate_auth = Hashtbl.create 0;
+ Ast.crate_meta = [||];
+ Ast.crate_main = main;
+ Ast.crate_files = files }
+ in
+ let cratei = span ps apos bpos crate in
+ htab_put files cratei.id fname;
+ cratei
+ end
+;;
+
+
+(*
+ * 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/item.ml b/src/boot/fe/item.ml
new file mode 100644
index 00000000..75f86a58
--- /dev/null
+++ b/src/boot/fe/item.ml
@@ -0,0 +1,1139 @@
+
+open Common;;
+open Token;;
+open Parser;;
+
+(* Item grammar. *)
+
+let default_exports =
+ let e = Hashtbl.create 0 in
+ Hashtbl.add e Ast.EXPORT_all_decls ();
+ e
+;;
+
+let empty_view = { Ast.view_imports = Hashtbl.create 0;
+ Ast.view_exports = default_exports }
+;;
+
+let rec parse_expr (ps:pstate) : (Ast.stmt array * Ast.expr) =
+ let pexp = ctxt "expr" Pexp.parse_pexp ps in
+ Pexp.desugar_expr ps pexp
+
+and parse_expr_atom (ps:pstate) : (Ast.stmt array * Ast.atom) =
+ let pexp = ctxt "expr" Pexp.parse_pexp ps in
+ Pexp.desugar_expr_atom ps pexp
+
+and parse_expr_atom_list
+ (bra:token)
+ (ket:token)
+ (ps:pstate)
+ : (Ast.stmt array * Ast.atom array) =
+ arj1st (bracketed_zero_or_more bra ket (Some COMMA)
+ (ctxt "expr-atom list" parse_expr_atom) ps)
+
+and parse_expr_init (lv:Ast.lval) (ps:pstate) : (Ast.stmt array) =
+ let pexp = ctxt "expr" Pexp.parse_pexp ps in
+ Pexp.desugar_expr_init ps lv pexp
+
+and parse_lval (ps:pstate) : (Ast.stmt array * Ast.lval) =
+ let pexp = Pexp.parse_pexp ps in
+ Pexp.desugar_lval ps pexp
+
+and parse_identified_slot_and_ident
+ (aliases_ok:bool)
+ (ps:pstate)
+ : (Ast.slot identified * Ast.ident) =
+ let slot =
+ ctxt "identified slot and ident: slot"
+ (Pexp.parse_identified_slot aliases_ok) ps
+ in
+ let ident =
+ ctxt "identified slot and ident: ident" Pexp.parse_ident ps
+ in
+ (slot, ident)
+
+and parse_zero_or_more_identified_slot_ident_pairs
+ (aliases_ok:bool)
+ (ps:pstate)
+ : (((Ast.slot identified) * Ast.ident) array) =
+ ctxt "zero+ slots and idents"
+ (paren_comma_list
+ (parse_identified_slot_and_ident aliases_ok)) ps
+
+and parse_block (ps:pstate) : Ast.block =
+ let apos = lexpos ps in
+ let stmts =
+ arj (ctxt "block: stmts"
+ (bracketed_zero_or_more LBRACE RBRACE
+ None parse_stmts) ps)
+ in
+ let bpos = lexpos ps in
+ span ps apos bpos stmts
+
+and parse_block_stmt (ps:pstate) : Ast.stmt =
+ let apos = lexpos ps in
+ let block = parse_block ps in
+ let bpos = lexpos ps in
+ span ps apos bpos (Ast.STMT_block block)
+
+and parse_init
+ (lval:Ast.lval)
+ (ps:pstate)
+ : Ast.stmt array =
+ let apos = lexpos ps in
+ let stmts =
+ match peek ps with
+ EQ ->
+ bump ps;
+ parse_expr_init lval ps
+ | LARROW ->
+ bump ps;
+ let (stmts, rhs) = ctxt "init: port" parse_lval ps in
+ let bpos = lexpos ps in
+ let stmt = Ast.STMT_recv (lval, rhs) in
+ Array.append stmts [| (span ps apos bpos stmt) |]
+ | _ -> arr []
+ in
+ let _ = expect ps SEMI in
+ stmts
+
+and parse_slot_and_ident_and_init
+ (ps:pstate)
+ : (Ast.stmt array * Ast.slot * Ast.ident) =
+ let apos = lexpos ps in
+ let (slot, ident) =
+ ctxt "slot, ident and init: slot and ident"
+ (Pexp.parse_slot_and_ident false) ps
+ in
+ let bpos = lexpos ps in
+ let lval = Ast.LVAL_base (span ps apos bpos (Ast.BASE_ident ident)) in
+ let stmts = ctxt "slot, ident and init: init" (parse_init lval) ps in
+ (stmts, slot, ident)
+
+and parse_auto_slot_and_init
+ (ps:pstate)
+ : (Ast.stmt array * Ast.slot * Ast.ident) =
+ let apos = lexpos ps in
+ let ident = Pexp.parse_ident ps in
+ let bpos = lexpos ps in
+ let lval = Ast.LVAL_base (span ps apos bpos (Ast.BASE_ident ident)) in
+ let stmts = ctxt "slot, ident and init: init" (parse_init lval) ps in
+ (stmts, slot_auto, ident)
+
+(*
+ * We have no way to parse a single Ast.stmt; any incoming syntactic statement
+ * may desugar to N>1 real Ast.stmts
+ *)
+
+and parse_stmts (ps:pstate) : Ast.stmt array =
+ let apos = lexpos ps in
+ match peek ps with
+
+ LOG ->
+ bump ps;
+ let (stmts, atom) = ctxt "stmts: log value" parse_expr_atom ps in
+ expect ps SEMI;
+ spans ps stmts apos (Ast.STMT_log atom)
+
+ | CHECK ->
+ bump ps;
+ begin
+
+ let rec name_to_lval (bpos:pos) (name:Ast.name)
+ : Ast.lval =
+ match name with
+ Ast.NAME_base nb ->
+ Ast.LVAL_base (span ps apos bpos nb)
+ | Ast.NAME_ext (n, nc) ->
+ Ast.LVAL_ext (name_to_lval bpos n, Ast.COMP_named nc)
+ in
+
+ let rec carg_path_to_lval (bpos:pos) (path:Ast.carg_path)
+ : Ast.lval =
+ match path with
+ Ast.CARG_base Ast.BASE_formal ->
+ raise (err "converting formal constraint-arg to atom" ps)
+ | Ast.CARG_base (Ast.BASE_named nb) ->
+ Ast.LVAL_base (span ps apos bpos nb)
+ | Ast.CARG_ext (pth, nc) ->
+ Ast.LVAL_ext (carg_path_to_lval bpos pth,
+ Ast.COMP_named nc)
+ in
+
+ let carg_to_atom (bpos:pos) (carg:Ast.carg)
+ : Ast.atom =
+ match carg with
+ Ast.CARG_lit lit ->
+ Ast.ATOM_literal (span ps apos bpos lit)
+ | Ast.CARG_path pth ->
+ Ast.ATOM_lval (carg_path_to_lval bpos pth)
+ in
+
+ let synthesise_check_call (bpos:pos) (constr:Ast.constr)
+ : (Ast.lval * (Ast.atom array)) =
+ let lval = name_to_lval bpos constr.Ast.constr_name in
+ let args =
+ Array.map (carg_to_atom bpos) constr.Ast.constr_args
+ in
+ (lval, args)
+ in
+
+ let synthesise_check_calls (bpos:pos) (constrs:Ast.constrs)
+ : Ast.check_calls =
+ Array.map (synthesise_check_call bpos) constrs
+ in
+
+ match peek ps with
+ LPAREN ->
+ bump ps;
+ let (stmts, expr) =
+ ctxt "stmts: check value" parse_expr ps
+ in
+ expect ps RPAREN;
+ expect ps SEMI;
+ spans ps stmts apos (Ast.STMT_check_expr expr)
+
+ | IF ->
+ bump ps;
+ expect ps LPAREN;
+ let constrs = Pexp.parse_constrs ps in
+ expect ps RPAREN;
+ let block = parse_block ps in
+ let bpos = lexpos ps in
+ let calls = synthesise_check_calls bpos constrs in
+ [| span ps apos bpos
+ (Ast.STMT_check_if (constrs, calls, block))
+ |]
+
+ | _ ->
+ let constrs = Pexp.parse_constrs ps in
+ expect ps SEMI;
+ let bpos = lexpos ps in
+ let calls = synthesise_check_calls bpos constrs in
+ [| span ps apos bpos
+ (Ast.STMT_check (constrs, calls))
+ |]
+ end
+
+ | ALT ->
+ bump ps;
+ begin
+ match peek ps with
+ TYPE -> [| |]
+ | LPAREN ->
+ let (stmts, lval) = bracketed LPAREN RPAREN parse_lval ps in
+ let rec parse_pat ps =
+ match peek ps with
+ IDENT ident ->
+ let apos = lexpos ps in
+ bump ps;
+ let bpos = lexpos ps in
+
+ (* TODO: nullary constructors *)
+ if peek ps != LPAREN then
+ let slot =
+ { Ast.slot_mode = Ast.MODE_interior;
+ Ast.slot_mutable = false;
+ Ast.slot_ty = None }
+ in
+ Ast.PAT_slot ((span ps apos bpos slot), ident)
+ else
+ let pats =
+ paren_comma_list parse_pat ps
+ in
+ Ast.PAT_tag (ident, pats)
+ | LIT_INT _ | LIT_CHAR _ | LIT_BOOL _ ->
+ Ast.PAT_lit (Pexp.parse_lit ps)
+ | UNDERSCORE -> bump ps; Ast.PAT_wild
+ | tok -> raise (Parse_err (ps,
+ "Expected pattern but found '" ^
+ (string_of_tok tok) ^ "'"))
+ in
+ let rec parse_arms ps =
+ match peek ps with
+ CASE ->
+ bump ps;
+ let pat = bracketed LPAREN RPAREN parse_pat ps in
+ let block = parse_block ps in
+ let arm = (pat, block) in
+ (span ps apos (lexpos ps) arm)::(parse_arms ps)
+ | _ -> []
+ in
+ let parse_alt_block ps =
+ let arms = ctxt "alt tag arms" parse_arms ps in
+ spans ps stmts apos begin
+ Ast.STMT_alt_tag {
+ Ast.alt_tag_lval = lval;
+ Ast.alt_tag_arms = Array.of_list arms
+ }
+ end
+ in
+ bracketed LBRACE RBRACE parse_alt_block ps
+ | _ -> [| |]
+ end
+
+ | IF ->
+ let final_else = ref None in
+ let rec parse_stmt_if _ =
+ bump ps;
+ let (stmts, expr) =
+ ctxt "stmts: if cond"
+ (bracketed LPAREN RPAREN parse_expr) ps
+ in
+ let then_block = ctxt "stmts: if-then" parse_block ps in
+ begin
+ match peek ps with
+ ELSE ->
+ begin
+ bump ps;
+ match peek ps with
+ IF ->
+ let nested_if = parse_stmt_if () in
+ let bpos = lexpos ps in
+ final_else :=
+ Some (span ps apos bpos nested_if)
+ | _ ->
+ final_else :=
+ Some (ctxt "stmts: if-else" parse_block ps)
+ end
+ | _ -> ()
+ end;
+ let res =
+ spans ps stmts apos
+ (Ast.STMT_if
+ { Ast.if_test = expr;
+ Ast.if_then = then_block;
+ Ast.if_else = !final_else; })
+ in
+ final_else := None;
+ res
+ in
+ parse_stmt_if()
+
+ | FOR ->
+ bump ps;
+ begin
+ match peek ps with
+ EACH ->
+ bump ps;
+ let inner ps : ((Ast.slot identified * Ast.ident)
+ * Ast.stmt array
+ * (Ast.lval * Ast.atom array)) =
+ let slot = (parse_identified_slot_and_ident true ps) in
+ let _ = (expect ps IN) in
+ let (stmts1, iter) = (rstr true parse_lval) ps in
+ let (stmts2, args) =
+ parse_expr_atom_list LPAREN RPAREN ps
+ in
+ (slot, Array.append stmts1 stmts2, (iter, args))
+ in
+ let (slot, stmts, call) = ctxt "stmts: foreach head"
+ (bracketed LPAREN RPAREN inner) ps
+ in
+ let body_block =
+ ctxt "stmts: foreach body" parse_block ps
+ in
+ let bpos = lexpos ps in
+ let head_block =
+ (*
+ * Slightly weird, but we put an extra nesting level of
+ * block here to separate the part that lives in our frame
+ * (the iter slot) from the part that lives in the callee
+ * frame (the body block).
+ *)
+ span ps apos bpos [|
+ span ps apos bpos (Ast.STMT_block body_block);
+ |]
+ in
+ Array.append stmts
+ [| span ps apos bpos
+ (Ast.STMT_for_each
+ { Ast.for_each_slot = slot;
+ Ast.for_each_call = call;
+ Ast.for_each_head = head_block;
+ Ast.for_each_body = body_block; }) |]
+ | _ ->
+ let inner ps =
+ let slot = (parse_identified_slot_and_ident false ps) in
+ let _ = (expect ps IN) in
+ let lval = (parse_lval ps) in
+ (slot, lval) in
+ let (slot, seq) =
+ ctxt "stmts: for head" (bracketed LPAREN RPAREN inner) ps
+ in
+ let body_block = ctxt "stmts: for body" parse_block ps in
+ let bpos = lexpos ps in
+ [| span ps apos bpos
+ (Ast.STMT_for
+ { Ast.for_slot = slot;
+ Ast.for_seq = seq;
+ Ast.for_body = body_block; }) |]
+ end
+
+ | WHILE ->
+ bump ps;
+ let (stmts, test) =
+ ctxt "stmts: while cond" (bracketed LPAREN RPAREN parse_expr) ps
+ in
+ let body_block = ctxt "stmts: while body" parse_block ps in
+ let bpos = lexpos ps in
+ [| span ps apos bpos
+ (Ast.STMT_while
+ { Ast.while_lval = (stmts, test);
+ Ast.while_body = body_block; }) |]
+
+ | PUT ->
+ begin
+ bump ps;
+ match peek ps with
+ EACH ->
+ bump ps;
+ let (lstmts, lval) =
+ ctxt "put each: lval" (rstr true parse_lval) ps
+ in
+ let (astmts, args) =
+ ctxt "put each: args"
+ (parse_expr_atom_list LPAREN RPAREN) ps
+ in
+ let bpos = lexpos ps in
+ let be =
+ span ps apos bpos (Ast.STMT_put_each (lval, args))
+ in
+ expect ps SEMI;
+ Array.concat [ lstmts; astmts; [| be |] ]
+
+ | _ ->
+ begin
+ let (stmts, e) =
+ match peek ps with
+ SEMI -> (arr [], None)
+ | _ ->
+ let (stmts, expr) =
+ ctxt "stmts: put expr" parse_expr_atom ps
+ in
+ expect ps SEMI;
+ (stmts, Some expr)
+ in
+ spans ps stmts apos (Ast.STMT_put e)
+ end
+ end
+
+ | RET ->
+ bump ps;
+ let (stmts, e) =
+ match peek ps with
+ SEMI -> (bump ps; (arr [], None))
+ | _ ->
+ let (stmts, expr) =
+ ctxt "stmts: ret expr" parse_expr_atom ps
+ in
+ expect ps SEMI;
+ (stmts, Some expr)
+ in
+ spans ps stmts apos (Ast.STMT_ret e)
+
+ | BE ->
+ bump ps;
+ let (lstmts, lval) = ctxt "be: lval" (rstr true parse_lval) ps in
+ let (astmts, args) =
+ ctxt "be: args" (parse_expr_atom_list LPAREN RPAREN) ps
+ in
+ let bpos = lexpos ps in
+ let be = span ps apos bpos (Ast.STMT_be (lval, args)) in
+ expect ps SEMI;
+ Array.concat [ lstmts; astmts; [| be |] ]
+
+ | LBRACE -> [| ctxt "stmts: block" parse_block_stmt ps |]
+
+ | LET ->
+ bump ps;
+ let (stmts, slot, ident) =
+ ctxt "stmt slot" parse_slot_and_ident_and_init ps in
+ let slot = Pexp.apply_mutability slot true in
+ let bpos = lexpos ps in
+ let decl = Ast.DECL_slot (Ast.KEY_ident ident,
+ (span ps apos bpos slot))
+ in
+ Array.concat [[| span ps apos bpos (Ast.STMT_decl decl) |]; stmts]
+
+ | AUTO ->
+ bump ps;
+ let (stmts, slot, ident) =
+ ctxt "stmt slot" parse_auto_slot_and_init ps in
+ let slot = Pexp.apply_mutability slot true in
+ let bpos = lexpos ps in
+ let decl = Ast.DECL_slot (Ast.KEY_ident ident,
+ (span ps apos bpos slot))
+ in
+ Array.concat [[| span ps apos bpos (Ast.STMT_decl decl) |]; stmts]
+
+ | YIELD ->
+ bump ps;
+ expect ps SEMI;
+ let bpos = lexpos ps in
+ [| span ps apos bpos Ast.STMT_yield |]
+
+ | FAIL ->
+ bump ps;
+ expect ps SEMI;
+ let bpos = lexpos ps in
+ [| span ps apos bpos Ast.STMT_fail |]
+
+ | JOIN ->
+ bump ps;
+ let (stmts, lval) = ctxt "stmts: task expr" parse_lval ps in
+ expect ps SEMI;
+ spans ps stmts apos (Ast.STMT_join lval)
+
+ | MOD | OBJ | TYPE | FN | USE | NATIVE ->
+ let (ident, item) = ctxt "stmt: decl" parse_mod_item ps in
+ let decl = Ast.DECL_mod_item (ident, item) in
+ let stmts = expand_tags_to_stmts ps item in
+ spans ps stmts apos (Ast.STMT_decl decl)
+
+ | _ ->
+ let (lstmts, lval) = ctxt "stmt: lval" parse_lval ps in
+ begin
+ match peek ps with
+
+ SEMI -> (bump ps; lstmts)
+
+ | EQ -> parse_init lval ps
+
+ | OPEQ binop_token ->
+ bump ps;
+ let (stmts, rhs) =
+ ctxt "stmt: opeq rhs" parse_expr_atom ps
+ in
+ let binop =
+ match binop_token with
+ PLUS -> Ast.BINOP_add
+ | MINUS -> Ast.BINOP_sub
+ | STAR -> Ast.BINOP_mul
+ | SLASH -> Ast.BINOP_div
+ | PERCENT -> Ast.BINOP_mod
+ | AND -> Ast.BINOP_and
+ | OR -> Ast.BINOP_or
+ | CARET -> Ast.BINOP_xor
+ | LSL -> Ast.BINOP_lsl
+ | LSR -> Ast.BINOP_lsr
+ | ASR -> Ast.BINOP_asr
+ | _ -> raise (err "unknown opeq token" ps)
+ in
+ expect ps SEMI;
+ spans ps stmts apos
+ (Ast.STMT_copy_binop (lval, binop, rhs))
+
+ | LARROW ->
+ bump ps;
+ let (stmts, rhs) = ctxt "stmt: recv rhs" parse_lval ps in
+ let _ = expect ps SEMI in
+ spans ps stmts apos (Ast.STMT_recv (lval, rhs))
+
+ | SEND ->
+ bump ps;
+ let (stmts, rhs) =
+ ctxt "stmt: send rhs" parse_expr_atom ps
+ in
+ let _ = expect ps SEMI in
+ let bpos = lexpos ps in
+ let (src, copy) = match rhs with
+ Ast.ATOM_lval lv -> (lv, [| |])
+ | _ ->
+ let (_, tmp, tempdecl) =
+ build_tmp ps slot_auto apos bpos
+ in
+ let copy = span ps apos bpos
+ (Ast.STMT_copy (tmp, Ast.EXPR_atom rhs)) in
+ ((clone_lval ps tmp), [| tempdecl; copy |])
+ in
+ let send =
+ span ps apos bpos
+ (Ast.STMT_send (lval, src))
+ in
+ Array.concat [ stmts; copy; [| send |] ]
+
+ | _ -> raise (unexpected ps)
+ end
+
+
+and parse_ty_param (iref:int ref) (ps:pstate) : Ast.ty_param identified =
+ let apos = lexpos ps in
+ let e = Pexp.parse_effect ps in
+ let ident = Pexp.parse_ident ps in
+ let i = !iref in
+ let bpos = lexpos ps in
+ incr iref;
+ span ps apos bpos (ident, (i, e))
+
+and parse_ty_params (ps:pstate)
+ : (Ast.ty_param identified) array =
+ match peek ps with
+ LBRACKET ->
+ bracketed_zero_or_more LBRACKET RBRACKET (Some COMMA)
+ (parse_ty_param (ref 0)) ps
+ | _ -> arr []
+
+and parse_ident_and_params (ps:pstate) (cstr:string)
+ : (Ast.ident * (Ast.ty_param identified) array) =
+ let ident = ctxt ("mod " ^ cstr ^ " item: ident") Pexp.parse_ident ps in
+ let params =
+ ctxt ("mod " ^ cstr ^ " item: type params") parse_ty_params ps
+ in
+ (ident, params)
+
+and parse_inputs
+ (ps:pstate)
+ : ((Ast.slot identified * Ast.ident) array * Ast.constrs) =
+ let slots =
+ match peek ps with
+ LPAREN -> ctxt "inputs: input idents and slots"
+ (parse_zero_or_more_identified_slot_ident_pairs true) ps
+ | _ -> raise (unexpected ps)
+ in
+ let constrs =
+ match peek ps with
+ COLON -> (bump ps; ctxt "inputs: constrs" Pexp.parse_constrs ps)
+ | _ -> [| |]
+ in
+ let rec rewrite_carg_path cp =
+ match cp with
+ Ast.CARG_base (Ast.BASE_named (Ast.BASE_ident ident)) ->
+ begin
+ let res = ref cp in
+ for i = 0 to (Array.length slots) - 1
+ do
+ let (_, ident') = slots.(i) in
+ if ident' = ident
+ then res := Ast.CARG_ext (Ast.CARG_base Ast.BASE_formal,
+ Ast.COMP_idx i)
+ else ()
+ done;
+ !res
+ end
+ | Ast.CARG_base _ -> cp
+ | Ast.CARG_ext (cp, ext) ->
+ Ast.CARG_ext (rewrite_carg_path cp, ext)
+ in
+ (* Rewrite constrs with input tuple as BASE_formal. *)
+ Array.iter
+ begin
+ fun constr ->
+ let args = constr.Ast.constr_args in
+ Array.iteri
+ begin
+ fun i carg ->
+ match carg with
+ Ast.CARG_path cp ->
+ args.(i) <- Ast.CARG_path (rewrite_carg_path cp)
+ | _ -> ()
+ end
+ args
+ end
+ constrs;
+ (slots, constrs)
+
+
+and parse_in_and_out
+ (ps:pstate)
+ : ((Ast.slot identified * Ast.ident) array
+ * Ast.constrs
+ * Ast.slot identified) =
+ let (inputs, constrs) = parse_inputs ps in
+ let output =
+ match peek ps with
+ RARROW ->
+ bump ps;
+ ctxt "fn in and out: output slot"
+ (Pexp.parse_identified_slot true) ps
+ | _ ->
+ let apos = lexpos ps in
+ span ps apos apos slot_nil
+ in
+ (inputs, constrs, output)
+
+
+(* parse_fn starts at the first lparen of the sig. *)
+and parse_fn
+ (is_iter:bool)
+ (effect:Ast.effect)
+ (ps:pstate)
+ : Ast.fn =
+ let (inputs, constrs, output) =
+ ctxt "fn: in_and_out" parse_in_and_out ps
+ in
+ let body = ctxt "fn: body" parse_block ps in
+ { Ast.fn_input_slots = inputs;
+ Ast.fn_input_constrs = constrs;
+ Ast.fn_output_slot = output;
+ Ast.fn_aux = { Ast.fn_effect = effect;
+ Ast.fn_is_iter = is_iter; };
+ Ast.fn_body = body; }
+
+and parse_meta_input (ps:pstate) : (Ast.ident * string option) =
+ let lab = (ctxt "meta input: label" Pexp.parse_ident ps) in
+ match peek ps with
+ EQ ->
+ bump ps;
+ let v =
+ match peek ps with
+ UNDERSCORE -> bump ps; None
+ | LIT_STR s -> bump ps; Some s
+ | _ -> raise (unexpected ps)
+ in
+ (lab, v)
+ | _ -> raise (unexpected ps)
+
+and parse_meta_pat (ps:pstate) : Ast.meta_pat =
+ bracketed_zero_or_more LPAREN RPAREN
+ (Some COMMA) parse_meta_input ps
+
+and parse_meta (ps:pstate) : Ast.meta =
+ Array.map
+ begin
+ fun (id,v) ->
+ match v with
+ None ->
+ raise (err ("wildcard found in meta "
+ ^ "pattern where value expected") ps)
+ | Some v -> (id,v)
+ end
+ (parse_meta_pat ps)
+
+and parse_optional_meta_pat (ps:pstate) (ident:Ast.ident) : Ast.meta_pat =
+ match peek ps with
+ LPAREN -> parse_meta_pat ps
+ | _ -> [| ("name", Some ident) |]
+
+
+and parse_obj_item
+ (ps:pstate)
+ (apos:pos)
+ (effect:Ast.effect)
+ : (Ast.ident * Ast.mod_item) =
+ expect ps OBJ;
+ let (ident, params) = parse_ident_and_params ps "obj" in
+ let (state, constrs) = (ctxt "obj state" parse_inputs ps) in
+ let drop = ref None in
+ expect ps LBRACE;
+ let fns = Hashtbl.create 0 in
+ while (not (peek ps = RBRACE))
+ do
+ let apos = lexpos ps in
+ match peek ps with
+ IO | STATE | UNSAFE | FN | ITER ->
+ let effect = Pexp.parse_effect ps in
+ let is_iter = (peek ps) = ITER in
+ bump ps;
+ let ident = ctxt "obj fn: ident" Pexp.parse_ident ps in
+ let fn = ctxt "obj fn: fn" (parse_fn is_iter effect) ps in
+ let bpos = lexpos ps in
+ htab_put fns ident (span ps apos bpos fn)
+ | DROP ->
+ bump ps;
+ drop := Some (parse_block ps)
+ | RBRACE -> ()
+ | _ -> raise (unexpected ps)
+ done;
+ expect ps RBRACE;
+ let bpos = lexpos ps in
+ let obj = { Ast.obj_state = state;
+ Ast.obj_effect = effect;
+ Ast.obj_constrs = constrs;
+ Ast.obj_fns = fns;
+ Ast.obj_drop = !drop }
+ in
+ (ident,
+ span ps apos bpos
+ (decl params (Ast.MOD_ITEM_obj obj)))
+
+
+and parse_mod_item (ps:pstate) : (Ast.ident * Ast.mod_item) =
+ let apos = lexpos ps in
+ let parse_lib_name ident =
+ match peek ps with
+ EQ ->
+ begin
+ bump ps;
+ match peek ps with
+ LIT_STR s -> (bump ps; s)
+ | _ -> raise (unexpected ps)
+ end
+ | _ -> ps.pstate_infer_lib_name ident
+ in
+
+ match peek ps with
+
+ IO | STATE | UNSAFE | OBJ | FN | ITER ->
+ let effect = Pexp.parse_effect ps in
+ begin
+ match peek ps with
+ OBJ -> parse_obj_item ps apos effect
+ | _ ->
+ let is_iter = (peek ps) = ITER in
+ bump ps;
+ let (ident, params) = parse_ident_and_params ps "fn" in
+ let fn =
+ ctxt "mod fn item: fn" (parse_fn is_iter effect) ps
+ in
+ let bpos = lexpos ps in
+ (ident,
+ span ps apos bpos
+ (decl params (Ast.MOD_ITEM_fn fn)))
+ end
+
+ | TYPE ->
+ bump ps;
+ let (ident, params) = parse_ident_and_params ps "type" in
+ let _ = expect ps EQ in
+ let ty = ctxt "mod type item: ty" Pexp.parse_ty ps in
+ let _ = expect ps SEMI in
+ let bpos = lexpos ps in
+ let item = Ast.MOD_ITEM_type ty in
+ (ident, span ps apos bpos (decl params item))
+
+ | MOD ->
+ bump ps;
+ let (ident, params) = parse_ident_and_params ps "mod" in
+ expect ps LBRACE;
+ let items = parse_mod_items ps RBRACE in
+ let bpos = lexpos ps in
+ (ident,
+ span ps apos bpos
+ (decl params (Ast.MOD_ITEM_mod items)))
+
+ | NATIVE ->
+ begin
+ bump ps;
+ let conv =
+ match peek ps with
+ LIT_STR s ->
+ bump ps;
+ begin
+ match string_to_conv s with
+ None -> raise (unexpected ps)
+ | Some c -> c
+ end
+ | _ -> CONV_cdecl
+ in
+ expect ps MOD;
+ let (ident, params) = parse_ident_and_params ps "native mod" 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 = span ps apos bpos item in
+ note_required_mod ps {lo=apos; hi=bpos} conv rlib item;
+ (ident, item)
+ end
+
+ | USE ->
+ begin
+ bump ps;
+ let ident = ctxt "use mod: ident" Pexp.parse_ident ps in
+ let meta =
+ ctxt "use mod: meta" parse_optional_meta_pat ps ident
+ in
+ let bpos = lexpos ps in
+ let id = (span ps apos bpos ()).id in
+ let (path, items) =
+ ps.pstate_get_mod meta id ps.pstate_node_id ps.pstate_opaque_id
+ in
+ let bpos = lexpos ps in
+ expect ps SEMI;
+ let rlib =
+ REQUIRED_LIB_rust { required_libname = path;
+ required_prefix = ps.pstate_depth }
+ in
+ iflog ps
+ begin
+ fun _ ->
+ log ps "extracted mod from %s (binding to %s)"
+ path ident;
+ log ps "%a" Ast.sprintf_mod_items items;
+ end;
+ let item = decl [||] (Ast.MOD_ITEM_mod (empty_view, items)) in
+ let item = span ps apos bpos item in
+ note_required_mod ps {lo=apos; hi=bpos} CONV_rust rlib item;
+ (ident, item)
+ end
+
+
+
+ | _ -> raise (unexpected ps)
+
+
+and parse_mod_items_from_signature
+ (ps:pstate)
+ : (Ast.mod_view * Ast.mod_items) =
+ let mis = Hashtbl.create 0 in
+ expect ps LBRACE;
+ while not (peek ps = RBRACE)
+ do
+ let (ident, mti) = ctxt "mod items from sig: mod item"
+ parse_mod_item_from_signature ps
+ in
+ Hashtbl.add mis ident mti;
+ done;
+ expect ps RBRACE;
+ (empty_view, mis)
+
+
+and parse_mod_item_from_signature (ps:pstate)
+ : (Ast.ident * Ast.mod_item) =
+ let apos = lexpos ps in
+ match peek ps with
+ MOD ->
+ bump ps;
+ let (ident, params) = parse_ident_and_params ps "mod signature" in
+ let items = parse_mod_items_from_signature ps in
+ let bpos = lexpos ps in
+ (ident, span ps apos bpos (decl params (Ast.MOD_ITEM_mod items)))
+
+ | IO | STATE | UNSAFE | FN | ITER ->
+ let effect = Pexp.parse_effect ps in
+ let is_iter = (peek ps) = ITER in
+ bump ps;
+ let (ident, params) = parse_ident_and_params ps "fn signature" in
+ let (inputs, constrs, output) = parse_in_and_out ps in
+ let bpos = lexpos ps in
+ let body = span ps apos bpos [| |] in
+ let fn =
+ Ast.MOD_ITEM_fn
+ { Ast.fn_input_slots = inputs;
+ Ast.fn_input_constrs = constrs;
+ Ast.fn_output_slot = output;
+ Ast.fn_aux = { Ast.fn_effect = effect;
+ Ast.fn_is_iter = is_iter; };
+ Ast.fn_body = body; }
+ in
+ let node = span ps apos bpos (decl params fn) in
+ begin
+ match peek ps with
+ EQ ->
+ bump ps;
+ begin
+ match peek ps with
+ LIT_STR s ->
+ bump ps;
+ htab_put ps.pstate_required_syms node.id s
+ | _ -> raise (unexpected ps)
+ end;
+ | _ -> ()
+ end;
+ expect ps SEMI;
+ (ident, node)
+
+ | TYPE ->
+ bump ps;
+ let (ident, params) = parse_ident_and_params ps "type type" in
+ let t =
+ match peek ps with
+ SEMI -> Ast.TY_native (next_opaque_id ps)
+ | _ -> Pexp.parse_ty ps
+ in
+ expect ps SEMI;
+ let bpos = lexpos ps in
+ (ident, span ps apos bpos (decl params (Ast.MOD_ITEM_type t)))
+
+ (* FIXME: parse obj. *)
+ | _ -> raise (unexpected ps)
+
+
+and expand_tags
+ (ps:pstate)
+ (item:Ast.mod_item)
+ : (Ast.ident * Ast.mod_item) array =
+ let handle_ty_tag id ttag =
+ let tags = ref [] in
+ Hashtbl.iter
+ begin
+ fun name tup ->
+ let ident = match name with
+ Ast.NAME_base (Ast.BASE_ident ident) -> ident
+ | _ ->
+ raise (Parse_err
+ (ps, "unexpected name type while expanding tag"))
+ in
+ let header =
+ Array.map (fun slot -> (clone_span ps item slot)) tup
+ in
+ let tag_item' = Ast.MOD_ITEM_tag (header, ttag, id) in
+ let cloned_params =
+ Array.map (fun p -> clone_span ps p p.node)
+ item.node.Ast.decl_params
+ in
+ let tag_item =
+ clone_span ps item (decl cloned_params tag_item')
+ in
+ tags := (ident, tag_item) :: (!tags)
+ end
+ ttag;
+ arr (!tags)
+ in
+ let handle_ty_decl id tyd =
+ match tyd with
+ Ast.TY_tag ttag -> handle_ty_tag id ttag
+ | _ -> [| |]
+ in
+ match item.node.Ast.decl_item with
+ Ast.MOD_ITEM_type tyd -> handle_ty_decl item.id tyd
+ | _ -> [| |]
+
+
+and expand_tags_to_stmts
+ (ps:pstate)
+ (item:Ast.mod_item)
+ : Ast.stmt array =
+ let id_items = expand_tags ps item in
+ Array.map
+ (fun (ident, tag_item) ->
+ clone_span ps item
+ (Ast.STMT_decl
+ (Ast.DECL_mod_item (ident, tag_item))))
+ id_items
+
+
+and expand_tags_to_items
+ (ps:pstate)
+ (item:Ast.mod_item)
+ (items:Ast.mod_items)
+ : unit =
+ let id_items = expand_tags ps item in
+ Array.iter
+ (fun (ident, item) -> htab_put items ident item)
+ id_items
+
+
+and note_required_mod
+ (ps:pstate)
+ (sp:span)
+ (conv:nabi_conv)
+ (rlib:required_lib)
+ (item:Ast.mod_item)
+ : unit =
+ iflog ps
+ begin
+ fun _ -> log ps "marking item #%d as required" (int_of_node item.id)
+ end;
+ htab_put ps.pstate_required item.id (rlib, conv);
+ if not (Hashtbl.mem ps.pstate_sess.Session.sess_spans item.id)
+ then Hashtbl.add ps.pstate_sess.Session.sess_spans item.id sp;
+ match item.node.Ast.decl_item with
+ Ast.MOD_ITEM_mod (_, items) ->
+ Hashtbl.iter
+ begin
+ fun _ sub ->
+ note_required_mod ps sp conv rlib sub
+ end
+ items
+ | _ -> ()
+
+
+and parse_import
+ (ps:pstate)
+ (imports:(Ast.ident, Ast.name) Hashtbl.t)
+ : unit =
+ let import a n =
+ let a = match a with
+ None ->
+ begin
+ match n with
+ Ast.NAME_ext (_, Ast.COMP_ident i)
+ | Ast.NAME_ext (_, Ast.COMP_app (i, _))
+ | Ast.NAME_base (Ast.BASE_ident i)
+ | Ast.NAME_base (Ast.BASE_app (i, _)) -> i
+ | _ -> raise (Parse_err (ps, "bad import specification"))
+ end
+ | Some i -> i
+ in
+ Hashtbl.add imports a n
+ in
+ match peek ps with
+ IDENT i ->
+ begin
+ bump ps;
+ match peek ps with
+ EQ ->
+ (*
+ * import x = ...
+ *)
+ bump ps;
+ import (Some i) (Pexp.parse_name ps)
+ | _ ->
+ (*
+ * import x...
+ *)
+ import None (Pexp.parse_name_ext ps
+ (Ast.NAME_base
+ (Ast.BASE_ident i)))
+ end
+ | _ ->
+ import None (Pexp.parse_name ps)
+
+
+and parse_export
+ (ps:pstate)
+ (exports:(Ast.export, unit) Hashtbl.t)
+ : unit =
+ let e =
+ match peek ps with
+ STAR -> bump ps; Ast.EXPORT_all_decls
+ | IDENT i -> bump ps; Ast.EXPORT_ident i
+ | _ -> raise (unexpected ps)
+ in
+ Hashtbl.add exports e ()
+
+
+and parse_mod_items
+ (ps:pstate)
+ (terminal:token)
+ : (Ast.mod_view * Ast.mod_items) =
+ ps.pstate_depth <- ps.pstate_depth + 1;
+ let imports = Hashtbl.create 0 in
+ let exports = Hashtbl.create 0 in
+ let in_view = ref true in
+ let items = Hashtbl.create 4 in
+ while (not (peek ps = terminal))
+ do
+ if !in_view
+ then
+ match peek ps with
+ IMPORT ->
+ bump ps;
+ parse_import ps imports;
+ expect ps SEMI;
+ | EXPORT ->
+ bump ps;
+ parse_export ps exports;
+ expect ps SEMI;
+ | _ ->
+ in_view := false
+ else
+ let (ident, item) = parse_mod_item ps in
+ htab_put items ident item;
+ expand_tags_to_items ps item items;
+ done;
+ if (Hashtbl.length exports) = 0
+ then Hashtbl.add exports Ast.EXPORT_all_decls ();
+ expect ps terminal;
+ ps.pstate_depth <- ps.pstate_depth - 1;
+ let view = { Ast.view_imports = imports;
+ Ast.view_exports = exports }
+ in
+ (view, items)
+;;
+
+
+
+(*
+ * 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/lexer.mll b/src/boot/fe/lexer.mll
new file mode 100644
index 00000000..fb4d58c5
--- /dev/null
+++ b/src/boot/fe/lexer.mll
@@ -0,0 +1,362 @@
+
+
+{
+
+ open Token;;
+ open Common;;
+
+ exception Lex_err of (string * Common.pos);;
+
+ let fail lexbuf s =
+ let p = lexbuf.Lexing.lex_start_p in
+ let pos =
+ (p.Lexing.pos_fname,
+ p.Lexing.pos_lnum ,
+ (p.Lexing.pos_cnum) - (p.Lexing.pos_bol))
+ in
+ raise (Lex_err (s, pos))
+ ;;
+
+ let bump_line p = { p with
+ Lexing.pos_lnum = p.Lexing.pos_lnum + 1;
+ Lexing.pos_bol = p.Lexing.pos_cnum }
+ ;;
+
+ let keyword_table = Hashtbl.create 100
+ let _ =
+ List.iter (fun (kwd, tok) -> Common.htab_put keyword_table kwd tok)
+ [ ("mod", MOD);
+ ("use", USE);
+ ("meta", META);
+ ("auth", AUTH);
+
+ ("syntax", SYNTAX);
+
+ ("if", IF);
+ ("else", ELSE);
+ ("while", WHILE);
+ ("do", DO);
+ ("alt", ALT);
+ ("case", CASE);
+
+ ("for", FOR);
+ ("each", EACH);
+ ("put", PUT);
+ ("ret", RET);
+ ("be", BE);
+
+ ("fail", FAIL);
+ ("drop", DROP);
+
+ ("type", TYPE);
+ ("check", CHECK);
+ ("claim", CLAIM);
+ ("prove", PROVE);
+
+ ("io", IO);
+ ("state", STATE);
+ ("unsafe", UNSAFE);
+
+ ("native", NATIVE);
+ ("mutable", MUTABLE);
+ ("auto", AUTO);
+
+ ("fn", FN);
+ ("iter", ITER);
+
+ ("import", IMPORT);
+ ("export", EXPORT);
+
+ ("let", LET);
+
+ ("log", LOG);
+ ("spawn", SPAWN);
+ ("thread", THREAD);
+ ("yield", YIELD);
+ ("join", JOIN);
+
+ ("bool", BOOL);
+
+ ("int", INT);
+ ("uint", UINT);
+
+ ("char", CHAR);
+ ("str", STR);
+
+ ("rec", REC);
+ ("tup", TUP);
+ ("tag", TAG);
+ ("vec", VEC);
+ ("any", ANY);
+
+ ("obj", OBJ);
+
+ ("port", PORT);
+ ("chan", CHAN);
+
+ ("task", TASK);
+
+ ("true", LIT_BOOL true);
+ ("false", LIT_BOOL false);
+
+ ("in", IN);
+
+ ("as", AS);
+ ("with", WITH);
+
+ ("bind", BIND);
+
+ ("u8", MACH TY_u8);
+ ("u16", MACH TY_u16);
+ ("u32", MACH TY_u32);
+ ("u64", MACH TY_u64);
+ ("i8", MACH TY_i8);
+ ("i16", MACH TY_i16);
+ ("i32", MACH TY_i32);
+ ("i64", MACH TY_i64);
+ ("f32", MACH TY_f32);
+ ("f64", MACH TY_f64)
+ ]
+;;
+}
+
+let hexdig = ['0'-'9' 'a'-'f' 'A'-'F']
+let bin = "0b" ['0' '1']['0' '1' '_']*
+let hex = "0x" hexdig ['0'-'9' 'a'-'f' 'A'-'F' '_']*
+let dec = ['0'-'9']+
+let exp = ['e''E']['-''+']? dec
+let flo = (dec '.' dec (exp?)) | (dec exp)
+
+let ws = [ ' ' '\t' '\r' ]
+
+let id = ['a'-'z' 'A'-'Z' '_']['a'-'z' 'A'-'Z' '0'-'9' '_']*
+
+rule token = parse
+ ws+ { token lexbuf }
+| '\n' { lexbuf.Lexing.lex_curr_p
+ <- (bump_line lexbuf.Lexing.lex_curr_p);
+ token lexbuf }
+| "//" [^'\n']* { token lexbuf }
+
+| '+' { PLUS }
+| '-' { MINUS }
+| '*' { STAR }
+| '/' { SLASH }
+| '%' { PERCENT }
+| '=' { EQ }
+| '<' { LT }
+| "<=" { LE }
+| "==" { EQEQ }
+| "!=" { NE }
+| ">=" { GE }
+| '>' { GT }
+| '!' { NOT }
+| '&' { AND }
+| "&&" { ANDAND }
+| '|' { OR }
+| "||" { OROR }
+| "<<" { LSL }
+| ">>" { LSR }
+| ">>>" { ASR }
+| '~' { TILDE }
+| '{' { LBRACE }
+| '_' (dec as n) { IDX (int_of_string n) }
+| '_' { UNDERSCORE }
+| '}' { RBRACE }
+
+| "+=" { OPEQ (PLUS) }
+| "-=" { OPEQ (MINUS) }
+| "*=" { OPEQ (STAR) }
+| "/=" { OPEQ (SLASH) }
+| "%=" { OPEQ (PERCENT) }
+| "&=" { OPEQ (AND) }
+| "|=" { OPEQ (OR) }
+| "<<=" { OPEQ (LSL) }
+| ">>=" { OPEQ (LSR) }
+| ">>>=" { OPEQ (ASR) }
+| "^=" { OPEQ (CARET) }
+
+| '#' { POUND }
+| '@' { AT }
+| '^' { CARET }
+| '.' { DOT }
+| ',' { COMMA }
+| ';' { SEMI }
+| ':' { COLON }
+| "<-" { LARROW }
+| "<|" { SEND }
+| "->" { RARROW }
+| '(' { LPAREN }
+| ')' { RPAREN }
+| '[' { LBRACKET }
+| ']' { RBRACKET }
+
+| id as i
+ { try
+ Hashtbl.find keyword_table i
+ with
+ Not_found -> IDENT (i)
+ }
+
+| bin as n { LIT_INT (Int64.of_string n, n) }
+| hex as n { LIT_INT (Int64.of_string n, n) }
+| dec as n { LIT_INT (Int64.of_string n, n) }
+| flo as n { LIT_FLO n }
+
+| '\'' { char lexbuf }
+| '"' { let buf = Buffer.create 32 in
+ str buf lexbuf }
+
+| eof { EOF }
+
+and str buf = parse
+ _ as ch
+ {
+ match ch with
+ '"' -> LIT_STR (Buffer.contents buf)
+ | '\\' -> str_escape buf lexbuf
+ | _ ->
+ Buffer.add_char buf ch;
+ let c = Char.code ch in
+ if bounds 0 c 0x7f
+ then str buf lexbuf
+ else
+ if ((c land 0b1110_0000) == 0b1100_0000)
+ then ext_str 1 buf lexbuf
+ else
+ if ((c land 0b1111_0000) == 0b1110_0000)
+ then ext_str 2 buf lexbuf
+ else
+ if ((c land 0b1111_1000) == 0b1111_0000)
+ then ext_str 3 buf lexbuf
+ else
+ if ((c land 0b1111_1100) == 0b1111_1000)
+ then ext_str 4 buf lexbuf
+ else
+ if ((c land 0b1111_1110) == 0b1111_1100)
+ then ext_str 5 buf lexbuf
+ else fail lexbuf "bad initial utf-8 byte"
+ }
+
+and str_escape buf = parse
+ 'x' ((hexdig hexdig) as h)
+ | 'u' ((hexdig hexdig hexdig hexdig) as h)
+ | 'U'
+ ((hexdig hexdig hexdig hexdig
+ hexdig hexdig hexdig hexdig) as h)
+ {
+ Buffer.add_string buf (char_as_utf8 (int_of_string ("0x" ^ h)));
+ str buf lexbuf
+ }
+ | 'n' { Buffer.add_char buf '\n'; str buf lexbuf }
+ | 'r' { Buffer.add_char buf '\r'; str buf lexbuf }
+ | 't' { Buffer.add_char buf '\t'; str buf lexbuf }
+ | '\\' { Buffer.add_char buf '\\'; str buf lexbuf }
+ | '"' { Buffer.add_char buf '"'; str buf lexbuf }
+ | _ as c { fail lexbuf ("bad escape: \\" ^ (Char.escaped c)) }
+
+
+and ext_str n buf = parse
+ _ as ch
+ {
+ let c = Char.code ch in
+ if ((c land 0b1100_0000) == (0b1000_0000))
+ then
+ begin
+ Buffer.add_char buf ch;
+ if n = 1
+ then str buf lexbuf
+ else ext_str (n-1) buf lexbuf
+ end
+ else
+ fail lexbuf "bad trailing utf-8 byte"
+ }
+
+
+and char = parse
+ '\\' { char_escape lexbuf }
+ | _ as c
+ {
+ let c = Char.code c in
+ if bounds 0 c 0x7f
+ then end_char c lexbuf
+ else
+ if ((c land 0b1110_0000) == 0b1100_0000)
+ then ext_char 1 (c land 0b0001_1111) lexbuf
+ else
+ if ((c land 0b1111_0000) == 0b1110_0000)
+ then ext_char 2 (c land 0b0000_1111) lexbuf
+ else
+ if ((c land 0b1111_1000) == 0b1111_0000)
+ then ext_char 3 (c land 0b0000_0111) lexbuf
+ else
+ if ((c land 0b1111_1100) == 0b1111_1000)
+ then ext_char 4 (c land 0b0000_0011) lexbuf
+ else
+ if ((c land 0b1111_1110) == 0b1111_1100)
+ then ext_char 5 (c land 0b0000_0001) lexbuf
+ else fail lexbuf "bad initial utf-8 byte"
+ }
+
+and char_escape = parse
+ 'x' ((hexdig hexdig) as h)
+ | 'u' ((hexdig hexdig hexdig hexdig) as h)
+ | 'U'
+ ((hexdig hexdig hexdig hexdig
+ hexdig hexdig hexdig hexdig) as h)
+ {
+ end_char (int_of_string ("0x" ^ h)) lexbuf
+ }
+ | 'n' { end_char (Char.code '\n') lexbuf }
+ | 'r' { end_char (Char.code '\r') lexbuf }
+ | 't' { end_char (Char.code '\t') lexbuf }
+ | '\\' { end_char (Char.code '\\') lexbuf }
+ | '\'' { end_char (Char.code '\'') lexbuf }
+ | _ as c { fail lexbuf ("bad escape: \\" ^ (Char.escaped c)) }
+
+
+and ext_char n accum = parse
+ _ as c
+ {
+ let c = Char.code c in
+ if ((c land 0b1100_0000) == (0b1000_0000))
+ then
+ let accum = (accum lsl 6) lor (c land 0b0011_1111) in
+ if n = 1
+ then end_char accum lexbuf
+ else ext_char (n-1) accum lexbuf
+ else
+ fail lexbuf "bad trailing utf-8 byte"
+ }
+
+and end_char accum = parse
+ '\'' { LIT_CHAR accum }
+
+
+and bracequote buf depth = parse
+
+ '\\' '{' { Buffer.add_char buf '{';
+ bracequote buf depth lexbuf }
+
+| '{' { Buffer.add_char buf '{';
+ bracequote buf (depth+1) lexbuf }
+
+| '\\' '}' { Buffer.add_char buf '}';
+ bracequote buf depth lexbuf }
+
+| '}' { if depth = 1
+ then BRACEQUOTE (Buffer.contents buf)
+ else
+ begin
+ Buffer.add_char buf '}';
+ bracequote buf (depth-1) lexbuf
+ end }
+
+| '\\' [^'{' '}'] { let s = Lexing.lexeme lexbuf in
+ Buffer.add_string buf s;
+ bracequote buf depth lexbuf }
+
+
+| [^'\\' '{' '}']+ { let s = Lexing.lexeme lexbuf in
+ Buffer.add_string buf s;
+ bracequote buf depth lexbuf }
diff --git a/src/boot/fe/parser.ml b/src/boot/fe/parser.ml
new file mode 100644
index 00000000..3dda93ac
--- /dev/null
+++ b/src/boot/fe/parser.ml
@@ -0,0 +1,374 @@
+
+open Common;;
+open Token;;
+
+(* Fundamental parser types and actions *)
+
+type get_mod_fn = (Ast.meta_pat
+ -> node_id
+ -> (node_id ref)
+ -> (opaque_id ref)
+ -> (filename * Ast.mod_items))
+;;
+
+type pstate =
+ { mutable pstate_peek : token;
+ mutable pstate_ctxt : (string * pos) list;
+ mutable pstate_rstr : bool;
+ mutable pstate_depth: int;
+ pstate_lexbuf : Lexing.lexbuf;
+ pstate_file : filename;
+ pstate_sess : Session.sess;
+ pstate_temp_id : temp_id ref;
+ pstate_node_id : node_id ref;
+ pstate_opaque_id : opaque_id ref;
+ pstate_get_mod : get_mod_fn;
+ 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; }
+;;
+
+let log (ps:pstate) = Session.log "parse"
+ ps.pstate_sess.Session.sess_log_parse
+ ps.pstate_sess.Session.sess_log_out
+;;
+
+let iflog ps thunk =
+ if ps.pstate_sess.Session.sess_log_parse
+ then thunk ()
+ else ()
+;;
+
+let make_parser
+ (tref:temp_id ref)
+ (nref:node_id ref)
+ (oref:opaque_id ref)
+ (sess:Session.sess)
+ (get_mod:get_mod_fn)
+ (infer_lib_name:Ast.ident -> filename)
+ (required:(node_id, (required_lib * nabi_conv)) Hashtbl.t)
+ (required_syms:(node_id, string) Hashtbl.t)
+ (fname:string)
+ : pstate =
+ let lexbuf = Lexing.from_channel (open_in fname) in
+ let spos = { lexbuf.Lexing.lex_start_p with Lexing.pos_fname = fname } in
+ let cpos = { lexbuf.Lexing.lex_curr_p with Lexing.pos_fname = fname } in
+ lexbuf.Lexing.lex_start_p <- spos;
+ lexbuf.Lexing.lex_curr_p <- cpos;
+ let first = Lexer.token lexbuf in
+ let ps =
+ { pstate_peek = first;
+ pstate_ctxt = [];
+ pstate_rstr = false;
+ pstate_depth = 0;
+ pstate_lexbuf = lexbuf;
+ pstate_file = fname;
+ pstate_sess = sess;
+ pstate_temp_id = tref;
+ pstate_node_id = nref;
+ pstate_opaque_id = oref;
+ pstate_get_mod = get_mod;
+ pstate_infer_lib_name = infer_lib_name;
+ pstate_required = required;
+ pstate_required_syms = required_syms; }
+ in
+ iflog ps (fun _ -> log ps "made parser for: %s\n%!" fname);
+ ps
+;;
+
+exception Parse_err of (pstate * string)
+;;
+
+let lexpos (ps:pstate) : pos =
+ let p = ps.pstate_lexbuf.Lexing.lex_start_p in
+ (p.Lexing.pos_fname,
+ p.Lexing.pos_lnum ,
+ (p.Lexing.pos_cnum) - (p.Lexing.pos_bol))
+;;
+
+let next_node_id (ps:pstate) : node_id =
+ let id = !(ps.pstate_node_id) in
+ ps.pstate_node_id := Node ((int_of_node id)+1);
+ id
+;;
+
+let next_opaque_id (ps:pstate) : opaque_id =
+ let id = !(ps.pstate_opaque_id) in
+ ps.pstate_opaque_id := Opaque ((int_of_opaque id)+1);
+ id
+;;
+
+let span
+ (ps:pstate)
+ (apos:pos)
+ (bpos:pos)
+ (x:'a)
+ : 'a identified =
+ let span = { lo = apos; hi = bpos } in
+ let id = next_node_id ps in
+ iflog ps (fun _ -> log ps "span for node #%d: %s"
+ (int_of_node id) (Session.string_of_span span));
+ htab_put ps.pstate_sess.Session.sess_spans id span;
+ { node = x; id = id }
+;;
+
+let decl p i =
+ { Ast.decl_params = p;
+ Ast.decl_item = i }
+;;
+
+let spans
+ (ps:pstate)
+ (things:('a identified) array)
+ (apos:pos)
+ (thing:'a)
+ : ('a identified) array =
+ Array.append things [| (span ps apos (lexpos ps) thing) |]
+;;
+
+(*
+ * The point of this is to make a new node_id entry for a node that is a
+ * "copy" of an lval returned from somewhere else. For example if you create
+ * a temp, the lval it returns can only be used in *one* place, for the
+ * node_id denotes the place that lval is first used; subsequent uses of
+ * 'the same' reference must clone_lval it into a new node_id. Otherwise
+ * there is trouble.
+ *)
+
+let clone_span
+ (ps:pstate)
+ (oldnode:'a identified)
+ (newthing:'b)
+ : 'b identified =
+ let s = Hashtbl.find ps.pstate_sess.Session.sess_spans oldnode.id in
+ span ps s.lo s.hi newthing
+;;
+
+let rec clone_lval (ps:pstate) (lval:Ast.lval) : Ast.lval =
+ match lval with
+ Ast.LVAL_base nb ->
+ let nnb = clone_span ps nb nb.node in
+ Ast.LVAL_base nnb
+ | Ast.LVAL_ext (base, ext) ->
+ Ast.LVAL_ext ((clone_lval ps base), ext)
+;;
+
+let clone_atom (ps:pstate) (atom:Ast.atom) : Ast.atom =
+ match atom with
+ Ast.ATOM_literal _ -> atom
+ | Ast.ATOM_lval lv -> Ast.ATOM_lval (clone_lval ps lv)
+;;
+
+let ctxt (n:string) (f:pstate -> 'a) (ps:pstate) : 'a =
+ (ps.pstate_ctxt <- (n, lexpos ps) :: ps.pstate_ctxt;
+ let res = f ps in
+ ps.pstate_ctxt <- List.tl ps.pstate_ctxt;
+ res)
+;;
+
+let rstr (r:bool) (f:pstate -> 'a) (ps:pstate) : 'a =
+ let prev = ps.pstate_rstr in
+ (ps.pstate_rstr <- r;
+ let res = f ps in
+ ps.pstate_rstr <- prev;
+ res)
+;;
+
+let err (str:string) (ps:pstate) =
+ (Parse_err (ps, (str)))
+;;
+
+
+let (slot_nil:Ast.slot) =
+ { Ast.slot_mode = Ast.MODE_interior;
+ Ast.slot_mutable = false;
+ Ast.slot_ty = Some Ast.TY_nil }
+;;
+
+let (slot_auto:Ast.slot) =
+ { Ast.slot_mode = Ast.MODE_interior;
+ Ast.slot_mutable = true;
+ Ast.slot_ty = None }
+;;
+
+let build_tmp
+ (ps:pstate)
+ (slot:Ast.slot)
+ (apos:pos)
+ (bpos:pos)
+ : (temp_id * Ast.lval * Ast.stmt) =
+ let nonce = !(ps.pstate_temp_id) in
+ ps.pstate_temp_id := Temp ((int_of_temp nonce)+1);
+ iflog ps
+ (fun _ -> log ps "building temporary %d" (int_of_temp nonce));
+ let decl = Ast.DECL_slot (Ast.KEY_temp nonce, (span ps apos bpos slot)) in
+ let declstmt = span ps apos bpos (Ast.STMT_decl decl) in
+ let tmp = Ast.LVAL_base (span ps apos bpos (Ast.BASE_temp nonce)) in
+ (nonce, tmp, declstmt)
+;;
+
+(* Simple helpers *)
+
+(* FIXME: please rename these, they make eyes bleed. *)
+
+let arr (ls:'a list) : 'a array = Array.of_list ls ;;
+let arl (ls:'a list) : 'a array = Array.of_list (List.rev ls) ;;
+let arj (ar:('a array array)) = Array.concat (Array.to_list ar) ;;
+let arj1st (pairs:(('a array) * 'b) array) : (('a array) * 'b array) =
+ let (az, bz) = List.split (Array.to_list pairs) in
+ (Array.concat az, Array.of_list bz)
+
+
+(* Bottom-most parser actions. *)
+
+let peek (ps:pstate) : token =
+ iflog ps
+ begin
+ fun _ ->
+ log ps "peeking at: %s // %s"
+ (string_of_tok ps.pstate_peek)
+ (match ps.pstate_ctxt with
+ (s, _) :: _ -> s
+ | _ -> "<empty>")
+ end;
+ ps.pstate_peek
+;;
+
+
+let bump (ps:pstate) : unit =
+ begin
+ iflog ps (fun _ -> log ps "bumping past: %s"
+ (string_of_tok ps.pstate_peek));
+ ps.pstate_peek <- Lexer.token ps.pstate_lexbuf
+ end
+;;
+
+let bump_bracequote (ps:pstate) : unit =
+ begin
+ assert (ps.pstate_peek = LBRACE);
+ iflog ps (fun _ -> log ps "bumping past: %s"
+ (string_of_tok ps.pstate_peek));
+ let buf = Buffer.create 32 in
+ ps.pstate_peek <- Lexer.bracequote buf 1 ps.pstate_lexbuf
+ end
+;;
+
+
+let expect (ps:pstate) (t:token) : unit =
+ let p = peek ps in
+ if p == t
+ then bump ps
+ else
+ let msg = ("Expected '" ^ (string_of_tok t) ^
+ "', found '" ^ (string_of_tok p ) ^ "'") in
+ raise (Parse_err (ps, msg))
+;;
+
+let unexpected (ps:pstate) =
+ err ("Unexpected token '" ^ (string_of_tok (peek ps)) ^ "'") ps
+;;
+
+
+
+(* Parser combinators. *)
+
+let one_or_more
+ (sep:token)
+ (prule:pstate -> 'a)
+ (ps:pstate)
+ : 'a array =
+ let accum = ref [prule ps] in
+ while peek ps == sep
+ do
+ bump ps;
+ accum := (prule ps) :: !accum
+ done;
+ arl !accum
+;;
+
+let bracketed_seq
+ (mandatory:int)
+ (bra:token)
+ (ket:token)
+ (sepOpt:token option)
+ (prule:pstate -> 'a)
+ (ps:pstate)
+ : 'a array =
+ expect ps bra;
+ let accum = ref [] in
+ let dosep _ =
+ (match sepOpt with
+ None -> ()
+ | Some tok ->
+ if (!accum = [])
+ then ()
+ else expect ps tok)
+ in
+ while mandatory > List.length (!accum) do
+ dosep ();
+ accum := (prule ps) :: (!accum)
+ done;
+ while (not (peek ps = ket))
+ do
+ dosep ();
+ accum := (prule ps) :: !accum
+ done;
+ expect ps ket;
+ arl !accum
+;;
+
+
+let bracketed_zero_or_more
+ (bra:token)
+ (ket:token)
+ (sepOpt:token option)
+ (prule:pstate -> 'a)
+ (ps:pstate)
+ : 'a array =
+ bracketed_seq 0 bra ket sepOpt (ctxt "bracketed_seq" prule) ps
+;;
+
+
+let paren_comma_list
+ (prule:pstate -> 'a)
+ (ps:pstate)
+ : 'a array =
+ bracketed_zero_or_more LPAREN RPAREN (Some COMMA) prule ps
+;;
+
+let bracketed_one_or_more
+ (bra:token)
+ (ket:token)
+ (sepOpt:token option)
+ (prule:pstate -> 'a)
+ (ps:pstate)
+ : 'a array =
+ bracketed_seq 1 bra ket sepOpt (ctxt "bracketed_seq" prule) ps
+;;
+
+let bracketed_two_or_more
+ (bra:token)
+ (ket:token)
+ (sepOpt:token option)
+ (prule:pstate -> 'a)
+ (ps:pstate)
+ : 'a array =
+ bracketed_seq 2 bra ket sepOpt (ctxt "bracketed_seq" prule) ps
+;;
+
+
+let bracketed (bra:token) (ket:token) (prule:pstate -> 'a) (ps:pstate) : 'a =
+ expect ps bra;
+ let res = ctxt "bracketed" prule ps in
+ expect ps ket;
+ res
+;;
+
+(*
+ * 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/pexp.ml b/src/boot/fe/pexp.ml
new file mode 100644
index 00000000..49eeeb5b
--- /dev/null
+++ b/src/boot/fe/pexp.ml
@@ -0,0 +1,1354 @@
+
+open Common;;
+open Token;;
+open Parser;;
+
+(* NB: pexps (parser-expressions) are only used transiently during
+ * parsing, static-evaluation and syntax-expansion. They're desugared
+ * into the general "item" AST and/or evaluated as part of the
+ * outermost "cexp" expressions. Expressions that can show up in source
+ * correspond to this loose grammar and have a wide-ish flexibility in
+ * *theoretical* composition; only subsets of those compositions are
+ * legal in various AST contexts.
+ *
+ * Desugaring on the fly is unfortunately complicated enough to require
+ * -- or at least "make much more convenient" -- this two-pass
+ * routine.
+ *)
+
+type pexp' =
+ PEXP_call of (pexp * pexp array)
+ | PEXP_spawn of (Ast.domain * pexp)
+ | PEXP_bind of (pexp * pexp option array)
+ | PEXP_rec of ((Ast.ident * pexp) array * pexp option)
+ | PEXP_tup of (pexp array)
+ | PEXP_vec of (Ast.slot * (pexp array))
+ | PEXP_port
+ | PEXP_chan of (pexp option)
+ | PEXP_binop of (Ast.binop * pexp * pexp)
+ | PEXP_lazy_and of (pexp * pexp)
+ | PEXP_lazy_or of (pexp * pexp)
+ | PEXP_unop of (Ast.unop * pexp)
+ | PEXP_lval of plval
+ | PEXP_lit of Ast.lit
+ | PEXP_str of string
+ | PEXP_mutable of pexp
+ | PEXP_exterior of pexp
+ | PEXP_custom of Ast.name * (token array) * (string option)
+
+and plval =
+ PLVAL_ident of Ast.ident
+ | PLVAL_app of (Ast.ident * (Ast.ty array))
+ | PLVAL_ext_name of (pexp * Ast.name_component)
+ | PLVAL_ext_pexp of (pexp * pexp)
+
+and pexp = pexp' Common.identified
+;;
+
+(* Pexp grammar. Includes names, idents, types, constrs, binops and unops,
+ etc. *)
+
+let parse_ident (ps:pstate) : Ast.ident =
+ match peek ps with
+ IDENT id -> (bump ps; id)
+ (* Decay IDX tokens to identifiers if they occur ousdide name paths. *)
+ | IDX i -> (bump ps; string_of_tok (IDX i))
+ | _ -> raise (unexpected ps)
+;;
+
+(* Enforces the restricted pexp grammar when applicable (e.g. after "bind") *)
+let check_rstr_start (ps:pstate) : 'a =
+ if (ps.pstate_rstr) then
+ match peek ps with
+ IDENT _ | LPAREN -> ()
+ | _ -> raise (unexpected ps)
+;;
+
+let rec parse_name_component (ps:pstate) : Ast.name_component =
+ match peek ps with
+ IDENT id ->
+ (bump ps;
+ match peek ps with
+ LBRACKET ->
+ let tys =
+ ctxt "name_component: apply"
+ (bracketed_one_or_more LBRACKET RBRACKET
+ (Some COMMA) parse_ty) ps
+ in
+ Ast.COMP_app (id, tys)
+ | _ -> Ast.COMP_ident id)
+
+ | IDX i ->
+ bump ps;
+ Ast.COMP_idx i
+ | _ -> raise (unexpected ps)
+
+and parse_name_base (ps:pstate) : Ast.name_base =
+ match peek ps with
+ IDENT i ->
+ (bump ps;
+ match peek ps with
+ LBRACKET ->
+ let tys =
+ ctxt "name_base: apply"
+ (bracketed_one_or_more LBRACKET RBRACKET
+ (Some COMMA) parse_ty) ps
+ in
+ Ast.BASE_app (i, tys)
+ | _ -> Ast.BASE_ident i)
+ | _ -> raise (unexpected ps)
+
+and parse_name_ext (ps:pstate) (base:Ast.name) : Ast.name =
+ match peek ps with
+ DOT ->
+ bump ps;
+ let comps = one_or_more DOT parse_name_component ps in
+ Array.fold_left (fun x y -> Ast.NAME_ext (x, y)) base comps
+ | _ -> base
+
+
+and parse_name (ps:pstate) : Ast.name =
+ let base = Ast.NAME_base (parse_name_base ps) in
+ let name = parse_name_ext ps base in
+ if Ast.sane_name name
+ then name
+ else raise (err "malformed name" ps)
+
+and parse_carg_base (ps:pstate) : Ast.carg_base =
+ match peek ps with
+ STAR -> bump ps; Ast.BASE_formal
+ | _ -> Ast.BASE_named (parse_name_base ps)
+
+and parse_carg (ps:pstate) : Ast.carg =
+ match peek ps with
+ IDENT _ ->
+ begin
+ let base = Ast.CARG_base (parse_carg_base ps) in
+ let path =
+ match peek ps with
+ DOT ->
+ bump ps;
+ let comps = one_or_more DOT parse_name_component ps in
+ Array.fold_left
+ (fun x y -> Ast.CARG_ext (x, y)) base comps
+ | _ -> base
+ in
+ Ast.CARG_path path
+ end
+ | _ ->
+ Ast.CARG_lit (parse_lit ps)
+
+
+and parse_constraint (ps:pstate) : Ast.constr =
+ match peek ps with
+
+ (*
+ * NB: A constraint *looks* a lot like an EXPR_call, but is restricted
+ * syntactically: the constraint name needs to be a name (not an lval)
+ * and the constraint args all need to be cargs, which are similar to
+ * names but can begin with the 'formal' base anchor '*'.
+ *)
+
+ IDENT _ ->
+ let n = ctxt "constraint: name" parse_name ps in
+ let args = ctxt "constraint: args"
+ (bracketed_zero_or_more
+ LPAREN RPAREN (Some COMMA)
+ parse_carg) ps
+ in
+ { Ast.constr_name = n;
+ Ast.constr_args = args }
+ | _ -> raise (unexpected ps)
+
+
+and parse_constrs (ps:pstate) : Ast.constrs =
+ ctxt "state: constraints" (one_or_more COMMA parse_constraint) ps
+
+and parse_optional_trailing_constrs (ps:pstate) : Ast.constrs =
+ match peek ps with
+ COLON -> (bump ps; parse_constrs ps)
+ | _ -> [| |]
+
+and parse_effect (ps:pstate) : Ast.effect =
+ match peek ps with
+ IO -> bump ps; Ast.IO
+ | STATE -> bump ps; Ast.STATE
+ | UNSAFE -> bump ps; Ast.UNSAFE
+ | _ -> Ast.PURE
+
+and parse_ty_fn
+ (effect:Ast.effect)
+ (ps:pstate)
+ : (Ast.ty_fn * Ast.ident option) =
+ match peek ps with
+ FN | ITER ->
+ let is_iter = (peek ps) = ITER in
+ bump ps;
+ let ident =
+ match peek ps with
+ IDENT i -> bump ps; Some i
+ | _ -> None
+ in
+ let in_slots =
+ match peek ps with
+ _ ->
+ bracketed_zero_or_more LPAREN RPAREN (Some COMMA)
+ (parse_slot_and_optional_ignored_ident true) ps
+ in
+ let out_slot =
+ match peek ps with
+ RARROW -> (bump ps; parse_slot false ps)
+ | _ -> slot_nil
+ in
+ let constrs = parse_optional_trailing_constrs ps in
+ let tsig = { Ast.sig_input_slots = in_slots;
+ Ast.sig_input_constrs = constrs;
+ Ast.sig_output_slot = out_slot; }
+ in
+ let taux = { Ast.fn_effect = effect;
+ Ast.fn_is_iter = is_iter; }
+ in
+ let tfn = (tsig, taux) in
+ (tfn, ident)
+
+ | _ -> raise (unexpected ps)
+
+and check_dup_rec_labels ps labels =
+ arr_check_dups labels
+ (fun l _ ->
+ raise (err (Printf.sprintf
+ "duplicate record label: %s" l) ps));
+
+
+and parse_atomic_ty (ps:pstate) : Ast.ty =
+ match peek ps with
+
+ BOOL ->
+ bump ps;
+ Ast.TY_bool
+
+ | INT ->
+ bump ps;
+ Ast.TY_int
+
+ | UINT ->
+ bump ps;
+ Ast.TY_uint
+
+ | CHAR ->
+ bump ps;
+ Ast.TY_char
+
+ | STR ->
+ bump ps;
+ Ast.TY_str
+
+ | ANY ->
+ bump ps;
+ Ast.TY_any
+
+ | TASK ->
+ bump ps;
+ Ast.TY_task
+
+ | CHAN ->
+ bump ps;
+ Ast.TY_chan (bracketed LBRACKET RBRACKET parse_ty ps)
+
+ | PORT ->
+ bump ps;
+ Ast.TY_port (bracketed LBRACKET RBRACKET parse_ty ps)
+
+ | VEC ->
+ bump ps;
+ Ast.TY_vec (bracketed LBRACKET RBRACKET (parse_slot false) ps)
+
+ | IDENT _ -> Ast.TY_named (parse_name ps)
+
+
+ | TAG ->
+ bump ps;
+ let htab = Hashtbl.create 4 in
+ let parse_tag_entry ps =
+ let ident = parse_ident ps in
+ let tup =
+ match peek ps with
+ LPAREN -> paren_comma_list (parse_slot false) ps
+ | _ -> raise (err "tag variant missing argument list" ps)
+ in
+ htab_put htab (Ast.NAME_base (Ast.BASE_ident ident)) tup
+ in
+ let _ =
+ bracketed_one_or_more LPAREN RPAREN
+ (Some COMMA) (ctxt "tag: variant" parse_tag_entry) ps
+ in
+ Ast.TY_tag htab
+
+ | REC ->
+ bump ps;
+ let parse_rec_entry ps =
+ let mut = parse_mutability ps in
+ let (slot, ident) = parse_slot_and_ident false ps in
+ (ident, apply_mutability slot mut)
+ in
+ let entries = paren_comma_list parse_rec_entry ps in
+ let labels = Array.map (fun (l, _) -> l) entries in
+ begin
+ check_dup_rec_labels ps labels;
+ Ast.TY_rec entries
+ end
+
+ | TUP ->
+ bump ps;
+ let slots = paren_comma_list (parse_slot false) ps in
+ Ast.TY_tup slots
+
+ | MACH m ->
+ bump ps;
+ Ast.TY_mach m
+
+ | IO | STATE | UNSAFE | OBJ | FN | ITER ->
+ let effect = parse_effect ps in
+ begin
+ match peek ps with
+ OBJ ->
+ bump ps;
+ let methods = Hashtbl.create 0 in
+ let parse_method ps =
+ let effect = parse_effect ps in
+ let (tfn, ident) = parse_ty_fn effect ps in
+ expect ps SEMI;
+ match ident with
+ None ->
+ raise (err (Printf.sprintf
+ "missing method identifier") ps)
+ | Some i -> htab_put methods i tfn
+ in
+ ignore (bracketed_zero_or_more LBRACE RBRACE
+ None parse_method ps);
+ Ast.TY_obj (effect, methods)
+
+ | FN | ITER ->
+ Ast.TY_fn (fst (parse_ty_fn effect ps))
+ | _ -> raise (unexpected ps)
+ end
+
+ | LPAREN ->
+ begin
+ bump ps;
+ match peek ps with
+ RPAREN ->
+ bump ps;
+ Ast.TY_nil
+ | _ ->
+ let t = parse_ty ps in
+ expect ps RPAREN;
+ t
+ end
+
+ | _ -> raise (unexpected ps)
+
+and flag (ps:pstate) (tok:token) : bool =
+ if peek ps = tok
+ then (bump ps; true)
+ else false
+
+and parse_mutability (ps:pstate) : bool =
+ flag ps MUTABLE
+
+and apply_mutability (slot:Ast.slot) (mut:bool) : Ast.slot =
+ { slot with Ast.slot_mutable = mut }
+
+and parse_slot (aliases_ok:bool) (ps:pstate) : Ast.slot =
+ let mut = parse_mutability ps in
+ let mode =
+ match (peek ps, aliases_ok) with
+ (AT, _) -> bump ps; Ast.MODE_exterior
+ | (AND, true) -> bump ps; Ast.MODE_alias
+ | (AND, false) -> raise (err "alias slot in prohibited context" ps)
+ | _ -> Ast.MODE_interior
+ in
+ let ty = parse_ty ps in
+ { Ast.slot_mode = mode;
+ Ast.slot_mutable = mut;
+ Ast.slot_ty = Some ty }
+
+and parse_slot_and_ident
+ (aliases_ok:bool)
+ (ps:pstate)
+ : (Ast.slot * Ast.ident) =
+ let slot = ctxt "slot and ident: slot" (parse_slot aliases_ok) ps in
+ let ident = ctxt "slot and ident: ident" parse_ident ps in
+ (slot, ident)
+
+and parse_slot_and_optional_ignored_ident
+ (aliases_ok:bool)
+ (ps:pstate)
+ : Ast.slot =
+ let slot = parse_slot aliases_ok ps in
+ begin
+ match peek ps with
+ IDENT _ -> bump ps
+ | _ -> ()
+ end;
+ slot
+
+and parse_identified_slot
+ (aliases_ok:bool)
+ (ps:pstate)
+ : Ast.slot identified =
+ let apos = lexpos ps in
+ let slot = parse_slot aliases_ok ps in
+ let bpos = lexpos ps in
+ span ps apos bpos slot
+
+and parse_constrained_ty (ps:pstate) : Ast.ty =
+ let base = ctxt "ty: base" parse_atomic_ty ps in
+ match peek ps with
+ COLON ->
+ bump ps;
+ let constrs = ctxt "ty: constrs" parse_constrs ps in
+ Ast.TY_constrained (base, constrs)
+
+ | _ -> base
+
+and parse_ty (ps:pstate) : Ast.ty =
+ parse_constrained_ty ps
+
+
+and parse_rec_input (ps:pstate) : (Ast.ident * pexp) =
+ let lab = (ctxt "rec input: label" parse_ident ps) in
+ match peek ps with
+ EQ ->
+ bump ps;
+ let pexp = ctxt "rec input: expr" parse_pexp ps in
+ (lab, pexp)
+ | _ -> raise (unexpected ps)
+
+
+and parse_rec_body (ps:pstate) : pexp' = (*((Ast.ident * pexp) array) =*)
+ begin
+ expect ps LPAREN;
+ match peek ps with
+ RPAREN -> PEXP_rec ([||], None)
+ | WITH -> raise (err "empty record extension" ps)
+ | _ ->
+ let inputs = one_or_more COMMA parse_rec_input ps in
+ let labels = Array.map (fun (l, _) -> l) inputs in
+ begin
+ check_dup_rec_labels ps labels;
+ match peek ps with
+ RPAREN -> (bump ps; PEXP_rec (inputs, None))
+ | WITH ->
+ begin
+ bump ps;
+ let base =
+ ctxt "rec input: extension base"
+ parse_pexp ps
+ in
+ expect ps RPAREN;
+ PEXP_rec (inputs, Some base)
+ end
+ | _ -> raise (err "expected 'with' or ')'" ps)
+ end
+ end
+
+
+and parse_lit (ps:pstate) : Ast.lit =
+ match peek ps with
+ LIT_INT (n,s) -> (bump ps; Ast.LIT_int (n,s))
+ | LIT_CHAR c -> (bump ps; Ast.LIT_char c)
+ | LIT_BOOL b -> (bump ps; Ast.LIT_bool b)
+ | _ -> raise (unexpected ps)
+
+
+and parse_bottom_pexp (ps:pstate) : pexp =
+ check_rstr_start ps;
+ let apos = lexpos ps in
+ match peek ps with
+
+ MUTABLE ->
+ bump ps;
+ let inner = parse_pexp ps in
+ let bpos = lexpos ps in
+ span ps apos bpos (PEXP_mutable inner)
+
+ | AT ->
+ bump ps;
+ let inner = parse_pexp ps in
+ let bpos = lexpos ps in
+ span ps apos bpos (PEXP_exterior inner)
+
+ | TUP ->
+ bump ps;
+ let pexps = ctxt "paren pexps(s)" (rstr false parse_pexp_list) ps in
+ let bpos = lexpos ps in
+ span ps apos bpos (PEXP_tup pexps)
+
+ | REC ->
+ bump ps;
+ let body = ctxt "rec pexp: rec body" parse_rec_body ps in
+ let bpos = lexpos ps in
+ span ps apos bpos body
+
+ | VEC ->
+ bump ps;
+ begin
+ let slot =
+ match peek ps with
+ LBRACKET -> bracketed LBRACKET RBRACKET (parse_slot false) ps
+ | _ -> { Ast.slot_mode = Ast.MODE_interior;
+ Ast.slot_mutable = false;
+ Ast.slot_ty = None }
+ in
+ let pexps = ctxt "vec pexp: exprs" parse_pexp_list ps in
+ let bpos = lexpos ps in
+ span ps apos bpos (PEXP_vec (slot, pexps))
+ end
+
+
+ | LIT_STR s ->
+ bump ps;
+ let bpos = lexpos ps in
+ span ps apos bpos (PEXP_str s)
+
+ | PORT ->
+ begin
+ bump ps;
+ expect ps LPAREN;
+ expect ps RPAREN;
+ let bpos = lexpos ps in
+ span ps apos bpos (PEXP_port)
+ end
+
+ | CHAN ->
+ begin
+ bump ps;
+ let port =
+ match peek ps with
+ LPAREN ->
+ begin
+ bump ps;
+ match peek ps with
+ RPAREN -> (bump ps; None)
+ | _ ->
+ let lv = parse_pexp ps in
+ expect ps RPAREN;
+ Some lv
+ end
+ | _ -> raise (unexpected ps)
+ in
+ let bpos = lexpos ps in
+ span ps apos bpos (PEXP_chan port)
+ end
+
+ | SPAWN ->
+ bump ps;
+ let domain =
+ match peek ps with
+ THREAD -> bump ps; Ast.DOMAIN_thread
+ | _ -> Ast.DOMAIN_local
+ in
+ let pexp = ctxt "spawn [domain] pexp: init call" parse_pexp ps in
+ let bpos = lexpos ps in
+ span ps apos bpos (PEXP_spawn (domain, pexp))
+
+ | BIND ->
+ let apos = lexpos ps in
+ begin
+ bump ps;
+ let pexp = ctxt "bind pexp: function" (rstr true parse_pexp) ps in
+ let args =
+ ctxt "bind args"
+ (paren_comma_list parse_bind_arg) ps
+ in
+ let bpos = lexpos ps in
+ span ps apos bpos (PEXP_bind (pexp, args))
+ end
+
+ | IDENT i ->
+ begin
+ bump ps;
+ match peek ps with
+ LBRACKET ->
+ begin
+ let tys =
+ ctxt "apply-type expr"
+ (bracketed_one_or_more LBRACKET RBRACKET
+ (Some COMMA) parse_ty) ps
+ in
+ let bpos = lexpos ps in
+ span ps apos bpos (PEXP_lval (PLVAL_app (i, tys)))
+ end
+
+ | _ ->
+ begin
+ let bpos = lexpos ps in
+ span ps apos bpos (PEXP_lval (PLVAL_ident i))
+ end
+ end
+
+ | (INT | UINT | CHAR | BOOL) as tok ->
+ begin
+ bump ps;
+ expect ps LPAREN;
+ match peek ps with
+ (LIT_INT _ | LIT_CHAR _ | LIT_BOOL _) as tok2 ->
+ bump ps;
+ expect ps RPAREN;
+ let i = match tok2 with
+ LIT_INT i -> i
+ | LIT_CHAR c -> (Int64.of_int c,
+ Common.escaped_char c)
+ | LIT_BOOL b -> if b then (1L, "1") else (0L, "0")
+ | _ -> bug () "expected int/char literal"
+ in
+ let bpos = lexpos ps in
+ span ps apos bpos
+ (PEXP_lit
+ (match tok with
+ INT -> Ast.LIT_int i
+ | UINT -> Ast.LIT_uint i
+ | CHAR ->
+ Ast.LIT_char
+ (Int64.to_int (fst i))
+ | BOOL -> Ast.LIT_bool (fst i <> 0L)
+ | _ -> bug () "expected int/uint/char/bool token"))
+
+ | _ ->
+ let pexp = parse_pexp ps in
+ expect ps RPAREN;
+ let bpos = lexpos ps in
+ let t =
+ match tok with
+ INT -> Ast.TY_int
+ | UINT -> Ast.TY_uint
+ | CHAR -> Ast.TY_char
+ | BOOL -> Ast.TY_bool
+ | _ -> bug () "expected int/uint/char/bool token"
+ in
+ let t = span ps apos bpos t in
+ span ps apos bpos
+ (PEXP_unop ((Ast.UNOP_cast t), pexp))
+ end
+
+ | MACH m ->
+ let literal (num, str) =
+ let _ = bump ps in
+ let _ = expect ps RPAREN in
+ let bpos = lexpos ps in
+ let check_range (lo:int64) (hi:int64) : unit =
+ if (num < lo) or (num > hi)
+ then raise (err (Printf.sprintf
+ "integral literal %Ld out of range [%Ld,%Ld]"
+ num lo hi) ps)
+ else ()
+ in
+ begin
+ match m with
+ TY_u8 -> check_range 0L 0xffL
+ | TY_u16 -> check_range 0L 0xffffL
+ | TY_u32 -> check_range 0L 0xffffffffL
+ (* | TY_u64 -> ... *)
+ | TY_i8 -> check_range (-128L) 127L
+ | TY_i16 -> check_range (-32768L) 32767L
+ | TY_i32 -> check_range (-2147483648L) 2147483647L
+ (*
+ | TY_i64 -> ...
+ | TY_f32 -> ...
+ | TY_f64 -> ...
+ *)
+ | _ -> ()
+ end;
+ span ps apos bpos
+ (PEXP_lit
+ (Ast.LIT_mach
+ (m, num, str)))
+
+ in
+ begin
+ bump ps;
+ expect ps LPAREN;
+ match peek ps with
+ LIT_INT (n,s) -> literal (n,s)
+ | MINUS ->
+ begin
+ bump ps;
+ match peek ps with
+ LIT_INT (n,s) ->
+ literal (Int64.neg n, "-" ^ s)
+ | _ -> raise (unexpected ps)
+ end
+ | _ ->
+ let pexp = parse_pexp ps in
+ expect ps RPAREN;
+ let bpos = lexpos ps in
+ let t = span ps apos bpos (Ast.TY_mach m) in
+ span ps apos bpos
+ (PEXP_unop ((Ast.UNOP_cast t), pexp))
+ end
+
+ | POUND ->
+ bump ps;
+ let name = parse_name ps in
+ let toks =
+ match peek ps with
+ LPAREN ->
+ bump ps;
+ let toks = Queue.create () in
+ while (peek ps) <> RPAREN
+ do
+ Queue.add (peek ps) toks;
+ bump ps;
+ done;
+ expect ps RPAREN;
+ queue_to_arr toks
+ | _ -> [| |]
+ in
+ let str =
+ match peek ps with
+ LBRACE ->
+ begin
+ bump_bracequote ps;
+ match peek ps with
+ BRACEQUOTE s -> bump ps; Some s
+ | _ -> raise (unexpected ps)
+ end
+ | _ -> None
+ in
+ let bpos = lexpos ps in
+ span ps apos bpos
+ (PEXP_custom (name, toks, str))
+
+ | LPAREN ->
+ begin
+ bump ps;
+ match peek ps with
+ RPAREN ->
+ bump ps;
+ let bpos = lexpos ps in
+ span ps apos bpos (PEXP_lit Ast.LIT_nil)
+ | _ ->
+ let pexp = parse_pexp ps in
+ expect ps RPAREN;
+ pexp
+ end
+
+ | _ ->
+ let lit = parse_lit ps in
+ let bpos = lexpos ps in
+ span ps apos bpos (PEXP_lit lit)
+
+
+and parse_bind_arg (ps:pstate) : pexp option =
+ match peek ps with
+ UNDERSCORE -> (bump ps; None)
+ | _ -> Some (parse_pexp ps)
+
+
+and parse_ext_pexp (ps:pstate) (pexp:pexp) : pexp =
+ let apos = lexpos ps in
+ match peek ps with
+ LPAREN ->
+ if ps.pstate_rstr
+ then pexp
+ else
+ let args = parse_pexp_list ps in
+ let bpos = lexpos ps in
+ let ext = span ps apos bpos (PEXP_call (pexp, args)) in
+ parse_ext_pexp ps ext
+
+ | DOT ->
+ begin
+ bump ps;
+ let ext =
+ match peek ps with
+ LPAREN ->
+ bump ps;
+ let rhs = rstr false parse_pexp ps in
+ expect ps RPAREN;
+ let bpos = lexpos ps in
+ span ps apos bpos
+ (PEXP_lval (PLVAL_ext_pexp (pexp, rhs)))
+ | _ ->
+ let rhs = parse_name_component ps in
+ let bpos = lexpos ps in
+ span ps apos bpos
+ (PEXP_lval (PLVAL_ext_name (pexp, rhs)))
+ in
+ parse_ext_pexp ps ext
+ end
+
+ | _ -> pexp
+
+
+and parse_negation_pexp (ps:pstate) : pexp =
+ let apos = lexpos ps in
+ match peek ps with
+ NOT ->
+ bump ps;
+ let rhs = ctxt "negation pexp" parse_negation_pexp ps in
+ let bpos = lexpos ps in
+ span ps apos bpos (PEXP_unop (Ast.UNOP_not, rhs))
+
+ | TILDE ->
+ bump ps;
+ let rhs = ctxt "negation pexp" parse_negation_pexp ps in
+ let bpos = lexpos ps in
+ span ps apos bpos (PEXP_unop (Ast.UNOP_bitnot, rhs))
+
+ | MINUS ->
+ bump ps;
+ let rhs = ctxt "negation pexp" parse_negation_pexp ps in
+ let bpos = lexpos ps in
+ span ps apos bpos (PEXP_unop (Ast.UNOP_neg, rhs))
+
+ | _ ->
+ let lhs = parse_bottom_pexp ps in
+ parse_ext_pexp ps lhs
+
+
+(* Binops are all left-associative, *)
+(* so we factor out some of the parsing code here. *)
+and binop_rhs
+ (ps:pstate)
+ (name:string)
+ (apos:pos)
+ (lhs:pexp)
+ (rhs_parse_fn:pstate -> pexp)
+ (op:Ast.binop)
+ : pexp =
+ bump ps;
+ let rhs = (ctxt (name ^ " rhs") rhs_parse_fn ps) in
+ let bpos = lexpos ps in
+ span ps apos bpos (PEXP_binop (op, lhs, rhs))
+
+
+and parse_factor_pexp (ps:pstate) : pexp =
+ let name = "factor pexp" in
+ let apos = lexpos ps in
+ let lhs = ctxt (name ^ " lhs") parse_negation_pexp ps in
+ match peek ps with
+ STAR -> binop_rhs ps name apos lhs parse_factor_pexp Ast.BINOP_mul
+ | SLASH -> binop_rhs ps name apos lhs parse_factor_pexp Ast.BINOP_div
+ | PERCENT -> binop_rhs ps name apos lhs parse_factor_pexp Ast.BINOP_mod
+ | _ -> lhs
+
+
+and parse_term_pexp (ps:pstate) : pexp =
+ let name = "term pexp" in
+ let apos = lexpos ps in
+ let lhs = ctxt (name ^ " lhs") parse_factor_pexp ps in
+ match peek ps with
+ PLUS -> binop_rhs ps name apos lhs parse_term_pexp Ast.BINOP_add
+ | MINUS -> binop_rhs ps name apos lhs parse_term_pexp Ast.BINOP_sub
+ | _ -> lhs
+
+
+and parse_shift_pexp (ps:pstate) : pexp =
+ let name = "shift pexp" in
+ let apos = lexpos ps in
+ let lhs = ctxt (name ^ " lhs") parse_term_pexp ps in
+ match peek ps with
+ LSL -> binop_rhs ps name apos lhs parse_shift_pexp Ast.BINOP_lsl
+ | LSR -> binop_rhs ps name apos lhs parse_shift_pexp Ast.BINOP_lsr
+ | ASR -> binop_rhs ps name apos lhs parse_shift_pexp Ast.BINOP_asr
+ | _ -> lhs
+
+
+and parse_and_pexp (ps:pstate) : pexp =
+ let name = "and pexp" in
+ let apos = lexpos ps in
+ let lhs = ctxt (name ^ " lhs") parse_shift_pexp ps in
+ match peek ps with
+ AND -> binop_rhs ps name apos lhs parse_and_pexp Ast.BINOP_and
+ | _ -> lhs
+
+
+and parse_xor_pexp (ps:pstate) : pexp =
+ let name = "xor pexp" in
+ let apos = lexpos ps in
+ let lhs = ctxt (name ^ " lhs") parse_and_pexp ps in
+ match peek ps with
+ CARET -> binop_rhs ps name apos lhs parse_xor_pexp Ast.BINOP_xor
+ | _ -> lhs
+
+
+and parse_or_pexp (ps:pstate) : pexp =
+ let name = "or pexp" in
+ let apos = lexpos ps in
+ let lhs = ctxt (name ^ " lhs") parse_xor_pexp ps in
+ match peek ps with
+ OR -> binop_rhs ps name apos lhs parse_or_pexp Ast.BINOP_or
+ | _ -> lhs
+
+
+and parse_relational_pexp (ps:pstate) : pexp =
+ let name = "relational pexp" in
+ let apos = lexpos ps in
+ let lhs = ctxt (name ^ " lhs") parse_or_pexp ps in
+ match peek ps with
+ LT -> binop_rhs ps name apos lhs parse_relational_pexp Ast.BINOP_lt
+ | LE -> binop_rhs ps name apos lhs parse_relational_pexp Ast.BINOP_le
+ | GE -> binop_rhs ps name apos lhs parse_relational_pexp Ast.BINOP_ge
+ | GT -> binop_rhs ps name apos lhs parse_relational_pexp Ast.BINOP_gt
+ | _ -> lhs
+
+
+and parse_equality_pexp (ps:pstate) : pexp =
+ let name = "equality pexp" in
+ let apos = lexpos ps in
+ let lhs = ctxt (name ^ " lhs") parse_relational_pexp ps in
+ match peek ps with
+ EQEQ -> binop_rhs ps name apos lhs parse_equality_pexp Ast.BINOP_eq
+ | NE -> binop_rhs ps name apos lhs parse_equality_pexp Ast.BINOP_ne
+ | _ -> lhs
+
+
+and parse_andand_pexp (ps:pstate) : pexp =
+ let name = "andand pexp" in
+ let apos = lexpos ps in
+ let lhs = ctxt (name ^ " lhs") parse_equality_pexp ps in
+ match peek ps with
+ ANDAND ->
+ bump ps;
+ let rhs = parse_andand_pexp ps in
+ let bpos = lexpos ps in
+ span ps apos bpos (PEXP_lazy_and (lhs, rhs))
+
+ | _ -> lhs
+
+
+and parse_oror_pexp (ps:pstate) : pexp =
+ let name = "oror pexp" in
+ let apos = lexpos ps in
+ let lhs = ctxt (name ^ " lhs") parse_andand_pexp ps in
+ match peek ps with
+ OROR ->
+ bump ps;
+ let rhs = parse_oror_pexp ps in
+ let bpos = lexpos ps in
+ span ps apos bpos (PEXP_lazy_or (lhs, rhs))
+
+ | _ -> lhs
+
+and parse_as_pexp (ps:pstate) : pexp =
+ let apos = lexpos ps in
+ let pexp = ctxt "as pexp" parse_oror_pexp ps in
+ match peek ps with
+ AS ->
+ bump ps;
+ let tapos = lexpos ps in
+ let t = parse_ty ps in
+ let bpos = lexpos ps in
+ let t = span ps tapos bpos t in
+ span ps apos bpos
+ (PEXP_unop ((Ast.UNOP_cast t), pexp))
+
+ | _ -> pexp
+
+and parse_pexp (ps:pstate) : pexp =
+ parse_as_pexp ps
+
+
+and parse_pexp_list (ps:pstate) : pexp array =
+ match peek ps with
+ LPAREN ->
+ bracketed_zero_or_more LPAREN RPAREN (Some COMMA)
+ (ctxt "pexp list" parse_pexp) ps
+ | _ -> raise (unexpected ps)
+
+;;
+
+(*
+ * FIXME: This is a crude approximation of the syntax-extension system,
+ * for purposes of prototyping and/or hard-wiring any extensions we
+ * wish to use in the bootstrap compiler. The eventual aim is to permit
+ * loading rust crates to process extensions, but this will likely
+ * require a rust-based frontend, or an ocaml-FFI-based connection to
+ * rust crates. At the moment we have neither.
+ *)
+
+let expand_pexp_custom
+ (ps:pstate)
+ (name:Ast.name)
+ (args:token array)
+ (body:string option)
+ : pexp' =
+ let nstr = Ast.fmt_to_str Ast.fmt_name name in
+ match (nstr, (Array.length args), body) with
+
+ ("shell", 0, Some cmd) ->
+ let c = Unix.open_process_in cmd in
+ let b = Buffer.create 32 in
+ let rec r _ =
+ try
+ Buffer.add_char b (input_char c);
+ r ()
+ with
+ End_of_file ->
+ ignore (Unix.close_process_in c);
+ Buffer.contents b
+ in
+ PEXP_str (r ())
+
+ | _ ->
+ raise (err ("unsupported syntax extension: " ^ nstr) ps)
+;;
+
+(*
+ * Desugarings depend on context:
+ *
+ * - If a pexp is used on the RHS of an assignment, it's turned into
+ * an initialization statement such as STMT_init_rec or such. This
+ * removes the possibility of initializing into a temp only to
+ * copy out. If the topmost pexp in such a desugaring is an atom,
+ * unop or binop, of course, it will still just emit a STMT_copy
+ * on a primitive expression.
+ *
+ * - If a pexp is used in the context where an atom is required, a
+ * statement declaring a temporary and initializing it with the
+ * result of the pexp is prepended, and the temporary atom is used.
+ *)
+
+let rec desugar_lval (ps:pstate) (pexp:pexp) : (Ast.stmt array * Ast.lval) =
+ let s = Hashtbl.find ps.pstate_sess.Session.sess_spans pexp.id in
+ let (apos, bpos) = (s.lo, s.hi) in
+ match pexp.node with
+
+ PEXP_lval (PLVAL_ident ident) ->
+ let nb = span ps apos bpos (Ast.BASE_ident ident) in
+ ([||], Ast.LVAL_base nb)
+
+ | PEXP_lval (PLVAL_app (ident, tys)) ->
+ let nb = span ps apos bpos (Ast.BASE_app (ident, tys)) in
+ ([||], Ast.LVAL_base nb)
+
+ | PEXP_lval (PLVAL_ext_name (base_pexp, comp)) ->
+ let (base_stmts, base_atom) = desugar_expr_atom ps base_pexp in
+ let base_lval = atom_lval ps base_atom in
+ (base_stmts, Ast.LVAL_ext (base_lval, Ast.COMP_named comp))
+
+ | PEXP_lval (PLVAL_ext_pexp (base_pexp, ext_pexp)) ->
+ let (base_stmts, base_atom) = desugar_expr_atom ps base_pexp in
+ let (ext_stmts, ext_atom) = desugar_expr_atom ps ext_pexp in
+ let base_lval = atom_lval ps base_atom in
+ (Array.append base_stmts ext_stmts,
+ Ast.LVAL_ext (base_lval, Ast.COMP_atom (clone_atom ps ext_atom)))
+
+ | _ ->
+ let (stmts, atom) = desugar_expr_atom ps pexp in
+ (stmts, atom_lval ps atom)
+
+
+and desugar_expr
+ (ps:pstate)
+ (pexp:pexp)
+ : (Ast.stmt array * Ast.expr) =
+ match pexp.node with
+
+ PEXP_unop (op, pe) ->
+ let (stmts, at) = desugar_expr_atom ps pe in
+ (stmts, Ast.EXPR_unary (op, at))
+
+ | PEXP_binop (op, lhs, rhs) ->
+ let (lhs_stmts, lhs_atom) = desugar_expr_atom ps lhs in
+ let (rhs_stmts, rhs_atom) = desugar_expr_atom ps rhs in
+ (Array.append lhs_stmts rhs_stmts,
+ Ast.EXPR_binary (op, lhs_atom, rhs_atom))
+
+ | _ ->
+ let (stmts, at) = desugar_expr_atom ps pexp in
+ (stmts, Ast.EXPR_atom at)
+
+
+and desugar_opt_expr_atom
+ (ps:pstate)
+ (po:pexp option)
+ : (Ast.stmt array * Ast.atom option) =
+ match po with
+ None -> ([| |], None)
+ | Some pexp ->
+ let (stmts, atom) = desugar_expr_atom ps pexp in
+ (stmts, Some atom)
+
+
+and desugar_expr_atom
+ (ps:pstate)
+ (pexp:pexp)
+ : (Ast.stmt array * Ast.atom) =
+ let s = Hashtbl.find ps.pstate_sess.Session.sess_spans pexp.id in
+ let (apos, bpos) = (s.lo, s.hi) in
+ match pexp.node with
+
+ PEXP_unop _
+ | PEXP_binop _
+ | PEXP_lazy_or _
+ | PEXP_lazy_and _
+ | PEXP_rec _
+ | PEXP_tup _
+ | PEXP_str _
+ | PEXP_vec _
+ | PEXP_port
+ | PEXP_chan _
+ | PEXP_call _
+ | PEXP_bind _
+ | PEXP_spawn _ ->
+ let (_, tmp, decl_stmt) = build_tmp ps slot_auto apos bpos in
+ let stmts = desugar_expr_init ps tmp pexp in
+ (Array.append [| decl_stmt |] stmts,
+ Ast.ATOM_lval (clone_lval ps tmp))
+
+ | PEXP_lit lit ->
+ ([||], Ast.ATOM_literal (span ps apos bpos lit))
+
+ | PEXP_lval _ ->
+ let (stmts, lval) = desugar_lval ps pexp in
+ (stmts, Ast.ATOM_lval lval)
+
+ | PEXP_exterior _ ->
+ raise (err "exterior symbol in atom context" ps)
+
+ | PEXP_mutable _ ->
+ raise (err "mutable keyword in atom context" ps)
+
+ | PEXP_custom (n, a, b) ->
+ desugar_expr_atom ps
+ { pexp with node = expand_pexp_custom ps n a b }
+
+
+and desugar_expr_mode_mut_atom
+ (ps:pstate)
+ (pexp:pexp)
+ : (Ast.stmt array * (Ast.mode * bool * Ast.atom)) =
+ let desugar_inner mode mut e =
+ let (stmts, atom) = desugar_expr_atom ps e in
+ (stmts, (mode, mut, atom))
+ in
+ match pexp.node with
+ PEXP_mutable {node=(PEXP_exterior e); id=_} ->
+ desugar_inner Ast.MODE_exterior true e
+ | PEXP_exterior e ->
+ desugar_inner Ast.MODE_exterior false e
+ | PEXP_mutable e ->
+ desugar_inner Ast.MODE_interior true e
+ | _ ->
+ desugar_inner Ast.MODE_interior false pexp
+
+and desugar_expr_atoms
+ (ps:pstate)
+ (pexps:pexp array)
+ : (Ast.stmt array * Ast.atom array) =
+ arj1st (Array.map (desugar_expr_atom ps) pexps)
+
+and desugar_opt_expr_atoms
+ (ps:pstate)
+ (pexps:pexp option array)
+ : (Ast.stmt array * Ast.atom option array) =
+ arj1st (Array.map (desugar_opt_expr_atom ps) pexps)
+
+and desugar_expr_mode_mut_atoms
+ (ps:pstate)
+ (pexps:pexp array)
+ : (Ast.stmt array * (Ast.mode * bool * Ast.atom) array) =
+ arj1st (Array.map (desugar_expr_mode_mut_atom ps) pexps)
+
+and desugar_expr_init
+ (ps:pstate)
+ (dst_lval:Ast.lval)
+ (pexp:pexp)
+ : (Ast.stmt array) =
+ let s = Hashtbl.find ps.pstate_sess.Session.sess_spans pexp.id in
+ let (apos, bpos) = (s.lo, s.hi) in
+
+ (* Helpers. *)
+ let ss x = span ps apos bpos x in
+ let cp v = Ast.STMT_copy (clone_lval ps dst_lval, v) in
+ let aa x y = Array.append x y in
+ let ac xs = Array.concat xs in
+
+ match pexp.node with
+
+ PEXP_lit _
+ | PEXP_lval _ ->
+ let (stmts, atom) = desugar_expr_atom ps pexp in
+ aa stmts [| ss (cp (Ast.EXPR_atom atom)) |]
+
+ | PEXP_binop (op, lhs, rhs) ->
+ let (lhs_stmts, lhs_atom) = desugar_expr_atom ps lhs in
+ let (rhs_stmts, rhs_atom) = desugar_expr_atom ps rhs in
+ let copy_stmt =
+ ss (cp (Ast.EXPR_binary (op, lhs_atom, rhs_atom)))
+ in
+ ac [ lhs_stmts; rhs_stmts; [| copy_stmt |] ]
+
+ (* x = a && b ==> if (a) { x = b; } else { x = false; } *)
+
+ | PEXP_lazy_and (lhs, rhs) ->
+ let (lhs_stmts, lhs_atom) = desugar_expr_atom ps lhs in
+ let (rhs_stmts, rhs_atom) = desugar_expr_atom ps rhs in
+ let sthen =
+ ss (aa rhs_stmts [| ss (cp (Ast.EXPR_atom rhs_atom)) |])
+ in
+ let selse =
+ ss [| ss (cp (Ast.EXPR_atom
+ (Ast.ATOM_literal (ss (Ast.LIT_bool false))))) |]
+ in
+ let sif =
+ ss (Ast.STMT_if { Ast.if_test = Ast.EXPR_atom lhs_atom;
+ Ast.if_then = sthen;
+ Ast.if_else = Some selse })
+ in
+ aa lhs_stmts [| sif |]
+
+ (* x = a || b ==> if (a) { x = true; } else { x = b; } *)
+
+ | PEXP_lazy_or (lhs, rhs) ->
+ let (lhs_stmts, lhs_atom) = desugar_expr_atom ps lhs in
+ let (rhs_stmts, rhs_atom) = desugar_expr_atom ps rhs in
+ let sthen =
+ ss [| ss (cp (Ast.EXPR_atom
+ (Ast.ATOM_literal (ss (Ast.LIT_bool true))))) |]
+ in
+ let selse =
+ ss (aa rhs_stmts [| ss (cp (Ast.EXPR_atom rhs_atom)) |])
+ in
+ let sif =
+ ss (Ast.STMT_if { Ast.if_test = Ast.EXPR_atom lhs_atom;
+ Ast.if_then = sthen;
+ Ast.if_else = Some selse })
+ in
+ aa lhs_stmts [| sif |]
+
+
+ | PEXP_unop (op, rhs) ->
+ let (rhs_stmts, rhs_atom) = desugar_expr_atom ps rhs in
+ let expr = Ast.EXPR_unary (op, rhs_atom) in
+ let copy_stmt = ss (cp expr) in
+ aa rhs_stmts [| copy_stmt |]
+
+ | PEXP_call (fn, args) ->
+ let (fn_stmts, fn_atom) = desugar_expr_atom ps fn in
+ let (arg_stmts, arg_atoms) = desugar_expr_atoms ps args in
+ let fn_lval = atom_lval ps fn_atom in
+ let call_stmt = ss (Ast.STMT_call (dst_lval, fn_lval, arg_atoms)) in
+ ac [ fn_stmts; arg_stmts; [| call_stmt |] ]
+
+ | PEXP_bind (fn, args) ->
+ let (fn_stmts, fn_atom) = desugar_expr_atom ps fn in
+ let (arg_stmts, arg_atoms) = desugar_opt_expr_atoms ps args in
+ let fn_lval = atom_lval ps fn_atom in
+ 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) ->
+ begin
+ match sub.node with
+ PEXP_call (fn, args) ->
+ let (fn_stmts, fn_atom) = desugar_expr_atom ps fn in
+ 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))
+ in
+ ac [ fn_stmts; arg_stmts; [| spawn_stmt |] ]
+ | _ -> raise (err "non-call spawn" ps)
+ end
+
+ | PEXP_rec (args, base) ->
+ let (arg_stmts, entries) =
+ arj1st
+ begin
+ Array.map
+ begin
+ fun (ident, pexp) ->
+ let (stmts, (mode, mut, atom)) =
+ desugar_expr_mode_mut_atom ps pexp
+ in
+ (stmts, (ident, mode, mut, atom))
+ end
+ args
+ end
+ in
+ begin
+ match base with
+ Some base ->
+ let (base_stmts, base_lval) = desugar_lval ps base in
+ let rec_stmt =
+ ss (Ast.STMT_init_rec
+ (dst_lval, entries, Some base_lval))
+ in
+ ac [ arg_stmts; base_stmts; [| rec_stmt |] ]
+ | None ->
+ let rec_stmt =
+ ss (Ast.STMT_init_rec (dst_lval, entries, None))
+ in
+ aa arg_stmts [| rec_stmt |]
+ end
+
+ | PEXP_tup args ->
+ let (arg_stmts, arg_mode_atoms) =
+ desugar_expr_mode_mut_atoms ps args
+ in
+ let stmt = ss (Ast.STMT_init_tup (dst_lval, arg_mode_atoms)) in
+ aa arg_stmts [| stmt |]
+
+ | PEXP_str s ->
+ let stmt = ss (Ast.STMT_init_str (dst_lval, s)) in
+ [| stmt |]
+
+ | PEXP_vec (slot, args) ->
+ let (arg_stmts, arg_atoms) = desugar_expr_atoms ps args in
+ let stmt = ss (Ast.STMT_init_vec (dst_lval, slot, arg_atoms)) in
+ aa arg_stmts [| stmt |]
+
+ | PEXP_port ->
+ [| ss (Ast.STMT_init_port dst_lval) |]
+
+ | PEXP_chan pexp_opt ->
+ let (port_stmts, port_opt) =
+ match pexp_opt with
+ None -> ([||], None)
+ | Some port_pexp ->
+ begin
+ let (port_stmts, port_atom) =
+ desugar_expr_atom ps port_pexp
+ in
+ let port_lval = atom_lval ps port_atom in
+ (port_stmts, Some port_lval)
+ end
+ in
+ let chan_stmt =
+ ss
+ (Ast.STMT_init_chan (dst_lval, port_opt))
+ in
+ aa port_stmts [| chan_stmt |]
+
+ | PEXP_exterior _ ->
+ raise (err "exterior symbol in initialiser context" ps)
+
+ | PEXP_mutable _ ->
+ raise (err "mutable keyword in initialiser context" ps)
+
+ | PEXP_custom (n, a, b) ->
+ desugar_expr_init ps dst_lval
+ { pexp with node = expand_pexp_custom ps n a b }
+
+
+and atom_lval (ps:pstate) (at:Ast.atom) : Ast.lval =
+ match at with
+ Ast.ATOM_lval lv -> lv
+ | Ast.ATOM_literal _ -> raise (err "literal where lval expected" ps)
+;;
+
+
+
+
+(*
+ * 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/token.ml b/src/boot/fe/token.ml
new file mode 100644
index 00000000..636e1ac2
--- /dev/null
+++ b/src/boot/fe/token.ml
@@ -0,0 +1,308 @@
+type token =
+
+ (* Expression operator symbols *)
+ PLUS
+ | MINUS
+ | STAR
+ | SLASH
+ | PERCENT
+ | EQ
+ | LT
+ | LE
+ | EQEQ
+ | NE
+ | GE
+ | GT
+ | NOT
+ | TILDE
+ | CARET
+ | AND
+ | ANDAND
+ | OR
+ | OROR
+ | LSL
+ | LSR
+ | ASR
+ | OPEQ of token
+ | 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 of (int64 * string)
+ | LIT_FLO of string
+ | LIT_STR of string
+ | LIT_CHAR of int
+ | LIT_BOOL of bool
+
+ (* Name components *)
+ | IDENT of string
+ | IDX of int
+ | UNDERSCORE
+
+ (* Reserved type names *)
+ | BOOL
+ | INT
+ | UINT
+ | CHAR
+ | STR
+ | MACH of Common.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
+
+ | EOF
+
+ | BRACEQUOTE of string
+
+;;
+
+let rec string_of_tok t =
+ match t with
+ (* Operator symbols (mostly) *)
+ PLUS -> "+"
+ | MINUS -> "-"
+ | STAR -> "*"
+ | SLASH -> "/"
+ | PERCENT -> "%"
+ | EQ -> "="
+ | LT -> "<"
+ | LE -> "<="
+ | EQEQ -> "=="
+ | NE -> "!="
+ | GE -> ">="
+ | GT -> ">"
+ | TILDE -> "~"
+ | CARET -> "^"
+ | NOT -> "!"
+ | AND -> "&"
+ | ANDAND -> "&&"
+ | OR -> "|"
+ | OROR -> "||"
+ | LSL -> "<<"
+ | LSR -> ">>"
+ | ASR -> ">>>"
+ | OPEQ op -> string_of_tok op ^ "="
+ | AS -> "as"
+ | WITH -> "with"
+
+ (* Structural symbols *)
+ | AT -> "@"
+ | DOT -> "."
+ | COMMA -> ","
+ | SEMI -> ";"
+ | COLON -> ":"
+ | RARROW -> "->"
+ | SEND -> "<|"
+ | LARROW -> "<-"
+ | LPAREN -> "("
+ | RPAREN -> ")"
+ | LBRACKET -> "["
+ | RBRACKET -> "]"
+ | LBRACE -> "{"
+ | RBRACE -> "}"
+
+ (* Module and crate keywords *)
+ | MOD -> "mod"
+ | USE -> "use"
+ | AUTH -> "auth"
+
+ (* Metaprogramming keywords *)
+ | SYNTAX -> "syntax"
+ | META -> "meta"
+ | POUND -> "#"
+
+ (* Control-flow keywords *)
+ | IF -> "if"
+ | ELSE -> "else"
+ | DO -> "do"
+ | WHILE -> "while"
+ | ALT -> "alt"
+ | CASE -> "case"
+
+ | FAIL -> "fail"
+ | DROP -> "drop"
+
+ | IN -> "in"
+ | FOR -> "for"
+ | EACH -> "each"
+ | PUT -> "put"
+ | RET -> "ret"
+ | BE -> "be"
+
+ (* Type and type-state keywords *)
+ | TYPE -> "type"
+ | CHECK -> "check"
+ | CLAIM -> "claim"
+ | PROVE -> "prove"
+
+ (* Effect keywords *)
+ | IO -> "io"
+ | STATE -> "state"
+ | UNSAFE -> "unsafe"
+
+ (* Type qualifiers *)
+ | NATIVE -> "native"
+ | AUTO -> "auto"
+ | MUTABLE -> "mutable"
+
+ (* Name management *)
+ | IMPORT -> "import"
+ | EXPORT -> "export"
+
+ (* Value / stmt declarators. *)
+ | LET -> "let"
+
+ (* Magic runtime services *)
+ | LOG -> "log"
+ | SPAWN -> "spawn"
+ | BIND -> "bind"
+ | THREAD -> "thread"
+ | YIELD -> "yield"
+ | JOIN -> "join"
+
+ (* Literals *)
+ | LIT_INT (_,s) -> s
+ | LIT_FLO n -> n
+ | LIT_STR s -> ("\"" ^ (String.escaped s) ^ "\"")
+ | LIT_CHAR c -> ("'" ^ (Common.escaped_char c) ^ "'")
+ | LIT_BOOL b -> if b then "true" else "false"
+
+ (* Name components *)
+ | IDENT s -> s
+ | IDX i -> ("_" ^ (string_of_int i))
+ | UNDERSCORE -> "_"
+
+ (* Reserved type names *)
+ | BOOL -> "bool"
+ | INT -> "int"
+ | UINT -> "uint"
+ | CHAR -> "char"
+ | STR -> "str"
+ | MACH m -> Common.string_of_ty_mach m
+
+ (* Algebraic type constructors *)
+ | REC -> "rec"
+ | TUP -> "tup"
+ | TAG -> "tag"
+ | VEC -> "vec"
+ | ANY -> "any"
+
+ (* Callable type constructors *)
+ | FN -> "fn"
+ | ITER -> "fn"
+
+ (* Object type *)
+ | OBJ -> "obj"
+
+ (* Ports and channels *)
+ | CHAN -> "chan"
+ | PORT -> "port"
+
+ (* Taskess types *)
+ | TASK -> "task"
+
+ | BRACEQUOTE _ -> "{...bracequote...}"
+
+ | EOF -> "<EOF>"
+;;
+
+
+(*
+ * Local Variables:
+ * fill-column: 78;
+ * indent-tabs-mode: nil
+ * buffer-file-coding-system: utf-8-unix
+ * compile-command: "make -k -C ../.. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
+ * End:
+ *)
diff --git a/src/boot/llvm/llabi.ml b/src/boot/llvm/llabi.ml
new file mode 100644
index 00000000..fd5d9277
--- /dev/null
+++ b/src/boot/llvm/llabi.ml
@@ -0,0 +1,69 @@
+(*
+ * LLVM integration with the Rust runtime.
+ *)
+
+type abi = {
+ crate_ty: Llvm.lltype;
+ task_ty: Llvm.lltype;
+ word_ty: Llvm.lltype;
+ rust_start: Llvm.llvalue;
+};;
+
+let declare_abi (llctx:Llvm.llcontext) (llmod:Llvm.llmodule) : abi =
+ let i32 = Llvm.i32_type llctx in
+
+ let crate_ty =
+ (* TODO: other architectures besides x86 *)
+ let crate_opaque_ty = Llvm.opaque_type llctx in
+ let crate_tyhandle = Llvm.handle_to_type (Llvm.struct_type llctx [|
+ i32; (* ptrdiff_t image_base_off *)
+ Llvm.pointer_type crate_opaque_ty;(* uintptr_t self_addr *)
+ i32; (* ptrdiff_t debug_abbrev_off *)
+ i32; (* size_t debug_abbrev_sz *)
+ i32; (* ptrdiff_t debug_info_off *)
+ i32; (* size_t debug_info_sz *)
+ i32; (* size_t activate_glue_off *)
+ i32; (* size_t main_exit_task_glue_off *)
+ i32; (* size_t unwind_glue_off *)
+ i32; (* size_t yield_glue_off *)
+ i32; (* int n_rust_syms *)
+ i32; (* int n_c_syms *)
+ i32 (* int n_libs *)
+ |])
+ in
+ Llvm.refine_type crate_opaque_ty (Llvm.type_of_handle crate_tyhandle);
+ Llvm.type_of_handle crate_tyhandle
+ in
+ ignore (Llvm.define_type_name "rust_crate" crate_ty llmod);
+
+ let task_ty =
+ (* TODO: other architectures besides x86 *)
+ Llvm.struct_type llctx [|
+ i32; (* size_t refcnt *)
+ Llvm.pointer_type i32; (* stk_seg *stk *)
+ Llvm.pointer_type i32; (* uintptr_t runtime_sp *)
+ Llvm.pointer_type i32; (* uintptr_t rust_sp *)
+ Llvm.pointer_type i32; (* rust_rt *rt *)
+ Llvm.pointer_type i32 (* rust_crate_cache *cache *)
+ |]
+ in
+ ignore (Llvm.define_type_name "rust_task" task_ty llmod);
+
+ let rust_start_ty =
+ let task_ptr_ty = Llvm.pointer_type task_ty in
+ let llnilty = Llvm.array_type (Llvm.i1_type llctx) 0 in
+ let main_ty = Llvm.function_type (Llvm.void_type llctx)
+ [| Llvm.pointer_type llnilty; task_ptr_ty; |]
+ in
+ let args_ty = Array.map Llvm.pointer_type [| main_ty; crate_ty; |] in
+ let args_ty = Array.append args_ty [| i32; i32 |] in
+ Llvm.function_type i32 args_ty
+ in
+ {
+ crate_ty = crate_ty;
+ task_ty = task_ty;
+ word_ty = i32;
+ rust_start = Llvm.declare_function "rust_start" rust_start_ty llmod
+ }
+;;
+
diff --git a/src/boot/llvm/llasm.ml b/src/boot/llvm/llasm.ml
new file mode 100644
index 00000000..56448b07
--- /dev/null
+++ b/src/boot/llvm/llasm.ml
@@ -0,0 +1,192 @@
+(*
+ * machine-specific assembler routines.
+ *)
+
+open Common;;
+
+type asm_glue =
+ {
+ asm_activate_glue : Llvm.llvalue;
+ asm_yield_glue : Llvm.llvalue;
+ asm_upcall_glues : Llvm.llvalue array;
+ }
+;;
+
+let n_upcall_glues = 7
+;;
+
+(* x86-specific asm. *)
+
+let x86_glue
+ (llctx:Llvm.llcontext)
+ (llmod:Llvm.llmodule)
+ (abi:Llabi.abi)
+ (sess:Session.sess)
+ : asm_glue =
+ let (prefix,align) =
+ match sess.Session.sess_targ with
+ Linux_x86_elf
+ | Win32_x86_pe -> ("",4)
+ | MacOS_x86_macho -> ("_", 16)
+ in
+ let save_callee_saves =
+ ["pushl %ebp";
+ "pushl %edi";
+ "pushl %esi";
+ "pushl %ebx";]
+ in
+ let restore_callee_saves =
+ ["popl %ebx";
+ "popl %esi";
+ "popl %edi";
+ "popl %ebp";]
+ in
+ let load_esp_from_rust_sp = ["movl 12(%edx), %esp"] in
+ let load_esp_from_runtime_sp = ["movl 8(%edx), %esp"] in
+ let store_esp_to_rust_sp = ["movl %esp, 12(%edx)"] in
+ let store_esp_to_runtime_sp = ["movl %esp, 8(%edx)"] in
+ let list_init i f = (Array.to_list (Array.init i f)) in
+ let list_init_concat i f = List.concat (list_init i f) in
+
+ let glue =
+ [
+ ("rust_activate_glue",
+ String.concat "\n\t"
+ (["movl 4(%esp), %edx # edx = rust_task"]
+ @ save_callee_saves
+ @ store_esp_to_runtime_sp
+ @ load_esp_from_rust_sp
+ (*
+ * This 'add' instruction is a bit surprising.
+ * See lengthy comment in boot/be/x86.ml activate_glue.
+ *)
+ @ ["addl $20, 12(%edx)"]
+ @ restore_callee_saves
+ @ ["ret"]));
+
+ ("rust_yield_glue",
+ String.concat "\n\t"
+
+ (["movl 0(%esp), %edx # edx = rust_task"]
+ @ load_esp_from_rust_sp
+ @ save_callee_saves
+ @ store_esp_to_rust_sp
+ @ load_esp_from_runtime_sp
+ @ restore_callee_saves
+ @ ["ret"]))
+ ]
+ @ list_init n_upcall_glues
+ begin
+ fun i ->
+ (*
+ * 0, 4, 8, 12 are callee-saves
+ * 16 is retpc
+ * 20 is taskptr
+ * 24 is callee
+ * 28 .. (7+i) * 4 are args
+ *)
+
+ ((Printf.sprintf "rust_upcall_%d" i),
+ String.concat "\n\t"
+ (save_callee_saves
+ @ ["movl %esp, %ebp # ebp = rust_sp";
+ "movl 20(%esp), %edx # edx = rust_task"]
+ @ store_esp_to_rust_sp
+ @ load_esp_from_runtime_sp
+ @ [Printf.sprintf
+ "subl $%d, %%esp # esp -= args" ((i+1)*4);
+ "andl $~0xf, %esp # align esp down";
+ "movl %edx, (%esp) # arg[0] = rust_task "]
+
+ @ (list_init_concat i
+ begin
+ fun j ->
+ [ Printf.sprintf "movl %d(%%ebp),%%edx" ((j+7)*4);
+ Printf.sprintf "movl %%edx,%d(%%esp)" ((j+1)*4) ]
+ end)
+
+ @ ["movl 24(%ebp), %edx # edx = callee";
+ "call *%edx # call *%edx";
+ "movl 20(%ebp), %edx # edx = rust_task"]
+ @ load_esp_from_rust_sp
+ @ restore_callee_saves
+ @ ["ret"]))
+ end
+ in
+
+ let _ =
+ Llvm.set_module_inline_asm llmod
+ begin
+ String.concat "\n"
+ begin
+ List.map
+ begin
+ fun (sym,asm) ->
+ Printf.sprintf
+ "\t.globl %s%s\n\t.balign %d\n%s%s:\n\t%s"
+ prefix sym align prefix sym asm
+ end
+ glue
+ end
+ end
+ in
+
+ let decl_cdecl_fn name out_ty arg_tys =
+ let ty = Llvm.function_type out_ty arg_tys in
+ let fn = Llvm.declare_function name ty llmod in
+ Llvm.set_function_call_conv Llvm.CallConv.c fn;
+ fn
+ in
+
+ let decl_glue s =
+ let task_ptr_ty = Llvm.pointer_type abi.Llabi.task_ty in
+ let void_ty = Llvm.void_type llctx in
+ decl_cdecl_fn s void_ty [| task_ptr_ty |]
+ in
+
+ let decl_upcall n =
+ let task_ptr_ty = Llvm.pointer_type abi.Llabi.task_ty in
+ let word_ty = abi.Llabi.word_ty in
+ let callee_ty = word_ty in
+ let args_ty =
+ Array.append
+ [| task_ptr_ty; callee_ty |]
+ (Array.init n (fun _ -> word_ty))
+ in
+ let name = Printf.sprintf "rust_upcall_%d" n in
+ decl_cdecl_fn name word_ty args_ty
+ in
+ {
+ asm_activate_glue = decl_glue "rust_activate_glue";
+ asm_yield_glue = decl_glue "rust_yield_glue";
+ asm_upcall_glues = Array.init n_upcall_glues decl_upcall;
+ }
+;;
+
+(* x64-specific asm. *)
+(* arm-specific asm. *)
+(* ... *)
+
+
+let get_glue
+ (llctx:Llvm.llcontext)
+ (llmod:Llvm.llmodule)
+ (abi:Llabi.abi)
+ (sess:Session.sess)
+ : asm_glue =
+ match sess.Session.sess_targ with
+ Linux_x86_elf
+ | Win32_x86_pe
+ | MacOS_x86_macho ->
+ x86_glue llctx llmod abi sess
+;;
+
+
+(*
+ * Local Variables:
+ * fill-column: 78;
+ * indent-tabs-mode: nil
+ * buffer-file-coding-system: utf-8-unix
+ * compile-command: "make -k -C ../.. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
+ * End:
+ *)
diff --git a/src/boot/llvm/llemit.ml b/src/boot/llvm/llemit.ml
new file mode 100644
index 00000000..2b229fde
--- /dev/null
+++ b/src/boot/llvm/llemit.ml
@@ -0,0 +1,36 @@
+(*
+ * LLVM emitter.
+ *)
+
+(* The top-level interface to the LLVM translation subsystem. *)
+let trans_and_process_crate
+ (sess:Session.sess)
+ (sem_cx:Semant.ctxt)
+ (crate:Ast.crate)
+ : unit =
+ let llcontext = Llvm.create_context () in
+ let emit_file (llmod:Llvm.llmodule) : unit =
+ let filename = Session.filename_of sess.Session.sess_out in
+ if not (Llvm_bitwriter.write_bitcode_file llmod filename)
+ then raise (Failure ("failed to write the LLVM bitcode '" ^ filename
+ ^ "'"))
+ in
+ let llmod = Lltrans.trans_crate sem_cx llcontext sess crate in
+ begin
+ try
+ emit_file llmod
+ with e -> Llvm.dispose_module llmod; raise e
+ end;
+ Llvm.dispose_module llmod;
+ Llvm.dispose_context llcontext
+;;
+
+(*
+ * Local Variables:
+ * fill-column: 78;
+ * indent-tabs-mode: nil
+ * buffer-file-coding-system: utf-8-unix
+ * compile-command: "make -k -C ../.. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
+ * End:
+ *)
+
diff --git a/src/boot/llvm/llfinal.ml b/src/boot/llvm/llfinal.ml
new file mode 100644
index 00000000..64ea3d37
--- /dev/null
+++ b/src/boot/llvm/llfinal.ml
@@ -0,0 +1,96 @@
+(*
+ * LLVM ABI-level stuff that needs to happen after modules have been
+ * translated.
+ *)
+
+let finalize_module
+ (llctx:Llvm.llcontext)
+ (llmod:Llvm.llmodule)
+ (abi:Llabi.abi)
+ (asm_glue:Llasm.asm_glue)
+ (exit_task_glue:Llvm.llvalue)
+ (crate_ptr:Llvm.llvalue)
+ : unit =
+ let i32 = Llvm.i32_type llctx in
+
+ (*
+ * Count the number of Rust functions and the number of C functions by
+ * simply (and crudely) testing whether each function in the module begins
+ * with "_rust_".
+ *)
+
+ let (rust_fn_count, c_fn_count) =
+ let count (rust_fn_count, c_fn_count) fn =
+ let begins_with prefix str =
+ let (str_len, prefix_len) =
+ (String.length str, String.length prefix)
+ in
+ prefix_len <= str_len && (String.sub str 0 prefix_len) = prefix
+ in
+ if begins_with "_rust_" (Llvm.value_name fn) then
+ (rust_fn_count + 1, c_fn_count)
+ else
+ (rust_fn_count, c_fn_count + 1)
+ in
+ Llvm.fold_left_functions count (0, 0) llmod
+ in
+
+ let crate_val =
+ let crate_addr = Llvm.const_ptrtoint crate_ptr i32 in
+ let glue_off glue =
+ let addr = Llvm.const_ptrtoint glue i32 in
+ Llvm.const_sub addr crate_addr
+ in
+ let activate_glue_off = glue_off asm_glue.Llasm.asm_activate_glue in
+ let yield_glue_off = glue_off asm_glue.Llasm.asm_yield_glue in
+ let exit_task_glue_off = glue_off exit_task_glue in
+
+ Llvm.const_struct llctx [|
+ Llvm.const_int i32 0; (* ptrdiff_t image_base_off *)
+ crate_ptr; (* uintptr_t self_addr *)
+ Llvm.const_int i32 0; (* ptrdiff_t debug_abbrev_off *)
+ Llvm.const_int i32 0; (* size_t debug_abbrev_sz *)
+ Llvm.const_int i32 0; (* ptrdiff_t debug_info_off *)
+ Llvm.const_int i32 0; (* size_t debug_info_sz *)
+ activate_glue_off; (* size_t activate_glue_off *)
+ exit_task_glue_off; (* size_t main_exit_task_glue_off *)
+ Llvm.const_int i32 0; (* size_t unwind_glue_off *)
+ yield_glue_off; (* size_t yield_glue_off *)
+ Llvm.const_int i32 rust_fn_count; (* int n_rust_syms *)
+ Llvm.const_int i32 c_fn_count; (* int n_c_syms *)
+ Llvm.const_int i32 0 (* int n_libs *)
+ |]
+ in
+
+ Llvm.set_initializer crate_val crate_ptr;
+
+ (* Define the main function for crt0 to call. *)
+ let main_fn =
+ let main_ty = Llvm.function_type i32 [| i32; i32 |] in
+ Llvm.define_function "main" main_ty llmod
+ in
+ let argc = Llvm.param main_fn 0 in
+ let argv = Llvm.param main_fn 1 in
+ let main_builder = Llvm.builder_at_end llctx (Llvm.entry_block main_fn) in
+ let rust_main_fn =
+ match Llvm.lookup_function "_rust_main" llmod with
+ None -> raise (Failure "no main function found")
+ | Some fn -> fn
+ in
+ let rust_start = abi.Llabi.rust_start in
+ let rust_start_args = [| rust_main_fn; crate_ptr; argc; argv |] in
+ ignore (Llvm.build_call
+ rust_start rust_start_args "start_rust" main_builder);
+ ignore (Llvm.build_ret (Llvm.const_int i32 0) main_builder)
+;;
+
+
+(*
+ * Local Variables:
+ * fill-column: 78;
+ * indent-tabs-mode: nil
+ * buffer-file-coding-system: utf-8-unix
+ * compile-command: "make -k -C ../.. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
+ * End:
+ *)
+
diff --git a/src/boot/llvm/lltrans.ml b/src/boot/llvm/lltrans.ml
new file mode 100644
index 00000000..7f985d25
--- /dev/null
+++ b/src/boot/llvm/lltrans.ml
@@ -0,0 +1,938 @@
+(*
+ * LLVM translator.
+ *)
+
+open Common;;
+open Transutil;;
+
+let log cx = Session.log "trans"
+ cx.Semant.ctxt_sess.Session.sess_log_trans
+ cx.Semant.ctxt_sess.Session.sess_log_out
+;;
+
+let trans_crate
+ (sem_cx:Semant.ctxt)
+ (llctx:Llvm.llcontext)
+ (sess:Session.sess)
+ (crate:Ast.crate)
+ : Llvm.llmodule =
+
+ let iflog thunk =
+ if sess.Session.sess_log_trans
+ then thunk ()
+ else ()
+ in
+
+ (* Helpers for adding metadata. *)
+ let (dbg_mdkind:int) = Llvm.mdkind_id llctx "dbg" in
+ let set_dbg_metadata (inst:Llvm.llvalue) (md:Llvm.llvalue) : unit =
+ Llvm.set_metadata inst dbg_mdkind md
+ in
+ let md_str (s:string) : Llvm.llvalue = Llvm.mdstring llctx s in
+ let md_node (vals:Llvm.llvalue array) : Llvm.llvalue =
+ Llvm.mdnode llctx vals
+ in
+ let const_i32 (i:int) : Llvm.llvalue =
+ Llvm.const_int (Llvm.i32_type llctx) i
+ in
+ let const_i1 (i:int) : Llvm.llvalue =
+ Llvm.const_int (Llvm.i1_type llctx) i
+ in
+ let llvm_debug_version : int = 0x8 lsl 16 in
+ let const_dw_tag (tag:Dwarf.dw_tag) : Llvm.llvalue =
+ const_i32 (llvm_debug_version lor (Dwarf.dw_tag_to_int tag))
+ in
+
+ (* Translation of our node_ids into LLVM identifiers, which are strings. *)
+ let next_anon_llid = ref 0 in
+ let num_llid num klass = Printf.sprintf "%s%d" klass num in
+ let anon_llid klass =
+ let llid = num_llid !next_anon_llid klass in
+ next_anon_llid := !next_anon_llid + 1;
+ llid
+ in
+ let node_llid (node_id_opt:node_id option) : (string -> string) =
+ match node_id_opt with
+ None -> anon_llid
+ | Some (Node num) -> num_llid num
+ in
+
+ (*
+ * Returns a bogus value for use in stub code that hasn't been implemented
+ * yet.
+ *
+ * TODO: On some joyous day, remove me.
+ *)
+ let bogus = Llvm.const_null (Llvm.i32_type llctx) in
+ let bogus_ptr = Llvm.const_null (Llvm.pointer_type (Llvm.i32_type llctx)) in
+
+ let llnilty = Llvm.array_type (Llvm.i1_type llctx) 0 in
+ let llnil = Llvm.const_array (Llvm.i1_type llctx) [| |] in
+
+ let ty_of_item = Hashtbl.find sem_cx.Semant.ctxt_all_item_types in
+ let ty_of_slot n = Semant.slot_ty (Semant.get_slot sem_cx n) in
+
+ let filename = Session.filename_of sess.Session.sess_in in
+ let llmod = Llvm.create_module llctx filename in
+
+ let (abi:Llabi.abi) = Llabi.declare_abi llctx llmod in
+ let (crate_ptr:Llvm.llvalue) =
+ Llvm.declare_global abi.Llabi.crate_ty "rust_crate" llmod
+ in
+
+ let (void_ty:Llvm.lltype) = Llvm.void_type llctx in
+ let (word_ty:Llvm.lltype) = abi.Llabi.word_ty in
+ let (wordptr_ty:Llvm.lltype) = Llvm.pointer_type word_ty in
+ let (task_ty:Llvm.lltype) = abi.Llabi.task_ty in
+ let (task_ptr_ty:Llvm.lltype) = Llvm.pointer_type task_ty in
+ let fn_ty (out:Llvm.lltype) (args:Llvm.lltype array) : Llvm.lltype =
+ Llvm.function_type out args
+ in
+
+ let imm (i:int64) : Llvm.llvalue =
+ Llvm.const_int word_ty (Int64.to_int i)
+ in
+
+ let asm_glue = Llasm.get_glue llctx llmod abi sess in
+
+ let llty_str llty =
+ Llvm.string_of_lltype llty
+ in
+
+ let llval_str llv =
+ let ts = llty_str (Llvm.type_of llv) in
+ match Llvm.value_name llv with
+ "" ->
+ Printf.sprintf "<anon=%s>" ts
+ | s -> Printf.sprintf "<%s=%s>" s ts
+ in
+
+ let llvals_str llvals =
+ (String.concat ", "
+ (Array.to_list
+ (Array.map llval_str llvals)))
+ in
+
+ let build_call callee args rvid builder =
+ iflog
+ begin
+ fun _ ->
+ let name = Llvm.value_name callee in
+ log sem_cx "build_call: %s(%s)" name (llvals_str args);
+ log sem_cx "build_call: typeof(%s) = %s"
+ name (llty_str (Llvm.type_of callee))
+ end;
+ Llvm.build_call callee args rvid builder
+ in
+
+ (* Upcall translation *)
+
+ let extern_upcalls = Hashtbl.create 0 in
+ let trans_upcall
+ (llbuilder:Llvm.llbuilder)
+ (lltask:Llvm.llvalue)
+ (name:string)
+ (lldest:Llvm.llvalue option)
+ (llargs:Llvm.llvalue array) =
+ let n = Array.length llargs in
+ let llglue = asm_glue.Llasm.asm_upcall_glues.(n) in
+ let llupcall = htab_search_or_add extern_upcalls name
+ begin
+ fun _ ->
+ let args_ty =
+ Array.append
+ [| task_ptr_ty |]
+ (Array.init n (fun i -> Llvm.type_of llargs.(i)))
+ in
+ let out_ty = match lldest with
+ None -> void_ty
+ | Some v -> Llvm.type_of v
+ in
+ let fty = fn_ty out_ty args_ty in
+ (*
+ * NB: At this point it actually doesn't matter what type
+ * we gave the upcall function, as we're just going to
+ * pointercast it to a word and pass it to the upcall-glue
+ * for now. But possibly in the future it might matter if
+ * we develop a proper upcall calling convention.
+ *)
+ Llvm.declare_function name fty llmod
+ end
+ in
+ (* Cast everything to plain words so we can hand off to the glue. *)
+ let llupcall = Llvm.const_pointercast llupcall word_ty in
+ let llargs =
+ Array.map
+ (fun arg ->
+ Llvm.build_pointercast arg word_ty
+ (anon_llid "arg") llbuilder)
+ llargs
+ in
+ let llallargs = Array.append [| lltask; llupcall |] llargs in
+ let llid = anon_llid "rv" in
+ let llrv = build_call llglue llallargs llid llbuilder in
+ Llvm.set_instruction_call_conv Llvm.CallConv.c llrv;
+ match lldest with
+ None -> ()
+ | Some lldest ->
+ let lldest =
+ Llvm.build_pointercast lldest wordptr_ty "" llbuilder
+ in
+ ignore (Llvm.build_store llrv lldest llbuilder);
+ in
+
+ let upcall
+ (llbuilder:Llvm.llbuilder)
+ (lltask:Llvm.llvalue)
+ (name:string)
+ (lldest:Llvm.llvalue option)
+ (llargs:Llvm.llvalue array)
+ : unit =
+ trans_upcall llbuilder lltask name lldest llargs
+ in
+
+ let trans_free
+ (llbuilder:Llvm.llbuilder)
+ (lltask:Llvm.llvalue)
+ (src:Llvm.llvalue)
+ : unit =
+ upcall llbuilder lltask "upcall_free" None [| src |]
+ in
+
+ (*
+ * let trans_malloc (llbuilder:Llvm.llbuilder)
+ * (dst:Llvm.llvalue) (nbytes:int64) : unit =
+ * upcall llbuilder "upcall_malloc" (Some dst) [| imm nbytes |]
+ * in
+ *)
+
+ (* Type translation *)
+
+ let lltys = Hashtbl.create 0 in
+
+ let trans_mach_ty (mty:ty_mach) : Llvm.lltype =
+ let tycon =
+ match mty with
+ TY_u8 | TY_i8 -> Llvm.i8_type
+ | TY_u16 | TY_i16 -> Llvm.i16_type
+ | TY_u32 | TY_i32 -> Llvm.i32_type
+ | TY_u64 | TY_i64 -> Llvm.i64_type
+ | TY_f32 -> Llvm.float_type
+ | TY_f64 -> Llvm.double_type
+ in
+ tycon llctx
+ in
+
+
+ let rec trans_ty_full (ty:Ast.ty) : Llvm.lltype =
+ let p t = Llvm.pointer_type t in
+ let s ts = Llvm.struct_type llctx ts in
+ let opaque _ = Llvm.opaque_type llctx in
+ let vec_body_ty _ =
+ s [| word_ty; word_ty; word_ty; (opaque()) |]
+ in
+ let rc_opaque_ty =
+ s [| word_ty; (opaque()) |]
+ in
+ match ty with
+ Ast.TY_any -> opaque ()
+ | Ast.TY_nil -> llnilty
+ | Ast.TY_bool -> Llvm.i1_type llctx
+ | Ast.TY_mach mty -> trans_mach_ty mty
+ | Ast.TY_int -> word_ty
+ | Ast.TY_uint -> word_ty
+ | Ast.TY_char -> Llvm.i32_type llctx
+ | Ast.TY_vec _
+ | Ast.TY_str -> p (vec_body_ty())
+
+ | Ast.TY_fn tfn ->
+ let (tsig, _) = tfn in
+ let lloutptr = p (trans_slot None tsig.Ast.sig_output_slot) in
+ let lltaskty = p abi.Llabi.task_ty in
+ let llins = Array.map (trans_slot None) tsig.Ast.sig_input_slots in
+ fn_ty void_ty (Array.append [| lloutptr; lltaskty |] llins)
+
+ | Ast.TY_tup slots ->
+ s (Array.map (trans_slot None) slots)
+
+ | Ast.TY_rec entries ->
+ s (Array.map (fun e -> trans_slot None (snd e)) entries)
+
+ | Ast.TY_constrained (ty', _) -> trans_ty ty'
+
+ | Ast.TY_chan _ | Ast.TY_port _ | Ast.TY_task ->
+ p rc_opaque_ty
+
+ | Ast.TY_native _ ->
+ word_ty
+
+ | Ast.TY_tag _ | Ast.TY_iso _ | Ast.TY_idx _
+ | Ast.TY_obj _ | Ast.TY_type -> (opaque()) (* TODO *)
+
+ | Ast.TY_param _ | Ast.TY_named _ ->
+ bug () "unresolved type in lltrans"
+
+ and trans_ty t =
+ htab_search_or_add lltys t (fun _ -> trans_ty_full t)
+
+ (* Translates the type of a slot into the corresponding LLVM type. If the
+ * id_opt parameter is specified, then the type will be fetched from the
+ * context. *)
+ and trans_slot (id_opt:node_id option) (slot:Ast.slot) : Llvm.lltype =
+ let ty =
+ match id_opt with
+ Some id -> ty_of_slot id
+ | None -> Semant.slot_ty slot
+ in
+ let base_llty = trans_ty ty in
+ match slot.Ast.slot_mode with
+ Ast.MODE_exterior _
+ | Ast.MODE_alias _ ->
+ Llvm.pointer_type base_llty
+ | Ast.MODE_interior _ -> base_llty
+ in
+
+ let get_element_ptr
+ (llbuilder:Llvm.llbuilder)
+ (ptr:Llvm.llvalue)
+ (i:int)
+ : Llvm.llvalue =
+ (*
+ * GEP takes a first-index of zero. Because it must! And this is
+ * sufficiently surprising that the GEP FAQ exists. And you must
+ * read it.
+ *)
+ let deref_ptr = Llvm.const_int (Llvm.i32_type llctx) 0 in
+ let idx = Llvm.const_int (Llvm.i32_type llctx) i in
+ Llvm.build_gep ptr [| deref_ptr; idx |] (anon_llid "gep") llbuilder
+ in
+
+ let free_ty
+ (llbuilder:Llvm.llbuilder)
+ (lltask:Llvm.llvalue)
+ (ty:Ast.ty)
+ (ptr:Llvm.llvalue)
+ : unit =
+ match ty with
+ Ast.TY_port _
+ | Ast.TY_chan _
+ | Ast.TY_task -> bug () "unimplemented ty in Lltrans.free_ty"
+ | _ -> trans_free llbuilder lltask ptr
+ in
+
+ let rec iter_ty_slots_full
+ (llbuilder:Llvm.llbuilder ref)
+ (ty:Ast.ty)
+ (dst_ptr:Llvm.llvalue)
+ (src_ptr:Llvm.llvalue)
+ (f:(Llvm.llvalue
+ -> Llvm.llvalue
+ -> Ast.slot
+ -> (Ast.ty_iso option)
+ -> unit))
+ (curr_iso:Ast.ty_iso option)
+ : unit =
+
+ (* NB: must deref llbuilder at call-time; don't curry this. *)
+ let gep p i = get_element_ptr (!llbuilder) p i in
+
+ match ty with
+ Ast.TY_rec entries ->
+ iter_rec_slots gep dst_ptr src_ptr entries f curr_iso
+
+ | Ast.TY_tup slots ->
+ iter_tup_slots gep dst_ptr src_ptr slots f curr_iso
+
+ | Ast.TY_tag _
+ | Ast.TY_iso _
+ | Ast.TY_fn _
+ | Ast.TY_obj _ ->
+ bug () "unimplemented ty in Lltrans.iter_ty_slots_full"
+
+ | _ -> ()
+
+ and iter_ty_slots
+ (llbuilder:Llvm.llbuilder ref)
+ (ty:Ast.ty)
+ (ptr:Llvm.llvalue)
+ (f:Llvm.llvalue -> Ast.slot -> (Ast.ty_iso option) -> unit)
+ (curr_iso:Ast.ty_iso option)
+ : unit =
+ iter_ty_slots_full llbuilder ty ptr ptr
+ (fun _ src_ptr slot curr_iso -> f src_ptr slot curr_iso)
+ curr_iso
+
+ and drop_ty
+ (llbuilder:Llvm.llbuilder ref)
+ (lltask:Llvm.llvalue)
+ (ty:Ast.ty)
+ (ptr:Llvm.llvalue)
+ (curr_iso:Ast.ty_iso option)
+ : unit =
+ iter_ty_slots llbuilder ty ptr (drop_slot llbuilder lltask) curr_iso
+
+ and drop_slot
+ (llbuilder:Llvm.llbuilder ref)
+ (lltask:Llvm.llvalue)
+ (slot_ptr:Llvm.llvalue)
+ (slot:Ast.slot)
+ (curr_iso:Ast.ty_iso option)
+ : unit =
+
+ let llfn = Llvm.block_parent (Llvm.insertion_block (!llbuilder)) in
+ let llty = trans_slot None slot in
+ let ty = Semant.slot_ty slot in
+
+ let new_block klass =
+ let llblock = Llvm.append_block llctx (anon_llid klass) llfn in
+ let llbuilder = Llvm.builder_at_end llctx llblock in
+ (llblock, llbuilder)
+ in
+
+ let if_ptr_in_slot_not_null
+ (inner:Llvm.llvalue -> Llvm.llbuilder -> Llvm.llbuilder)
+ (llbuilder:Llvm.llbuilder)
+ : Llvm.llbuilder =
+ let ptr = Llvm.build_load slot_ptr (anon_llid "tmp") llbuilder in
+ let null = Llvm.const_pointer_null llty in
+ let test =
+ Llvm.build_icmp Llvm.Icmp.Ne null ptr (anon_llid "nullp") llbuilder
+ in
+ let (llthen, llthen_builder) = new_block "then" in
+ let (llnext, llnext_builder) = new_block "next" in
+ ignore (Llvm.build_cond_br test llthen llnext llbuilder);
+ let llthen_builder = inner ptr llthen_builder in
+ ignore (Llvm.build_br llnext llthen_builder);
+ llnext_builder
+ in
+
+ let decr_refcnt_and_if_zero
+ (rc_elt:int)
+ (inner:Llvm.llvalue -> Llvm.llbuilder -> Llvm.llbuilder)
+ (ptr:Llvm.llvalue)
+ (llbuilder:Llvm.llbuilder)
+ : Llvm.llbuilder =
+ let rc_ptr = get_element_ptr llbuilder ptr rc_elt in
+ let rc = Llvm.build_load rc_ptr (anon_llid "rc") llbuilder in
+ let rc = Llvm.build_sub rc (imm 1L) (anon_llid "tmp") llbuilder in
+ let _ = Llvm.build_store rc rc_ptr llbuilder in
+ log sem_cx "rc type: %s" (llval_str rc);
+ let test =
+ Llvm.build_icmp Llvm.Icmp.Eq
+ rc (imm 0L) (anon_llid "zerop") llbuilder
+ in
+ let (llthen, llthen_builder) = new_block "then" in
+ let (llnext, llnext_builder) = new_block "next" in
+ ignore (Llvm.build_cond_br test llthen llnext llbuilder);
+ let llthen_builder = inner ptr llthen_builder in
+ ignore (Llvm.build_br llnext llthen_builder);
+ llnext_builder
+ in
+
+ let free_and_null_out_slot
+ (ptr:Llvm.llvalue)
+ (llbuilder:Llvm.llbuilder)
+ : Llvm.llbuilder =
+ free_ty llbuilder lltask ty ptr;
+ let null = Llvm.const_pointer_null llty in
+ ignore (Llvm.build_store null slot_ptr llbuilder);
+ llbuilder
+ in
+
+ begin
+ match slot_mem_ctrl slot with
+ MEM_rc_struct
+ | MEM_gc ->
+ llbuilder :=
+ if_ptr_in_slot_not_null
+ (decr_refcnt_and_if_zero
+ Abi.exterior_rc_slot_field_refcnt
+ free_and_null_out_slot)
+ (!llbuilder)
+
+ | MEM_rc_opaque ->
+ llbuilder :=
+ if_ptr_in_slot_not_null
+ (decr_refcnt_and_if_zero
+ Abi.exterior_rc_slot_field_refcnt
+ free_and_null_out_slot)
+ (!llbuilder)
+
+ | MEM_interior when Semant.type_is_structured ty ->
+ (* FIXME: to handle recursive types, need to call drop
+ glue here, not inline. *)
+ drop_ty llbuilder lltask ty slot_ptr curr_iso
+
+ | _ -> ()
+ end
+ in
+
+ let (llitems:(node_id, Llvm.llvalue) Hashtbl.t) = Hashtbl.create 0 in
+ let declare_mod_item
+ (name:Ast.ident)
+ { node = { Ast.decl_item = (item:Ast.mod_item') }; id = id }
+ : unit =
+ let full_name = Semant.item_str sem_cx id in
+ let line_num =
+ match Session.get_span sess id with
+ None -> 0
+ | Some span ->
+ let (_, line, _) = span.lo in
+ line
+ in
+ match item with
+ Ast.MOD_ITEM_fn _ ->
+ let llty = trans_ty (ty_of_item id) in
+ let llfn = Llvm.declare_function ("_rust_" ^ name) llty llmod in
+ let meta =
+ md_node
+ [|
+ const_dw_tag Dwarf.DW_TAG_subprogram;
+ const_i32 0; (* unused *)
+ const_i32 0; (* context metadata llvalue *)
+ md_str name;
+ md_str full_name;
+ md_str full_name;
+ const_i32 0; (* file metadata llvalue *)
+ const_i32 line_num;
+ const_i32 0; (* type descriptor metadata llvalue *)
+ const_i1 1; (* flag: local to compile unit? *)
+ const_i1 1; (* flag: defined in compile unit? *)
+ |]
+ in
+ Llvm.set_function_call_conv Llvm.CallConv.c llfn;
+ Hashtbl.add llitems id llfn;
+
+ (* FIXME: Adding metadata does not work yet. . *)
+ let _ = fun _ -> set_dbg_metadata llfn meta in
+ ()
+
+ | _ -> () (* TODO *)
+ in
+
+ let trans_fn
+ ({
+ Ast.fn_input_slots = (header_slots:Ast.header_slots);
+ Ast.fn_body = (body:Ast.block)
+ }:Ast.fn)
+ (fn_id:node_id)
+ : unit =
+ let llfn = Hashtbl.find llitems fn_id in
+ let lloutptr = Llvm.param llfn 0 in
+ let lltask = Llvm.param llfn 1 in
+
+ (* LLVM requires that functions be grouped into basic blocks terminated by
+ * terminator instructions, while our AST is less strict. So we have to do
+ * a little trickery here to wrangle the statement sequence into LLVM's
+ * format. *)
+
+ let new_block id_opt klass =
+ let llblock = Llvm.append_block llctx (node_llid id_opt klass) llfn in
+ let llbuilder = Llvm.builder_at_end llctx llblock in
+ (llblock, llbuilder)
+ in
+
+ (* Build up the slot-to-llvalue mapping, allocating space along the
+ * way. *)
+ let slot_to_llvalue = Hashtbl.create 0 in
+ let (_, llinitbuilder) = new_block None "init" in
+
+ (* Allocate space for arguments (needed because arguments are lvalues in
+ * Rust), and store them in the slot-to-llvalue mapping. *)
+ let n_implicit_args = 2 in
+ let build_arg idx llargval =
+ if idx >= n_implicit_args
+ then
+ let ({ id = id }, ident) = header_slots.(idx - 2) in
+ Llvm.set_value_name ident llargval;
+ let llarg =
+ let llty = Llvm.type_of llargval in
+ Llvm.build_alloca llty (ident ^ "_ptr") llinitbuilder
+ in
+ ignore (Llvm.build_store llargval llarg llinitbuilder);
+ Hashtbl.add slot_to_llvalue id llarg
+ in
+ Array.iteri build_arg (Llvm.params llfn);
+
+ (* Allocate space for all the blocks' slots.
+ * and zero the exteriors. *)
+ let init_block (block_id:node_id) : unit =
+ let init_slot
+ (key:Ast.slot_key)
+ (slot_id:node_id)
+ (slot:Ast.slot)
+ : unit =
+ let name = Ast.sprintf_slot_key () key in
+ let llty = trans_slot (Some slot_id) slot in
+ let llptr = Llvm.build_alloca llty name llinitbuilder in
+ begin
+ match slot_mem_ctrl slot with
+ MEM_rc_struct
+ | MEM_rc_opaque
+ | MEM_gc ->
+ ignore (Llvm.build_store
+ (Llvm.const_pointer_null llty)
+ llptr llinitbuilder);
+ | _ -> ()
+ end;
+ Hashtbl.add slot_to_llvalue slot_id llptr
+ in
+ iter_block_slots sem_cx block_id init_slot
+ in
+
+ let exit_block
+ (llbuilder:Llvm.llbuilder)
+ (block_id:node_id)
+ : Llvm.llbuilder =
+ let r = ref llbuilder in
+ iter_block_slots sem_cx block_id
+ begin
+ fun _ slot_id slot ->
+ if (not (Semant.slot_is_obj_state sem_cx slot_id))
+ then
+ let ptr = Hashtbl.find slot_to_llvalue slot_id in
+ drop_slot r lltask ptr slot None
+ end;
+ !r
+ in
+
+ List.iter init_block (Hashtbl.find sem_cx.Semant.ctxt_frame_blocks fn_id);
+
+ let static_str (s:string) : Llvm.llvalue =
+ Llvm.define_global (anon_llid "str") (Llvm.const_stringz llctx s) llmod
+ in
+
+
+ (* Translates a list of AST statements to a sequence of LLVM instructions.
+ * The supplied "terminate" function appends the appropriate terminator
+ * instruction to the instruction stream. It may or may not be called,
+ * depending on whether the AST contains a terminating instruction
+ * explicitly. *)
+ let rec trans_stmts
+ (block_id:node_id)
+ (llbuilder:Llvm.llbuilder)
+ (stmts:Ast.stmt list)
+ (terminate:(Llvm.llbuilder -> node_id -> unit))
+ : unit =
+ let trans_literal
+ (lit:Ast.lit)
+ : Llvm.llvalue =
+ match lit with
+ Ast.LIT_nil -> llnil
+ | Ast.LIT_bool value ->
+ Llvm.const_int (Llvm.i1_type llctx) (if value then 1 else 0)
+ | Ast.LIT_mach (mty, value, _) ->
+ let llty = trans_mach_ty mty in
+ Llvm.const_of_int64 llty value (mach_is_signed mty)
+ | Ast.LIT_int (value, _) ->
+ Llvm.const_of_int64 (Llvm.i32_type llctx) value true
+ | Ast.LIT_uint (value, _) ->
+ Llvm.const_of_int64 (Llvm.i32_type llctx) value false
+ | Ast.LIT_char ch ->
+ Llvm.const_int (Llvm.i32_type llctx) ch
+ in
+
+ (* Translates an lval by reference into the appropriate pointer
+ * value. *)
+ let trans_lval (lval:Ast.lval) : Llvm.llvalue =
+ iflog (fun _ -> log sem_cx "trans_lval: %a" Ast.sprintf_lval lval);
+ match lval with
+ Ast.LVAL_base { id = base_id } ->
+ let id =
+ Hashtbl.find sem_cx.Semant.ctxt_lval_to_referent base_id
+ in
+ let referent = Hashtbl.find sem_cx.Semant.ctxt_all_defns id in
+ begin
+ match referent with
+ Semant.DEFN_slot _ -> Hashtbl.find slot_to_llvalue id
+ | Semant.DEFN_item _ -> Hashtbl.find llitems id
+ | _ -> bogus_ptr (* TODO *)
+ end
+ | Ast.LVAL_ext _ -> bogus_ptr (* TODO *)
+ in
+
+ let trans_atom (atom:Ast.atom) : Llvm.llvalue =
+ iflog (fun _ -> log sem_cx "trans_atom: %a" Ast.sprintf_atom atom);
+ match atom with
+ Ast.ATOM_literal { node = lit } -> trans_literal lit
+ | Ast.ATOM_lval lval ->
+ Llvm.build_load (trans_lval lval) (anon_llid "tmp") llbuilder
+ in
+
+ let trans_binary_expr
+ ((op:Ast.binop), (lhs:Ast.atom), (rhs:Ast.atom))
+ : Llvm.llvalue =
+ (* Evaluate the operands in the proper order. *)
+ let (lllhs, llrhs) =
+ match op with
+ Ast.BINOP_or | Ast.BINOP_and | Ast.BINOP_eq | Ast.BINOP_ne
+ | Ast.BINOP_lt | Ast.BINOP_le | Ast.BINOP_ge | Ast.BINOP_gt
+ | Ast.BINOP_lsl | Ast.BINOP_lsr | Ast.BINOP_asr
+ | Ast.BINOP_add | Ast.BINOP_sub | Ast.BINOP_mul
+ | Ast.BINOP_div | Ast.BINOP_mod | Ast.BINOP_xor ->
+ (trans_atom lhs, trans_atom rhs)
+ | Ast.BINOP_send ->
+ let llrhs = trans_atom rhs in
+ let lllhs = trans_atom lhs in
+ (lllhs, llrhs)
+ in
+ let llid = anon_llid "expr" in
+ match op with
+ Ast.BINOP_eq ->
+ (* TODO: equality works on more than just integers *)
+ Llvm.build_icmp Llvm.Icmp.Eq lllhs llrhs llid llbuilder
+
+ (* TODO: signed/unsigned distinction, floating point *)
+ | Ast.BINOP_add -> Llvm.build_add lllhs llrhs llid llbuilder
+ | Ast.BINOP_sub -> Llvm.build_sub lllhs llrhs llid llbuilder
+ | Ast.BINOP_mul -> Llvm.build_mul lllhs llrhs llid llbuilder
+ | Ast.BINOP_div -> Llvm.build_sdiv lllhs llrhs llid llbuilder
+ | Ast.BINOP_mod -> Llvm.build_srem lllhs llrhs llid llbuilder
+
+ | _ -> bogus (* TODO *)
+ in
+
+ let trans_unary_expr _ = bogus in (* TODO *)
+
+ let trans_expr (expr:Ast.expr) : Llvm.llvalue =
+ iflog (fun _ -> log sem_cx "trans_expr: %a" Ast.sprintf_expr expr);
+ match expr with
+ Ast.EXPR_binary binexp -> trans_binary_expr binexp
+ | Ast.EXPR_unary unexp -> trans_unary_expr unexp
+ | Ast.EXPR_atom atom -> trans_atom atom
+ in
+
+ let trans_log_str (atom:Ast.atom) : unit =
+ upcall llbuilder lltask "upcall_log_str" None [| trans_atom atom |]
+ in
+
+ let trans_log_int (atom:Ast.atom) : unit =
+ upcall llbuilder lltask "upcall_log_int" None [| trans_atom atom |]
+ in
+
+ let trans_fail
+ (llbuilder:Llvm.llbuilder)
+ (lltask:Llvm.llvalue)
+ (reason:string)
+ (stmt_id:node_id)
+ : unit =
+ let (file, line, _) =
+ match Session.get_span sem_cx.Semant.ctxt_sess stmt_id with
+ None -> ("<none>", 0, 0)
+ | Some sp -> sp.lo
+ in
+ upcall llbuilder lltask "upcall_fail" None [|
+ static_str reason;
+ static_str file;
+ Llvm.const_int (Llvm.i32_type llctx) line
+ |];
+ ignore (Llvm.build_unreachable llbuilder)
+ in
+
+ (* FIXME: this may be irrelevant; possibly LLVM will wind up
+ * using GOT and such wherever it needs to to achieve PIC
+ * data.
+ *)
+ (*
+ let crate_rel (v:Llvm.llvalue) : Llvm.llvalue =
+ let v_int = Llvm.const_pointercast v word_ty in
+ let c_int = Llvm.const_pointercast crate_ptr word_ty in
+ Llvm.const_sub v_int c_int
+ in
+ *)
+
+ match stmts with
+ [] -> terminate llbuilder block_id
+ | head::tail ->
+
+ iflog (fun _ ->
+ log sem_cx "trans_stmt: %a" Ast.sprintf_stmt head);
+
+ let trans_tail_with_builder llbuilder' : unit =
+ trans_stmts block_id llbuilder' tail terminate
+ in
+ let trans_tail () = trans_tail_with_builder llbuilder in
+
+ match head.node with
+ Ast.STMT_init_tup (dest, atoms) ->
+ let zero = const_i32 0 in
+ let lldest = trans_lval dest in
+ let trans_tup_atom idx (_, _, atom) =
+ let indices = [| zero; const_i32 idx |] in
+ let gep_id = anon_llid "init_tup_gep" in
+ let ptr =
+ Llvm.build_gep lldest indices gep_id llbuilder
+ in
+ ignore (Llvm.build_store (trans_atom atom) ptr llbuilder)
+ in
+ Array.iteri trans_tup_atom atoms;
+ trans_tail ()
+
+ | Ast.STMT_copy (dest, src) ->
+ let llsrc = trans_expr src in
+ let lldest = trans_lval dest in
+ ignore (Llvm.build_store llsrc lldest llbuilder);
+ trans_tail ()
+
+ | Ast.STMT_call (dest, fn, args) ->
+ let llargs = Array.map trans_atom args in
+ let lldest = trans_lval dest in
+ let llfn = trans_lval fn in
+ let llallargs = Array.append [| lldest; lltask |] llargs in
+ let llrv = build_call llfn llallargs "" llbuilder in
+ Llvm.set_instruction_call_conv Llvm.CallConv.c llrv;
+ trans_tail ()
+
+ | Ast.STMT_if sif ->
+ let llexpr = trans_expr sif.Ast.if_test in
+ let (llnext, llnextbuilder) = new_block None "next" in
+ let branch_to_next llbuilder' _ =
+ ignore (Llvm.build_br llnext llbuilder')
+ in
+ let llthen = trans_block sif.Ast.if_then branch_to_next in
+ let llelse =
+ match sif.Ast.if_else with
+ None -> llnext
+ | Some if_else -> trans_block if_else branch_to_next
+ in
+ ignore (Llvm.build_cond_br llexpr llthen llelse llbuilder);
+ trans_tail_with_builder llnextbuilder
+
+ | Ast.STMT_ret atom_opt ->
+ begin
+ match atom_opt with
+ None -> ()
+ | Some atom ->
+ ignore (Llvm.build_store (trans_atom atom)
+ lloutptr llbuilder)
+ end;
+ let llbuilder = exit_block llbuilder block_id in
+ ignore (Llvm.build_ret_void llbuilder)
+
+ | Ast.STMT_fail ->
+ trans_fail llbuilder lltask "explicit failure" head.id
+
+ | Ast.STMT_log a ->
+ begin
+ match Semant.atom_type sem_cx a with
+ (* NB: If you extend this, be sure to update the
+ * typechecking code in type.ml as well. *)
+ Ast.TY_str -> trans_log_str a
+ | Ast.TY_int | Ast.TY_uint | Ast.TY_bool | Ast.TY_char
+ | Ast.TY_mach (TY_u8) | Ast.TY_mach (TY_u16)
+ | Ast.TY_mach (TY_u32) | Ast.TY_mach (TY_i8)
+ | Ast.TY_mach (TY_i16) | Ast.TY_mach (TY_i32) ->
+ trans_log_int a
+ | _ -> Semant.bugi sem_cx head.id
+ "unimplemented logging type"
+ end;
+ trans_tail ()
+
+ | Ast.STMT_check_expr expr ->
+ let llexpr = trans_expr expr in
+ let (llfail, llfailbuilder) = new_block None "fail" in
+ let reason = Ast.fmt_to_str Ast.fmt_expr expr in
+ trans_fail llfailbuilder lltask reason head.id;
+ let (llok, llokbuilder) = new_block None "ok" in
+ ignore (Llvm.build_cond_br llexpr llok llfail llbuilder);
+ trans_tail_with_builder llokbuilder
+
+ | Ast.STMT_init_str (dst, str) ->
+ let d = trans_lval dst in
+ let s = static_str str in
+ let len =
+ Llvm.const_int word_ty ((String.length str) + 1)
+ in
+ upcall llbuilder lltask "upcall_new_str"
+ (Some d) [| s; len |];
+ trans_tail ()
+
+ | _ -> trans_stmts block_id llbuilder tail terminate
+
+ (*
+ * Translates an AST block to one or more LLVM basic blocks and returns
+ * the first basic block. The supplied callback is expected to add a
+ * terminator instruction.
+ *)
+
+ and trans_block
+ ({ node = (stmts:Ast.stmt array); id = id }:Ast.block)
+ (terminate:Llvm.llbuilder -> node_id -> unit)
+ : Llvm.llbasicblock =
+ let (llblock, llbuilder) = new_block (Some id) "bb" in
+ trans_stmts id llbuilder (Array.to_list stmts) terminate;
+ llblock
+ in
+
+ (* "Falling off the end" of a function needs to turn into an explicit
+ * return instruction. *)
+ let default_terminate llbuilder block_id =
+ let llbuilder = exit_block llbuilder block_id in
+ ignore (Llvm.build_ret_void llbuilder)
+ in
+
+ (* Build up the first body block, and link it to the end of the
+ * initialization block. *)
+ let llbodyblock = (trans_block body default_terminate) in
+ ignore (Llvm.build_br llbodyblock llinitbuilder)
+ in
+
+ let trans_mod_item
+ (_:Ast.ident)
+ { node = { Ast.decl_item = (item:Ast.mod_item') }; id = id }
+ : unit =
+ match item with
+ Ast.MOD_ITEM_fn fn -> trans_fn fn id
+ | _ -> ()
+ in
+
+ let exit_task_glue =
+ (* The exit-task glue does not get called.
+ *
+ * Rather, control arrives at it by *returning* to the first
+ * instruction of it, when control falls off the end of the task's
+ * root function.
+ *
+ * There is a "fake" frame set up by the runtime, underneath us,
+ * that we find ourselves in. This frame has the shape of a frame
+ * entered with 2 standard arguments (outptr + taskptr), then a
+ * retpc and N callee-saves sitting on the stack; all this is under
+ * ebp. Then there are 2 *outgoing* args at sp[0] and sp[1].
+ *
+ * All these are fake except the taskptr, which is the one bit we
+ * want. So we construct an equally fake cdecl llvm signature here
+ * to crudely *get* the taskptr that's sitting 2 words up from sp,
+ * and pass it to upcall_exit.
+ *
+ * The latter never returns.
+ *)
+ let llty = fn_ty void_ty [| task_ptr_ty |] in
+ let llfn = Llvm.declare_function "rust_exit_task_glue" llty llmod in
+ let lltask = Llvm.param llfn 0 in
+ let llblock = Llvm.append_block llctx "body" llfn in
+ let llbuilder = Llvm.builder_at_end llctx llblock in
+ trans_upcall llbuilder lltask "upcall_exit" None [||];
+ ignore (Llvm.build_ret_void llbuilder);
+ llfn
+ in
+
+ try
+ let crate' = crate.node in
+ let items = snd (crate'.Ast.crate_items) in
+ Hashtbl.iter declare_mod_item items;
+ Hashtbl.iter trans_mod_item items;
+ Llfinal.finalize_module
+ llctx llmod abi asm_glue exit_task_glue crate_ptr;
+ llmod
+ with e -> Llvm.dispose_module llmod; raise e
+;;
+
+(*
+ * Local Variables:
+ * fill-column: 78;
+ * indent-tabs-mode: nil
+ * buffer-file-coding-system: utf-8-unix
+ * compile-command: "make -k -C ../.. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
+ * End:
+ *)
+
diff --git a/src/boot/me/alias.ml b/src/boot/me/alias.ml
new file mode 100644
index 00000000..7009fe10
--- /dev/null
+++ b/src/boot/me/alias.ml
@@ -0,0 +1,134 @@
+open Semant;;
+open Common;;
+
+let log cx = Session.log "alias"
+ cx.ctxt_sess.Session.sess_log_alias
+ cx.ctxt_sess.Session.sess_log_out
+;;
+
+let alias_analysis_visitor
+ (cx:ctxt)
+ (inner:Walk.visitor)
+ : Walk.visitor =
+ let curr_stmt = Stack.create () in
+
+ let alias_slot (slot_id:node_id) : unit =
+ begin
+ log cx "noting slot #%d as aliased" (int_of_node slot_id);
+ Hashtbl.replace cx.ctxt_slot_aliased slot_id ()
+ end
+ in
+
+ let alias lval =
+ match lval with
+ Ast.LVAL_base nb ->
+ let referent = Hashtbl.find cx.ctxt_lval_to_referent nb.id in
+ if (referent_is_slot cx referent)
+ then alias_slot referent
+ | _ -> err None "unhandled form of lval %a in alias analysis"
+ Ast.sprintf_lval lval
+ in
+
+ let alias_atom at =
+ match at with
+ Ast.ATOM_lval lv -> alias lv
+ | _ -> err None "aliasing literal"
+ in
+
+ let alias_call_args dst callee args =
+ alias dst;
+ let callee_ty = lval_ty cx callee in
+ match callee_ty with
+ Ast.TY_fn (tsig,_) ->
+ Array.iteri
+ begin
+ fun i slot ->
+ match slot.Ast.slot_mode with
+ Ast.MODE_alias _ ->
+ alias_atom args.(i)
+ | _ -> ()
+ end
+ tsig.Ast.sig_input_slots
+ | _ -> ()
+ in
+
+ let visit_stmt_pre s =
+ Stack.push s.id curr_stmt;
+ begin
+ try
+ match s.node with
+ (* FIXME (issue #26): actually all these *existing* cases
+ * can probably go now that we're using Trans.aliasing to
+ * form short-term spill-based aliases. Only aliases that
+ * 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)
+ -> alias_call_args dst callee args
+
+ | Ast.STMT_send (_, src) -> alias src
+ | Ast.STMT_recv (dst, _) -> alias dst
+ | Ast.STMT_init_port (dst) -> alias dst
+ | Ast.STMT_init_chan (dst, _) -> alias dst
+ | Ast.STMT_init_vec (dst, _, _) -> alias dst
+ | Ast.STMT_init_str (dst, _) -> alias dst
+ | Ast.STMT_for_each sfe ->
+ let (slot, _) = sfe.Ast.for_each_slot in
+ alias_slot slot.id
+ | _ -> () (* FIXME (issue #29): plenty more to handle here. *)
+ with
+ Semant_err (None, msg) ->
+ raise (Semant_err ((Some s.id), msg))
+ end;
+ inner.Walk.visit_stmt_pre s
+ in
+ let visit_stmt_post s =
+ inner.Walk.visit_stmt_post s;
+ ignore (Stack.pop curr_stmt);
+ in
+
+ let visit_lval_pre lv =
+ let slot_id = lval_to_referent cx (lval_base_id lv) in
+ if (not (Stack.is_empty curr_stmt)) && (referent_is_slot cx slot_id)
+ then
+ begin
+ let slot_depth = get_slot_depth cx slot_id in
+ let stmt_depth = get_stmt_depth cx (Stack.top curr_stmt) in
+ if slot_depth <> stmt_depth
+ then
+ begin
+ let _ = assert (slot_depth < stmt_depth) in
+ alias_slot slot_id
+ end
+ end
+ in
+
+ { inner with
+ Walk.visit_stmt_pre = visit_stmt_pre;
+ Walk.visit_stmt_post = visit_stmt_post;
+ Walk.visit_lval_pre = visit_lval_pre
+ }
+;;
+
+let process_crate
+ (cx:ctxt)
+ (crate:Ast.crate)
+ : unit =
+ let path = Stack.create () in
+ let passes =
+ [|
+ (alias_analysis_visitor cx
+ Walk.empty_visitor);
+ |]
+ in
+ run_passes cx "alias" path passes (log cx "%s") crate
+;;
+
+(*
+ * 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/me/dead.ml b/src/boot/me/dead.ml
new file mode 100644
index 00000000..47e56166
--- /dev/null
+++ b/src/boot/me/dead.ml
@@ -0,0 +1,121 @@
+(*
+ * A simple dead-code analysis that rejects code following unconditional
+ * 'ret' or 'be'.
+ *)
+
+open Semant;;
+open Common;;
+
+let log cx = Session.log "dead"
+ cx.ctxt_sess.Session.sess_log_dead
+ cx.ctxt_sess.Session.sess_log_out
+;;
+
+let dead_code_visitor
+ ((*cx*)_:ctxt)
+ (inner:Walk.visitor)
+ : Walk.visitor =
+
+ (* FIXME: create separate table for each fn body for less garbage *)
+ let must_exit = Hashtbl.create 100 in
+
+ let all_must_exit ids =
+ arr_for_all (fun _ id -> Hashtbl.mem must_exit id) ids
+ in
+
+ let visit_block_post block =
+ let stmts = block.node in
+ let len = Array.length stmts in
+ if len > 0 then
+ Array.iteri
+ begin
+ fun i s ->
+ if (i < (len - 1)) && (Hashtbl.mem must_exit s.id) then
+ err (Some stmts.(i + 1).id) "dead statement"
+ end
+ stmts;
+ inner.Walk.visit_block_post block
+ in
+
+ let visit_stmt_post s =
+ begin
+ match s.node with
+ | Ast.STMT_block block ->
+ if Hashtbl.mem must_exit block.id then
+ Hashtbl.add must_exit s.id ()
+
+ | Ast.STMT_while { Ast.while_body = body }
+ | Ast.STMT_do_while { Ast.while_body = body }
+ | Ast.STMT_for_each { Ast.for_each_body = body }
+ | Ast.STMT_for { Ast.for_body = body } ->
+ if (Hashtbl.mem must_exit body.id) then
+ Hashtbl.add must_exit s.id ()
+
+ | Ast.STMT_if { Ast.if_then = b1; Ast.if_else = Some b2 } ->
+ if (Hashtbl.mem must_exit b1.id) && (Hashtbl.mem must_exit b2.id)
+ then Hashtbl.add must_exit s.id ()
+
+ | Ast.STMT_if _ -> ()
+
+ | Ast.STMT_ret _
+ | Ast.STMT_be _ ->
+ Hashtbl.add must_exit s.id ()
+
+ | Ast.STMT_alt_tag { Ast.alt_tag_arms = arms } ->
+ let arm_ids =
+ Array.map (fun { node = (_, block) } -> block.id) arms
+ in
+ if all_must_exit arm_ids
+ then Hashtbl.add must_exit s.id ()
+
+ | Ast.STMT_alt_type { Ast.alt_type_arms = arms;
+ Ast.alt_type_else = alt_type_else } ->
+ let arm_ids = Array.map (fun (_, _, block) -> block.id) arms in
+ let else_ids =
+ begin
+ match alt_type_else with
+ Some stmt -> [| stmt.id |]
+ | None -> [| |]
+ end
+ in
+ if all_must_exit (Array.append arm_ids else_ids) then
+ Hashtbl.add must_exit s.id ()
+
+ (* FIXME: figure this one out *)
+ | Ast.STMT_alt_port _ -> ()
+
+ | _ -> ()
+ end;
+ inner.Walk.visit_stmt_post s
+
+ in
+ { inner with
+ Walk.visit_block_post = visit_block_post;
+ Walk.visit_stmt_post = visit_stmt_post }
+;;
+
+let process_crate
+ (cx:ctxt)
+ (crate:Ast.crate)
+ : unit =
+ let path = Stack.create () in
+ let passes =
+ [|
+ (dead_code_visitor cx
+ Walk.empty_visitor)
+ |]
+ in
+
+ run_passes cx "dead" path passes (log cx "%s") crate;
+ ()
+;;
+
+
+(*
+ * 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/me/dwarf.ml b/src/boot/me/dwarf.ml
new file mode 100644
index 00000000..9423d4ee
--- /dev/null
+++ b/src/boot/me/dwarf.ml
@@ -0,0 +1,3019 @@
+(*
+ * Walk crate and generate DWARF-3 records. This file might also go in
+ * the be/ directory; it's half-middle-end, half-back-end. Debug info is
+ * like that.
+ *
+ * Some notes about DWARF:
+ *
+ * - Records form an ownership tree. The tree is serialized in
+ * depth-first pre-order with child lists ending with null
+ * records. When a node type is defined to have no children, no null
+ * child record is provided; it's implied.
+ *
+ * [parent]
+ * / \
+ * [child1] [child2]
+ * |
+ * [grandchild1]
+ *
+ * serializes as:
+ *
+ * [parent][child1][grandchild1][null][child2][null][null]
+ *
+ * - Sometimes you want to make it possible to scan through a sibling
+ * list quickly while skipping the sub-children of each (such as
+ * skipping the 'grandchild' above); this can be done with a
+ * DW_AT_sibling attribute that points forward to the next same-level
+ * sibling.
+ *
+ * - A DWARF consumer contains a little stack-machine interpreter for
+ * a micro-language that you can embed in DWARF records to compute
+ * values algorithmically.
+ *
+ * - DWARF is not "officially" supported by any Microsoft tools in
+ * PE files, but the Microsoft debugging information formats are
+ * proprietary and ever-shifting, and not clearly sufficient for
+ * our needs; by comparison DWARF is widely supported, stable,
+ * flexible, and required everywhere *else*. We are using DWARF to
+ * support major components of the rust runtime (reflection,
+ * unwinding, profiling) so it's helpful to not have to span
+ * technologies, just focus on DWARF. Luckily the MINGW/Cygwin
+ * communities have worked out a convention for PE, and taught BFD
+ * (thus most tools) how to digest DWARF sections trailing after
+ * the .idata section of a normal PE file. Seems to work fine.
+ *
+ * - DWARF supports variable-length coding using LEB128, and in the
+ * cases where these are symbolic or self-contained numbers, we
+ * support them in the assembler. Inter-DWARF-record references
+ * can be done via fixed-size DW_FORM_ref{1,2,4,8} or
+ * DW_FORM_ref_addr; or else via variable-size (LEB128)
+ * DW_FORM_ref_udata. It is hazardous to use the LEB128 form in
+ * our implementation of references, since we use a generic 2-pass
+ * (+ relaxation) fixup mechanism in our assembler which in
+ * general may present an information-dependency cycle for LEB128
+ * coding of offsets: you need to know the offset before you can
+ * work out the LEB128 size, and you may need to know several
+ * LEB128-sizes before you can work out the offsets of other
+ * LEB128s (possibly even the one you're currently coding). In
+ * general the assembler makes no attempt to resolve such
+ * cycles. It'll just throw if it can't handle what you ask
+ * for. So it's best to pay a little extra space and use
+ * DW_FORM_ref_addr or DW_FORM_ref{1,2,4,8} values, in all cases.
+ *)
+
+open Semant;;
+open Common;;
+open Asm;;
+
+let log cx = Session.log "dwarf"
+ cx.ctxt_sess.Session.sess_log_dwarf
+ cx.ctxt_sess.Session.sess_log_out
+;;
+
+type dw_tag =
+ DW_TAG_array_type
+ | DW_TAG_class_type
+ | DW_TAG_entry_point
+ | DW_TAG_enumeration_type
+ | DW_TAG_formal_parameter
+ | DW_TAG_imported_declaration
+ | DW_TAG_label
+ | DW_TAG_lexical_block
+ | DW_TAG_member
+ | DW_TAG_pointer_type
+ | DW_TAG_reference_type
+ | DW_TAG_compile_unit
+ | DW_TAG_string_type
+ | DW_TAG_structure_type
+ | DW_TAG_subroutine_type
+ | DW_TAG_typedef
+ | DW_TAG_union_type
+ | DW_TAG_unspecified_parameters
+ | DW_TAG_variant
+ | DW_TAG_common_block
+ | DW_TAG_common_inclusion
+ | DW_TAG_inheritance
+ | DW_TAG_inlined_subroutine
+ | DW_TAG_module
+ | DW_TAG_ptr_to_member_type
+ | DW_TAG_set_type
+ | DW_TAG_subrange_type
+ | DW_TAG_with_stmt
+ | DW_TAG_access_declaration
+ | DW_TAG_base_type
+ | DW_TAG_catch_block
+ | DW_TAG_const_type
+ | DW_TAG_constant
+ | DW_TAG_enumerator
+ | DW_TAG_file_type
+ | DW_TAG_friend
+ | DW_TAG_namelist
+ | DW_TAG_namelist_item
+ | DW_TAG_packed_type
+ | DW_TAG_subprogram
+ | DW_TAG_template_type_parameter
+ | DW_TAG_template_value_parameter
+ | DW_TAG_thrown_type
+ | DW_TAG_try_block
+ | DW_TAG_variant_part
+ | DW_TAG_variable
+ | DW_TAG_volatile_type
+ | DW_TAG_dwarf_procedure
+ | DW_TAG_restrict_type
+ | DW_TAG_interface_type
+ | DW_TAG_namespace
+ | DW_TAG_imported_module
+ | DW_TAG_unspecified_type
+ | DW_TAG_partial_unit
+ | DW_TAG_imported_unit
+ | DW_TAG_condition
+ | DW_TAG_shared_type
+ | DW_TAG_lo_user
+ | DW_TAG_rust_meta
+ | DW_TAG_hi_user
+;;
+
+
+let dw_tag_to_int (tag:dw_tag) : int =
+ match tag with
+ DW_TAG_array_type -> 0x01
+ | DW_TAG_class_type -> 0x02
+ | DW_TAG_entry_point -> 0x03
+ | DW_TAG_enumeration_type -> 0x04
+ | DW_TAG_formal_parameter -> 0x05
+ | DW_TAG_imported_declaration -> 0x08
+ | DW_TAG_label -> 0x0a
+ | DW_TAG_lexical_block -> 0x0b
+ | DW_TAG_member -> 0x0d
+ | DW_TAG_pointer_type -> 0x0f
+ | DW_TAG_reference_type -> 0x10
+ | DW_TAG_compile_unit -> 0x11
+ | DW_TAG_string_type -> 0x12
+ | DW_TAG_structure_type -> 0x13
+ | DW_TAG_subroutine_type -> 0x15
+ | DW_TAG_typedef -> 0x16
+ | DW_TAG_union_type -> 0x17
+ | DW_TAG_unspecified_parameters -> 0x18
+ | DW_TAG_variant -> 0x19
+ | DW_TAG_common_block -> 0x1a
+ | DW_TAG_common_inclusion -> 0x1b
+ | DW_TAG_inheritance -> 0x1c
+ | DW_TAG_inlined_subroutine -> 0x1d
+ | DW_TAG_module -> 0x1e
+ | DW_TAG_ptr_to_member_type -> 0x1f
+ | DW_TAG_set_type -> 0x20
+ | DW_TAG_subrange_type -> 0x21
+ | DW_TAG_with_stmt -> 0x22
+ | DW_TAG_access_declaration -> 0x23
+ | DW_TAG_base_type -> 0x24
+ | DW_TAG_catch_block -> 0x25
+ | DW_TAG_const_type -> 0x26
+ | DW_TAG_constant -> 0x27
+ | DW_TAG_enumerator -> 0x28
+ | DW_TAG_file_type -> 0x29
+ | DW_TAG_friend -> 0x2a
+ | DW_TAG_namelist -> 0x2b
+ | DW_TAG_namelist_item -> 0x2c
+ | DW_TAG_packed_type -> 0x2d
+ | DW_TAG_subprogram -> 0x2e
+ | DW_TAG_template_type_parameter -> 0x2f
+ | DW_TAG_template_value_parameter -> 0x30
+ | DW_TAG_thrown_type -> 0x31
+ | DW_TAG_try_block -> 0x32
+ | DW_TAG_variant_part -> 0x33
+ | DW_TAG_variable -> 0x34
+ | DW_TAG_volatile_type -> 0x35
+ | DW_TAG_dwarf_procedure -> 0x36
+ | DW_TAG_restrict_type -> 0x37
+ | DW_TAG_interface_type -> 0x38
+ | DW_TAG_namespace -> 0x39
+ | DW_TAG_imported_module -> 0x3a
+ | DW_TAG_unspecified_type -> 0x3b
+ | DW_TAG_partial_unit -> 0x3c
+ | DW_TAG_imported_unit -> 0x3d
+ | DW_TAG_condition -> 0x3f
+ | DW_TAG_shared_type -> 0x40
+ | DW_TAG_lo_user -> 0x4080
+ | DW_TAG_rust_meta -> 0x4300
+ | DW_TAG_hi_user -> 0xffff
+;;
+
+let dw_tag_of_int (i:int) : dw_tag =
+ match i with
+ 0x01 -> DW_TAG_array_type
+ | 0x02 -> DW_TAG_class_type
+ | 0x03 -> DW_TAG_entry_point
+ | 0x04 -> DW_TAG_enumeration_type
+ | 0x05 -> DW_TAG_formal_parameter
+ | 0x08 -> DW_TAG_imported_declaration
+ | 0x0a -> DW_TAG_label
+ | 0x0b -> DW_TAG_lexical_block
+ | 0x0d -> DW_TAG_member
+ | 0x0f -> DW_TAG_pointer_type
+ | 0x10 -> DW_TAG_reference_type
+ | 0x11 -> DW_TAG_compile_unit
+ | 0x12 -> DW_TAG_string_type
+ | 0x13 -> DW_TAG_structure_type
+ | 0x15 -> DW_TAG_subroutine_type
+ | 0x16 -> DW_TAG_typedef
+ | 0x17 -> DW_TAG_union_type
+ | 0x18 -> DW_TAG_unspecified_parameters
+ | 0x19 -> DW_TAG_variant
+ | 0x1a -> DW_TAG_common_block
+ | 0x1b -> DW_TAG_common_inclusion
+ | 0x1c -> DW_TAG_inheritance
+ | 0x1d -> DW_TAG_inlined_subroutine
+ | 0x1e -> DW_TAG_module
+ | 0x1f -> DW_TAG_ptr_to_member_type
+ | 0x20 -> DW_TAG_set_type
+ | 0x21 -> DW_TAG_subrange_type
+ | 0x22 -> DW_TAG_with_stmt
+ | 0x23 -> DW_TAG_access_declaration
+ | 0x24 -> DW_TAG_base_type
+ | 0x25 -> DW_TAG_catch_block
+ | 0x26 -> DW_TAG_const_type
+ | 0x27 -> DW_TAG_constant
+ | 0x28 -> DW_TAG_enumerator
+ | 0x29 -> DW_TAG_file_type
+ | 0x2a -> DW_TAG_friend
+ | 0x2b -> DW_TAG_namelist
+ | 0x2c -> DW_TAG_namelist_item
+ | 0x2d -> DW_TAG_packed_type
+ | 0x2e -> DW_TAG_subprogram
+ | 0x2f -> DW_TAG_template_type_parameter
+ | 0x30 -> DW_TAG_template_value_parameter
+ | 0x31 -> DW_TAG_thrown_type
+ | 0x32 -> DW_TAG_try_block
+ | 0x33 -> DW_TAG_variant_part
+ | 0x34 -> DW_TAG_variable
+ | 0x35 -> DW_TAG_volatile_type
+ | 0x36 -> DW_TAG_dwarf_procedure
+ | 0x37 -> DW_TAG_restrict_type
+ | 0x38 -> DW_TAG_interface_type
+ | 0x39 -> DW_TAG_namespace
+ | 0x3a -> DW_TAG_imported_module
+ | 0x3b -> DW_TAG_unspecified_type
+ | 0x3c -> DW_TAG_partial_unit
+ | 0x3d -> DW_TAG_imported_unit
+ | 0x3f -> DW_TAG_condition
+ | 0x40 -> DW_TAG_shared_type
+ | 0x4080 -> DW_TAG_lo_user
+ | 0x4300 -> DW_TAG_rust_meta
+ | 0xffff -> DW_TAG_hi_user
+ | _ -> bug () "bad DWARF tag code: %d" i
+;;
+
+
+let dw_tag_to_string (tag:dw_tag) : string =
+ match tag with
+ DW_TAG_array_type -> "DW_TAG_array_type"
+ | DW_TAG_class_type -> "DW_TAG_class_type"
+ | DW_TAG_entry_point -> "DW_TAG_entry_point"
+ | DW_TAG_enumeration_type -> "DW_TAG_enumeration_type"
+ | DW_TAG_formal_parameter -> "DW_TAG_formal_parameter"
+ | DW_TAG_imported_declaration -> "DW_TAG_imported_declaration"
+ | DW_TAG_label -> "DW_TAG_label"
+ | DW_TAG_lexical_block -> "DW_TAG_lexical_block"
+ | DW_TAG_member -> "DW_TAG_member"
+ | DW_TAG_pointer_type -> "DW_TAG_pointer_type"
+ | DW_TAG_reference_type -> "DW_TAG_reference_type"
+ | DW_TAG_compile_unit -> "DW_TAG_compile_unit"
+ | DW_TAG_string_type -> "DW_TAG_string_type"
+ | DW_TAG_structure_type -> "DW_TAG_structure_type"
+ | DW_TAG_subroutine_type -> "DW_TAG_subroutine_type"
+ | DW_TAG_typedef -> "DW_TAG_typedef"
+ | DW_TAG_union_type -> "DW_TAG_union_type"
+ | DW_TAG_unspecified_parameters -> "DW_TAG_unspecified_parameters"
+ | DW_TAG_variant -> "DW_TAG_variant"
+ | DW_TAG_common_block -> "DW_TAG_common_block"
+ | DW_TAG_common_inclusion -> "DW_TAG_common_inclusion"
+ | DW_TAG_inheritance -> "DW_TAG_inheritance"
+ | DW_TAG_inlined_subroutine -> "DW_TAG_inlined_subroutine"
+ | DW_TAG_module -> "DW_TAG_module"
+ | DW_TAG_ptr_to_member_type -> "DW_TAG_ptr_to_member_type"
+ | DW_TAG_set_type -> "DW_TAG_set_type"
+ | DW_TAG_subrange_type -> "DW_TAG_subrange_type"
+ | DW_TAG_with_stmt -> "DW_TAG_with_stmt"
+ | DW_TAG_access_declaration -> "DW_TAG_access_declaration"
+ | DW_TAG_base_type -> "DW_TAG_base_type"
+ | DW_TAG_catch_block -> "DW_TAG_catch_block"
+ | DW_TAG_const_type -> "DW_TAG_const_type"
+ | DW_TAG_constant -> "DW_TAG_constant"
+ | DW_TAG_enumerator -> "DW_TAG_enumerator"
+ | DW_TAG_file_type -> "DW_TAG_file_type"
+ | DW_TAG_friend -> "DW_TAG_friend"
+ | DW_TAG_namelist -> "DW_TAG_namelist"
+ | DW_TAG_namelist_item -> "DW_TAG_namelist_item"
+ | DW_TAG_packed_type -> "DW_TAG_packed_type"
+ | DW_TAG_subprogram -> "DW_TAG_subprogram"
+ | DW_TAG_template_type_parameter -> "DW_TAG_template_type_parameter"
+ | DW_TAG_template_value_parameter -> "DW_TAG_template_value_parameter"
+ | DW_TAG_thrown_type -> "DW_TAG_thrown_type"
+ | DW_TAG_try_block -> "DW_TAG_try_block"
+ | DW_TAG_variant_part -> "DW_TAG_variant_part"
+ | DW_TAG_variable -> "DW_TAG_variable"
+ | DW_TAG_volatile_type -> "DW_TAG_volatile_type"
+ | DW_TAG_dwarf_procedure -> "DW_TAG_dwarf_procedure"
+ | DW_TAG_restrict_type -> "DW_TAG_restrict_type"
+ | DW_TAG_interface_type -> "DW_TAG_interface_type"
+ | DW_TAG_namespace -> "DW_TAG_namespace"
+ | DW_TAG_imported_module -> "DW_TAG_imported_module"
+ | DW_TAG_unspecified_type -> "DW_TAG_unspecified_type"
+ | DW_TAG_partial_unit -> "DW_TAG_partial_unit"
+ | DW_TAG_imported_unit -> "DW_TAG_imported_unit"
+ | DW_TAG_condition -> "DW_TAG_condition"
+ | DW_TAG_shared_type -> "DW_TAG_shared_type"
+ | DW_TAG_lo_user -> "DW_TAG_lo_user"
+ | DW_TAG_rust_meta -> "DW_TAG_rust_meta"
+ | DW_TAG_hi_user -> "DW_TAG_hi_user"
+;;
+
+
+type dw_children =
+ DW_CHILDREN_no
+ | DW_CHILDREN_yes
+;;
+
+
+let dw_children_to_int (ch:dw_children) : int =
+ match ch with
+ DW_CHILDREN_no -> 0x00
+ | DW_CHILDREN_yes -> 0x01
+;;
+
+let dw_children_of_int (i:int) : dw_children =
+ match i with
+ 0 -> DW_CHILDREN_no
+ | 1 -> DW_CHILDREN_yes
+ | _ -> bug () "bad DWARF children code: %d" i
+;;
+
+type dw_at =
+ DW_AT_sibling
+ | DW_AT_location
+ | DW_AT_name
+ | DW_AT_ordering
+ | DW_AT_byte_size
+ | DW_AT_bit_offset
+ | DW_AT_bit_size
+ | DW_AT_stmt_list
+ | DW_AT_low_pc
+ | DW_AT_high_pc
+ | DW_AT_language
+ | DW_AT_discr
+ | DW_AT_discr_value
+ | DW_AT_visibility
+ | DW_AT_import
+ | DW_AT_string_length
+ | DW_AT_common_reference
+ | DW_AT_comp_dir
+ | DW_AT_const_value
+ | DW_AT_containing_type
+ | DW_AT_default_value
+ | DW_AT_inline
+ | DW_AT_is_optional
+ | DW_AT_lower_bound
+ | DW_AT_producer
+ | DW_AT_prototyped
+ | DW_AT_return_addr
+ | DW_AT_start_scope
+ | DW_AT_bit_stride
+ | DW_AT_upper_bound
+ | DW_AT_abstract_origin
+ | DW_AT_accessibility
+ | DW_AT_address_class
+ | DW_AT_artificial
+ | DW_AT_base_types
+ | DW_AT_calling_convention
+ | DW_AT_count
+ | DW_AT_data_member_location
+ | DW_AT_decl_column
+ | DW_AT_decl_file
+ | DW_AT_decl_line
+ | DW_AT_declaration
+ | DW_AT_discr_list
+ | DW_AT_encoding
+ | DW_AT_external
+ | DW_AT_frame_base
+ | DW_AT_friend
+ | DW_AT_identifier_case
+ | DW_AT_macro_info
+ | DW_AT_namelist_item
+ | DW_AT_priority
+ | DW_AT_segment
+ | DW_AT_specification
+ | DW_AT_static_link
+ | DW_AT_type
+ | DW_AT_use_location
+ | DW_AT_variable_parameter
+ | DW_AT_virtuality
+ | DW_AT_vtable_elem_location
+ | DW_AT_allocated
+ | DW_AT_associated
+ | DW_AT_data_location
+ | DW_AT_byte_stride
+ | DW_AT_entry_pc
+ | DW_AT_use_UTF8
+ | DW_AT_extension
+ | DW_AT_ranges
+ | DW_AT_trampoline
+ | DW_AT_call_column
+ | DW_AT_call_file
+ | DW_AT_call_line
+ | DW_AT_description
+ | DW_AT_binary_scale
+ | DW_AT_decimal_scale
+ | DW_AT_small
+ | DW_AT_decimal_sign
+ | DW_AT_digit_count
+ | DW_AT_picture_string
+ | DW_AT_mutable
+ | DW_AT_threads_scaled
+ | DW_AT_explicit
+ | DW_AT_object_pointer
+ | DW_AT_endianity
+ | DW_AT_elemental
+ | DW_AT_pure
+ | DW_AT_recursive
+ | DW_AT_lo_user
+ | DW_AT_rust_type_code
+ | DW_AT_rust_type_param_index
+ | DW_AT_rust_iterator
+ | DW_AT_rust_native_type_id
+ | DW_AT_hi_user
+;;
+
+
+let dw_at_to_int (a:dw_at) : int =
+ match a with
+ DW_AT_sibling -> 0x01
+ | DW_AT_location -> 0x02
+ | DW_AT_name -> 0x03
+ | DW_AT_ordering -> 0x09
+ | DW_AT_byte_size -> 0x0b
+ | DW_AT_bit_offset -> 0x0c
+ | DW_AT_bit_size -> 0x0d
+ | DW_AT_stmt_list -> 0x10
+ | DW_AT_low_pc -> 0x11
+ | DW_AT_high_pc -> 0x12
+ | DW_AT_language -> 0x13
+ | DW_AT_discr -> 0x15
+ | DW_AT_discr_value -> 0x16
+ | DW_AT_visibility -> 0x17
+ | DW_AT_import -> 0x18
+ | DW_AT_string_length -> 0x19
+ | DW_AT_common_reference -> 0x1a
+ | DW_AT_comp_dir -> 0x1b
+ | DW_AT_const_value -> 0x1c
+ | DW_AT_containing_type -> 0x1d
+ | DW_AT_default_value -> 0x1e
+ | DW_AT_inline -> 0x20
+ | DW_AT_is_optional -> 0x21
+ | DW_AT_lower_bound -> 0x22
+ | DW_AT_producer -> 0x25
+ | DW_AT_prototyped -> 0x27
+ | DW_AT_return_addr -> 0x2a
+ | DW_AT_start_scope -> 0x2c
+ | DW_AT_bit_stride -> 0x2e
+ | DW_AT_upper_bound -> 0x2f
+ | DW_AT_abstract_origin -> 0x31
+ | DW_AT_accessibility -> 0x32
+ | DW_AT_address_class -> 0x33
+ | DW_AT_artificial -> 0x34
+ | DW_AT_base_types -> 0x35
+ | DW_AT_calling_convention -> 0x36
+ | DW_AT_count -> 0x37
+ | DW_AT_data_member_location -> 0x38
+ | DW_AT_decl_column -> 0x39
+ | DW_AT_decl_file -> 0x3a
+ | DW_AT_decl_line -> 0x3b
+ | DW_AT_declaration -> 0x3c
+ | DW_AT_discr_list -> 0x3d
+ | DW_AT_encoding -> 0x3e
+ | DW_AT_external -> 0x3f
+ | DW_AT_frame_base -> 0x40
+ | DW_AT_friend -> 0x41
+ | DW_AT_identifier_case -> 0x42
+ | DW_AT_macro_info -> 0x43
+ | DW_AT_namelist_item -> 0x44
+ | DW_AT_priority -> 0x45
+ | DW_AT_segment -> 0x46
+ | DW_AT_specification -> 0x47
+ | DW_AT_static_link -> 0x48
+ | DW_AT_type -> 0x49
+ | DW_AT_use_location -> 0x4a
+ | DW_AT_variable_parameter -> 0x4b
+ | DW_AT_virtuality -> 0x4c
+ | DW_AT_vtable_elem_location -> 0x4d
+ | DW_AT_allocated -> 0x4e
+ | DW_AT_associated -> 0x4f
+ | DW_AT_data_location -> 0x50
+ | DW_AT_byte_stride -> 0x51
+ | DW_AT_entry_pc -> 0x52
+ | DW_AT_use_UTF8 -> 0x53
+ | DW_AT_extension -> 0x54
+ | DW_AT_ranges -> 0x55
+ | DW_AT_trampoline -> 0x56
+ | DW_AT_call_column -> 0x57
+ | DW_AT_call_file -> 0x58
+ | DW_AT_call_line -> 0x59
+ | DW_AT_description -> 0x5a
+ | DW_AT_binary_scale -> 0x5b
+ | DW_AT_decimal_scale -> 0x5c
+ | DW_AT_small -> 0x5d
+ | DW_AT_decimal_sign -> 0x5e
+ | DW_AT_digit_count -> 0x5f
+ | DW_AT_picture_string -> 0x60
+ | DW_AT_mutable -> 0x61
+ | DW_AT_threads_scaled -> 0x62
+ | DW_AT_explicit -> 0x63
+ | DW_AT_object_pointer -> 0x64
+ | DW_AT_endianity -> 0x65
+ | DW_AT_elemental -> 0x66
+ | DW_AT_pure -> 0x67
+ | DW_AT_recursive -> 0x68
+ | DW_AT_lo_user -> 0x2000
+ | DW_AT_rust_type_code -> 0x2300
+ | DW_AT_rust_type_param_index -> 0x2301
+ | DW_AT_rust_iterator -> 0x2302
+ | DW_AT_rust_native_type_id -> 0x2303
+ | DW_AT_hi_user -> 0x3fff
+;;
+
+let dw_at_of_int (i:int) : dw_at =
+ match i with
+ 0x01 -> DW_AT_sibling
+ | 0x02 -> DW_AT_location
+ | 0x03 -> DW_AT_name
+ | 0x09 -> DW_AT_ordering
+ | 0x0b -> DW_AT_byte_size
+ | 0x0c -> DW_AT_bit_offset
+ | 0x0d -> DW_AT_bit_size
+ | 0x10 -> DW_AT_stmt_list
+ | 0x11 -> DW_AT_low_pc
+ | 0x12 -> DW_AT_high_pc
+ | 0x13 -> DW_AT_language
+ | 0x15 -> DW_AT_discr
+ | 0x16 -> DW_AT_discr_value
+ | 0x17 -> DW_AT_visibility
+ | 0x18 -> DW_AT_import
+ | 0x19 -> DW_AT_string_length
+ | 0x1a -> DW_AT_common_reference
+ | 0x1b -> DW_AT_comp_dir
+ | 0x1c -> DW_AT_const_value
+ | 0x1d -> DW_AT_containing_type
+ | 0x1e -> DW_AT_default_value
+ | 0x20 -> DW_AT_inline
+ | 0x21 -> DW_AT_is_optional
+ | 0x22 -> DW_AT_lower_bound
+ | 0x25 -> DW_AT_producer
+ | 0x27 -> DW_AT_prototyped
+ | 0x2a -> DW_AT_return_addr
+ | 0x2c -> DW_AT_start_scope
+ | 0x2e -> DW_AT_bit_stride
+ | 0x2f -> DW_AT_upper_bound
+ | 0x31 -> DW_AT_abstract_origin
+ | 0x32 -> DW_AT_accessibility
+ | 0x33 -> DW_AT_address_class
+ | 0x34 -> DW_AT_artificial
+ | 0x35 -> DW_AT_base_types
+ | 0x36 -> DW_AT_calling_convention
+ | 0x37 -> DW_AT_count
+ | 0x38 -> DW_AT_data_member_location
+ | 0x39 -> DW_AT_decl_column
+ | 0x3a -> DW_AT_decl_file
+ | 0x3b -> DW_AT_decl_line
+ | 0x3c -> DW_AT_declaration
+ | 0x3d -> DW_AT_discr_list
+ | 0x3e -> DW_AT_encoding
+ | 0x3f -> DW_AT_external
+ | 0x40 -> DW_AT_frame_base
+ | 0x41 -> DW_AT_friend
+ | 0x42 -> DW_AT_identifier_case
+ | 0x43 -> DW_AT_macro_info
+ | 0x44 -> DW_AT_namelist_item
+ | 0x45 -> DW_AT_priority
+ | 0x46 -> DW_AT_segment
+ | 0x47 -> DW_AT_specification
+ | 0x48 -> DW_AT_static_link
+ | 0x49 -> DW_AT_type
+ | 0x4a -> DW_AT_use_location
+ | 0x4b -> DW_AT_variable_parameter
+ | 0x4c -> DW_AT_virtuality
+ | 0x4d -> DW_AT_vtable_elem_location
+ | 0x4e -> DW_AT_allocated
+ | 0x4f -> DW_AT_associated
+ | 0x50 -> DW_AT_data_location
+ | 0x51 -> DW_AT_byte_stride
+ | 0x52 -> DW_AT_entry_pc
+ | 0x53 -> DW_AT_use_UTF8
+ | 0x54 -> DW_AT_extension
+ | 0x55 -> DW_AT_ranges
+ | 0x56 -> DW_AT_trampoline
+ | 0x57 -> DW_AT_call_column
+ | 0x58 -> DW_AT_call_file
+ | 0x59 -> DW_AT_call_line
+ | 0x5a -> DW_AT_description
+ | 0x5b -> DW_AT_binary_scale
+ | 0x5c -> DW_AT_decimal_scale
+ | 0x5d -> DW_AT_small
+ | 0x5e -> DW_AT_decimal_sign
+ | 0x5f -> DW_AT_digit_count
+ | 0x60 -> DW_AT_picture_string
+ | 0x61 -> DW_AT_mutable
+ | 0x62 -> DW_AT_threads_scaled
+ | 0x63 -> DW_AT_explicit
+ | 0x64 -> DW_AT_object_pointer
+ | 0x65 -> DW_AT_endianity
+ | 0x66 -> DW_AT_elemental
+ | 0x67 -> DW_AT_pure
+ | 0x68 -> DW_AT_recursive
+ | 0x2000 -> DW_AT_lo_user
+ | 0x2300 -> DW_AT_rust_type_code
+ | 0x2301 -> DW_AT_rust_type_param_index
+ | 0x2302 -> DW_AT_rust_iterator
+ | 0x2303 -> DW_AT_rust_native_type_id
+ | 0x3fff -> DW_AT_hi_user
+ | _ -> bug () "bad DWARF attribute code: 0x%x" i
+;;
+
+let dw_at_to_string (a:dw_at) : string =
+ match a with
+ DW_AT_sibling -> "DW_AT_sibling"
+ | DW_AT_location -> "DW_AT_location"
+ | DW_AT_name -> "DW_AT_name"
+ | DW_AT_ordering -> "DW_AT_ordering"
+ | DW_AT_byte_size -> "DW_AT_byte_size"
+ | DW_AT_bit_offset -> "DW_AT_bit_offset"
+ | DW_AT_bit_size -> "DW_AT_bit_size"
+ | DW_AT_stmt_list -> "DW_AT_stmt_list"
+ | DW_AT_low_pc -> "DW_AT_low_pc"
+ | DW_AT_high_pc -> "DW_AT_high_pc"
+ | DW_AT_language -> "DW_AT_language"
+ | DW_AT_discr -> "DW_AT_discr"
+ | DW_AT_discr_value -> "DW_AT_discr_value"
+ | DW_AT_visibility -> "DW_AT_visibility"
+ | DW_AT_import -> "DW_AT_import"
+ | DW_AT_string_length -> "DW_AT_string_length"
+ | DW_AT_common_reference -> "DW_AT_common_reference"
+ | DW_AT_comp_dir -> "DW_AT_comp_dir"
+ | DW_AT_const_value -> "DW_AT_const_value"
+ | DW_AT_containing_type -> "DW_AT_containing_type"
+ | DW_AT_default_value -> "DW_AT_default_value"
+ | DW_AT_inline -> "DW_AT_inline"
+ | DW_AT_is_optional -> "DW_AT_is_optional"
+ | DW_AT_lower_bound -> "DW_AT_lower_bound"
+ | DW_AT_producer -> "DW_AT_producer"
+ | DW_AT_prototyped -> "DW_AT_prototyped"
+ | DW_AT_return_addr -> "DW_AT_return_addr"
+ | DW_AT_start_scope -> "DW_AT_start_scope"
+ | DW_AT_bit_stride -> "DW_AT_bit_stride"
+ | DW_AT_upper_bound -> "DW_AT_upper_bound"
+ | DW_AT_abstract_origin -> "DW_AT_abstract_origin"
+ | DW_AT_accessibility -> "DW_AT_accessibility"
+ | DW_AT_address_class -> "DW_AT_address_class"
+ | DW_AT_artificial -> "DW_AT_artificial"
+ | DW_AT_base_types -> "DW_AT_base_types"
+ | DW_AT_calling_convention -> "DW_AT_calling_convention"
+ | DW_AT_count -> "DW_AT_count"
+ | DW_AT_data_member_location -> "DW_AT_data_member_location"
+ | DW_AT_decl_column -> "DW_AT_decl_column"
+ | DW_AT_decl_file -> "DW_AT_decl_file"
+ | DW_AT_decl_line -> "DW_AT_decl_line"
+ | DW_AT_declaration -> "DW_AT_declaration"
+ | DW_AT_discr_list -> "DW_AT_discr_list"
+ | DW_AT_encoding -> "DW_AT_encoding"
+ | DW_AT_external -> "DW_AT_external"
+ | DW_AT_frame_base -> "DW_AT_frame_base"
+ | DW_AT_friend -> "DW_AT_friend"
+ | DW_AT_identifier_case -> "DW_AT_identifier_case"
+ | DW_AT_macro_info -> "DW_AT_macro_info"
+ | DW_AT_namelist_item -> "DW_AT_namelist_item"
+ | DW_AT_priority -> "DW_AT_priority"
+ | DW_AT_segment -> "DW_AT_segment"
+ | DW_AT_specification -> "DW_AT_specification"
+ | DW_AT_static_link -> "DW_AT_static_link"
+ | DW_AT_type -> "DW_AT_type"
+ | DW_AT_use_location -> "DW_AT_use_location"
+ | DW_AT_variable_parameter -> "DW_AT_variable_parameter"
+ | DW_AT_virtuality -> "DW_AT_virtuality"
+ | DW_AT_vtable_elem_location -> "DW_AT_vtable_elem_location"
+ | DW_AT_allocated -> "DW_AT_allocated"
+ | DW_AT_associated -> "DW_AT_associated"
+ | DW_AT_data_location -> "DW_AT_data_location"
+ | DW_AT_byte_stride -> "DW_AT_byte_stride"
+ | DW_AT_entry_pc -> "DW_AT_entry_pc"
+ | DW_AT_use_UTF8 -> "DW_AT_use_UTF8"
+ | DW_AT_extension -> "DW_AT_extension"
+ | DW_AT_ranges -> "DW_AT_ranges"
+ | DW_AT_trampoline -> "DW_AT_trampoline"
+ | DW_AT_call_column -> "DW_AT_call_column"
+ | DW_AT_call_file -> "DW_AT_call_file"
+ | DW_AT_call_line -> "DW_AT_call_line"
+ | DW_AT_description -> "DW_AT_description"
+ | DW_AT_binary_scale -> "DW_AT_binary_scale"
+ | DW_AT_decimal_scale -> "DW_AT_decimal_scale"
+ | DW_AT_small -> "DW_AT_small"
+ | DW_AT_decimal_sign -> "DW_AT_decimal_sign"
+ | DW_AT_digit_count -> "DW_AT_digit_count"
+ | DW_AT_picture_string -> "DW_AT_picture_string"
+ | DW_AT_mutable -> "DW_AT_mutable"
+ | DW_AT_threads_scaled -> "DW_AT_threads_scaled"
+ | DW_AT_explicit -> "DW_AT_explicit"
+ | DW_AT_object_pointer -> "DW_AT_object_pointer"
+ | DW_AT_endianity -> "DW_AT_endianity"
+ | DW_AT_elemental -> "DW_AT_elemental"
+ | DW_AT_pure -> "DW_AT_pure"
+ | DW_AT_recursive -> "DW_AT_recursive"
+ | DW_AT_lo_user -> "DW_AT_lo_user"
+ | DW_AT_rust_type_code -> "DW_AT_rust_type_code"
+ | DW_AT_rust_type_param_index -> "DW_AT_rust_type_param_index"
+ | DW_AT_rust_iterator -> "DW_AT_rust_iterator"
+ | DW_AT_rust_native_type_id -> "DW_AT_native_type_id"
+ | DW_AT_hi_user -> "DW_AT_hi_user"
+;;
+
+(*
+ * We encode our 'built-in types' using DW_TAG_pointer_type and various
+ * DW_AT_pointer_type_codes. This seems to be more gdb-compatible than
+ * the DWARF-recommended way of using DW_TAG_unspecified_type.
+ *)
+type dw_rust_type =
+ DW_RUST_type_param
+ | DW_RUST_nil
+ | DW_RUST_vec
+ | DW_RUST_chan
+ | DW_RUST_port
+ | DW_RUST_task
+ | DW_RUST_tag
+ | DW_RUST_iso
+ | DW_RUST_type
+ | DW_RUST_native
+;;
+
+let dw_rust_type_to_int (pt:dw_rust_type) : int =
+ match pt with
+ DW_RUST_type_param -> 0x1
+ | DW_RUST_nil -> 0x2
+ | DW_RUST_vec -> 0x3
+ | DW_RUST_chan -> 0x4
+ | DW_RUST_port -> 0x5
+ | DW_RUST_task -> 0x6
+ | DW_RUST_tag -> 0x7
+ | DW_RUST_iso -> 0x8
+ | DW_RUST_type -> 0x9
+ | DW_RUST_native -> 0xa
+;;
+
+let dw_rust_type_of_int (i:int) : dw_rust_type =
+ match i with
+ 0x1 -> DW_RUST_type_param
+ | 0x2 -> DW_RUST_nil
+ | 0x3 -> DW_RUST_vec
+ | 0x4 -> DW_RUST_chan
+ | 0x5 -> DW_RUST_port
+ | 0x6 -> DW_RUST_task
+ | 0x7 -> DW_RUST_tag
+ | 0x8 -> DW_RUST_iso
+ | 0x9 -> DW_RUST_type
+ | 0xa -> DW_RUST_native
+ | _ -> bug () "bad DWARF rust-pointer-type code: %d" i
+;;
+
+type dw_ate =
+ DW_ATE_address
+ | DW_ATE_boolean
+ | DW_ATE_complex_float
+ | DW_ATE_float
+ | DW_ATE_signed
+ | DW_ATE_signed_char
+ | DW_ATE_unsigned
+ | DW_ATE_unsigned_char
+ | DW_ATE_imaginary_float
+ | DW_ATE_packed_decimal
+ | DW_ATE_numeric_string
+ | DW_ATE_edited
+ | DW_ATE_signed_fixed
+ | DW_ATE_unsigned_fixed
+ | DW_ATE_decimal_float
+ | DW_ATE_lo_user
+ | DW_ATE_hi_user
+;;
+
+let dw_ate_to_int (ate:dw_ate) : int =
+ match ate with
+ DW_ATE_address -> 0x01
+ | DW_ATE_boolean -> 0x02
+ | DW_ATE_complex_float -> 0x03
+ | DW_ATE_float -> 0x04
+ | DW_ATE_signed -> 0x05
+ | DW_ATE_signed_char -> 0x06
+ | DW_ATE_unsigned -> 0x07
+ | DW_ATE_unsigned_char -> 0x08
+ | DW_ATE_imaginary_float -> 0x09
+ | DW_ATE_packed_decimal -> 0x0a
+ | DW_ATE_numeric_string -> 0x0b
+ | DW_ATE_edited -> 0x0c
+ | DW_ATE_signed_fixed -> 0x0d
+ | DW_ATE_unsigned_fixed -> 0x0e
+ | DW_ATE_decimal_float -> 0x0f
+ | DW_ATE_lo_user -> 0x80
+ | DW_ATE_hi_user -> 0xff
+;;
+
+let dw_ate_of_int (i:int) : dw_ate =
+ match i with
+ 0x01 -> DW_ATE_address
+ | 0x02 -> DW_ATE_boolean
+ | 0x03 -> DW_ATE_complex_float
+ | 0x04 -> DW_ATE_float
+ | 0x05 -> DW_ATE_signed
+ | 0x06 -> DW_ATE_signed_char
+ | 0x07 -> DW_ATE_unsigned
+ | 0x08 -> DW_ATE_unsigned_char
+ | 0x09 -> DW_ATE_imaginary_float
+ | 0x0a -> DW_ATE_packed_decimal
+ | 0x0b -> DW_ATE_numeric_string
+ | 0x0c -> DW_ATE_edited
+ | 0x0d -> DW_ATE_signed_fixed
+ | 0x0e -> DW_ATE_unsigned_fixed
+ | 0x0f -> DW_ATE_decimal_float
+ | 0x80 -> DW_ATE_lo_user
+ | 0xff -> DW_ATE_hi_user
+ | _ -> bug () "bad DWARF attribute-encoding code: %d" i
+;;
+
+type dw_form =
+ | DW_FORM_addr
+ | DW_FORM_block2
+ | DW_FORM_block4
+ | DW_FORM_data2
+ | DW_FORM_data4
+ | DW_FORM_data8
+ | DW_FORM_string
+ | DW_FORM_block
+ | DW_FORM_block1
+ | DW_FORM_data1
+ | DW_FORM_flag
+ | DW_FORM_sdata
+ | DW_FORM_strp
+ | DW_FORM_udata
+ | DW_FORM_ref_addr
+ | DW_FORM_ref1
+ | DW_FORM_ref2
+ | DW_FORM_ref4
+ | DW_FORM_ref8
+ | DW_FORM_ref_udata
+ | DW_FORM_indirect
+;;
+
+
+let dw_form_to_int (f:dw_form) : int =
+ match f with
+ | DW_FORM_addr -> 0x01
+ | DW_FORM_block2 -> 0x03
+ | DW_FORM_block4 -> 0x04
+ | DW_FORM_data2 -> 0x05
+ | DW_FORM_data4 -> 0x06
+ | DW_FORM_data8 -> 0x07
+ | DW_FORM_string -> 0x08
+ | DW_FORM_block -> 0x09
+ | DW_FORM_block1 -> 0x0a
+ | DW_FORM_data1 -> 0x0b
+ | DW_FORM_flag -> 0x0c
+ | DW_FORM_sdata -> 0x0d
+ | DW_FORM_strp -> 0x0e
+ | DW_FORM_udata -> 0x0f
+ | DW_FORM_ref_addr -> 0x10
+ | DW_FORM_ref1 -> 0x11
+ | DW_FORM_ref2 -> 0x12
+ | DW_FORM_ref4 -> 0x13
+ | DW_FORM_ref8 -> 0x14
+ | DW_FORM_ref_udata -> 0x15
+ | DW_FORM_indirect -> 0x16
+;;
+
+let dw_form_of_int (i:int) : dw_form =
+ match i with
+ | 0x01 -> DW_FORM_addr
+ | 0x03 -> DW_FORM_block2
+ | 0x04 -> DW_FORM_block4
+ | 0x05 -> DW_FORM_data2
+ | 0x06 -> DW_FORM_data4
+ | 0x07 -> DW_FORM_data8
+ | 0x08 -> DW_FORM_string
+ | 0x09 -> DW_FORM_block
+ | 0x0a -> DW_FORM_block1
+ | 0x0b -> DW_FORM_data1
+ | 0x0c -> DW_FORM_flag
+ | 0x0d -> DW_FORM_sdata
+ | 0x0e -> DW_FORM_strp
+ | 0x0f -> DW_FORM_udata
+ | 0x10 -> DW_FORM_ref_addr
+ | 0x11 -> DW_FORM_ref1
+ | 0x12 -> DW_FORM_ref2
+ | 0x13 -> DW_FORM_ref4
+ | 0x14 -> DW_FORM_ref8
+ | 0x15 -> DW_FORM_ref_udata
+ | 0x16 -> DW_FORM_indirect
+ | _ -> bug () "bad DWARF form code: 0x%x" i
+;;
+
+let dw_form_to_string (f:dw_form) : string =
+ match f with
+ | DW_FORM_addr -> "DW_FORM_addr"
+ | DW_FORM_block2 -> "DW_FORM_block2"
+ | DW_FORM_block4 -> "DW_FORM_block4"
+ | DW_FORM_data2 -> "DW_FORM_data2"
+ | DW_FORM_data4 -> "DW_FORM_data4"
+ | DW_FORM_data8 -> "DW_FORM_data8"
+ | DW_FORM_string -> "DW_FORM_string"
+ | DW_FORM_block -> "DW_FORM_block"
+ | DW_FORM_block1 -> "DW_FORM_block1"
+ | DW_FORM_data1 -> "DW_FORM_data1"
+ | DW_FORM_flag -> "DW_FORM_flag"
+ | DW_FORM_sdata -> "DW_FORM_sdata"
+ | DW_FORM_strp -> "DW_FORM_strp"
+ | DW_FORM_udata -> "DW_FORM_udata"
+ | DW_FORM_ref_addr -> "DW_FORM_ref_addr"
+ | DW_FORM_ref1 -> "DW_FORM_ref1"
+ | DW_FORM_ref2 -> "DW_FORM_ref2"
+ | DW_FORM_ref4 -> "DW_FORM_ref4"
+ | DW_FORM_ref8 -> "DW_FORM_ref8"
+ | DW_FORM_ref_udata -> "DW_FORM_ref_udata"
+ | DW_FORM_indirect -> "DW_FORM_indirect"
+;;
+
+type dw_op =
+ DW_OP_lit of int
+ | DW_OP_addr of Asm.expr64
+ | DW_OP_const1u of Asm.expr64
+ | DW_OP_const1s of Asm.expr64
+ | DW_OP_const2u of Asm.expr64
+ | DW_OP_const2s of Asm.expr64
+ | DW_OP_const4u of Asm.expr64
+ | DW_OP_const4s of Asm.expr64
+ | DW_OP_const8u of Asm.expr64
+ | DW_OP_const8s of Asm.expr64
+ | DW_OP_constu of Asm.expr64
+ | DW_OP_consts of Asm.expr64
+ | DW_OP_fbreg of Asm.expr64
+ | DW_OP_reg of int
+ | DW_OP_regx of Asm.expr64
+ | DW_OP_breg of (int * Asm.expr64)
+ | DW_OP_bregx of (Asm.expr64 * Asm.expr64)
+ | DW_OP_dup
+ | DW_OP_drop
+ | DW_OP_pick of Asm.expr64
+ | DW_OP_over
+ | DW_OP_swap
+ | DW_OP_rot
+ | DW_OP_piece of Asm.expr64
+ | DW_OP_bit_piece of (Asm.expr64 * Asm.expr64)
+ | DW_OP_deref
+ | DW_OP_deref_size of Asm.expr64
+ | DW_OP_xderef
+ | DW_OP_xderef_size of Asm.expr64
+ | DW_OP_push_object_address
+ | DW_OP_form_tls_address
+ | DW_OP_call_frame_cfa
+ | DW_OP_abs
+ | DW_OP_and
+ | DW_OP_div
+ | DW_OP_minus
+ | DW_OP_mod
+ | DW_OP_mul
+ | DW_OP_neg
+ | DW_OP_not
+ | DW_OP_or
+ | DW_OP_plus
+ | DW_OP_plus_uconst of Asm.expr64
+ | DW_OP_shl
+ | DW_OP_shr
+ | DW_OP_shra
+ | DW_OP_xor
+ | DW_OP_le
+ | DW_OP_ge
+ | DW_OP_eq
+ | DW_OP_lt
+ | DW_OP_gt
+ | DW_OP_ne
+ | DW_OP_skip of Asm.expr64
+ | DW_OP_bra of Asm.expr64
+ | DW_OP_call2 of Asm.expr64
+ | DW_OP_call4 of Asm.expr64
+ | DW_OP_call_ref of Asm.expr64
+ | DW_OP_nop
+;;
+
+let dw_op_to_frag (abi:Abi.abi) (op:dw_op) : Asm.frag =
+ match op with
+
+ DW_OP_addr e -> SEQ [| BYTE 0x03; WORD (abi.Abi.abi_word_ty, e) |]
+ | DW_OP_deref -> BYTE 0x06
+ | DW_OP_const1u e -> SEQ [| BYTE 0x08; WORD (TY_u8, e) |]
+ | DW_OP_const1s e -> SEQ [| BYTE 0x09; WORD (TY_i8, e) |]
+ | DW_OP_const2u e -> SEQ [| BYTE 0x0a; WORD (TY_u16, e) |]
+ | DW_OP_const2s e -> SEQ [| BYTE 0x0b; WORD (TY_i16, e) |]
+ | DW_OP_const4u e -> SEQ [| BYTE 0x0c; WORD (TY_u32, e) |]
+ | DW_OP_const4s e -> SEQ [| BYTE 0x0d; WORD (TY_i32, e) |]
+ | DW_OP_const8u e -> SEQ [| BYTE 0x0e; WORD (TY_u64, e) |]
+ | DW_OP_const8s e -> SEQ [| BYTE 0x0f; WORD (TY_i64, e) |]
+ | DW_OP_constu e -> SEQ [| BYTE 0x10; ULEB128 e |]
+ | DW_OP_consts e -> SEQ [| BYTE 0x11; SLEB128 e |]
+ | DW_OP_dup -> BYTE 0x12
+ | DW_OP_drop -> BYTE 0x13
+ | DW_OP_over -> BYTE 0x14
+ | DW_OP_pick e -> SEQ [| BYTE 0x15; WORD (TY_u8, e) |]
+ | DW_OP_swap -> BYTE 0x16
+ | DW_OP_rot -> BYTE 0x17
+ | DW_OP_xderef -> BYTE 0x18
+ | DW_OP_abs -> BYTE 0x19
+ | DW_OP_and -> BYTE 0x1a
+ | DW_OP_div -> BYTE 0x1b
+ | DW_OP_minus -> BYTE 0x1c
+ | DW_OP_mod -> BYTE 0x1d
+ | DW_OP_mul -> BYTE 0x1e
+ | DW_OP_neg -> BYTE 0x1f
+ | DW_OP_not -> BYTE 0x20
+ | DW_OP_or -> BYTE 0x21
+ | DW_OP_plus -> BYTE 0x22
+ | DW_OP_plus_uconst e -> SEQ [| BYTE 0x23; ULEB128 e |]
+ | DW_OP_shl -> BYTE 0x24
+ | DW_OP_shr -> BYTE 0x25
+ | DW_OP_shra -> BYTE 0x26
+ | DW_OP_xor -> BYTE 0x27
+ | DW_OP_skip e -> SEQ [| BYTE 0x2f; WORD (TY_i16, e) |]
+ | DW_OP_bra e -> SEQ [| BYTE 0x28; WORD (TY_i16, e) |]
+ | DW_OP_eq -> BYTE 0x29
+ | DW_OP_ge -> BYTE 0x2a
+ | DW_OP_gt -> BYTE 0x2b
+ | DW_OP_le -> BYTE 0x2c
+ | DW_OP_lt -> BYTE 0x2d
+ | DW_OP_ne -> BYTE 0x2e
+
+ | DW_OP_lit i ->
+ assert (0 <= i && i < 32);
+ BYTE (i + 0x30)
+
+ | DW_OP_reg i ->
+ assert (0 <= i && i < 32);
+ BYTE (i + 0x50)
+
+ | DW_OP_breg (i, e) ->
+ assert (0 <= i && i < 32);
+ SEQ [| BYTE (i + 0x70); SLEB128 e |]
+
+ | DW_OP_regx e -> SEQ [| BYTE 0x90; ULEB128 e|]
+ | DW_OP_fbreg e -> SEQ [| BYTE 0x91; SLEB128 e |]
+ | DW_OP_bregx (r, off) -> SEQ [| BYTE 0x92; ULEB128 r; SLEB128 off |]
+ | DW_OP_piece e -> SEQ [| BYTE 0x93; ULEB128 e |]
+ | DW_OP_deref_size e -> SEQ [| BYTE 0x94; WORD (TY_u8, e) |]
+ | DW_OP_xderef_size e -> SEQ [| BYTE 0x95; WORD (TY_u8, e) |]
+ | DW_OP_nop -> BYTE 0x96
+ | DW_OP_push_object_address -> BYTE 0x97
+ | DW_OP_call2 e -> SEQ [| BYTE 0x98; WORD (TY_u16, e) |]
+ | DW_OP_call4 e -> SEQ [| BYTE 0x99; WORD (TY_u32, e) |]
+ | DW_OP_call_ref e -> SEQ [| BYTE 0x9a; WORD (abi.Abi.abi_word_ty, e) |]
+ | DW_OP_form_tls_address -> BYTE 0x9b
+ | DW_OP_call_frame_cfa -> BYTE 0x9c
+ | DW_OP_bit_piece (sz, off) ->
+ SEQ [| BYTE 0x9d; ULEB128 sz; ULEB128 off |]
+;;
+
+type dw_lns =
+ DW_LNS_copy
+ | DW_LNS_advance_pc
+ | DW_LNS_advance_line
+ | DW_LNS_set_file
+ | DW_LNS_set_column
+ | DW_LNS_negage_stmt
+ | DW_LNS_set_basic_block
+ | DW_LNS_const_add_pc
+ | DW_LNS_fixed_advance_pc
+ | DW_LNS_set_prologue_end
+ | DW_LNS_set_epilogue_begin
+ | DW_LNS_set_isa
+;;
+
+let int_to_dw_lns i =
+ match i with
+ 1 -> DW_LNS_copy
+ | 2 -> DW_LNS_advance_pc
+ | 3 -> DW_LNS_advance_line
+ | 4 -> DW_LNS_set_file
+ | 5 -> DW_LNS_set_column
+ | 6 -> DW_LNS_negage_stmt
+ | 7 -> DW_LNS_set_basic_block
+ | 8 -> DW_LNS_const_add_pc
+ | 9 -> DW_LNS_fixed_advance_pc
+ | 10 -> DW_LNS_set_prologue_end
+ | 11 -> DW_LNS_set_epilogue_begin
+ | 12 -> DW_LNS_set_isa
+ | _ -> bug () "Internal logic error: (Dwarf.int_to_dw_lns %d)" i
+;;
+
+let dw_lns_to_int lns =
+ match lns with
+ DW_LNS_copy -> 1
+ | DW_LNS_advance_pc -> 2
+ | DW_LNS_advance_line -> 3
+ | DW_LNS_set_file -> 4
+ | DW_LNS_set_column -> 5
+ | DW_LNS_negage_stmt -> 6
+ | DW_LNS_set_basic_block -> 7
+ | DW_LNS_const_add_pc -> 8
+ | DW_LNS_fixed_advance_pc -> 9
+ | DW_LNS_set_prologue_end -> 10
+ | DW_LNS_set_epilogue_begin -> 11
+ | DW_LNS_set_isa -> 12
+;;
+
+let max_dw_lns = 12;;
+
+let dw_lns_arity lns =
+ match lns with
+ DW_LNS_copy -> 0
+ | DW_LNS_advance_pc -> 1
+ | DW_LNS_advance_line -> 1
+ | DW_LNS_set_file -> 1
+ | DW_LNS_set_column -> 1
+ | DW_LNS_negage_stmt -> 0
+ | DW_LNS_set_basic_block -> 0
+ | DW_LNS_const_add_pc -> 0
+ | DW_LNS_fixed_advance_pc -> 1
+ | DW_LNS_set_prologue_end -> 0
+ | DW_LNS_set_epilogue_begin -> 0
+ | DW_LNS_set_isa -> 1
+;;
+
+type debug_records =
+ {
+ debug_aranges: Asm.frag;
+ debug_pubnames: Asm.frag;
+ debug_info: Asm.frag;
+ debug_abbrev: Asm.frag;
+ debug_line: Asm.frag;
+ debug_frame: Asm.frag;
+ }
+
+type abbrev = (dw_tag * dw_children * ((dw_at * dw_form) array));;
+
+let (abbrev_crate_cu:abbrev) =
+ (DW_TAG_compile_unit, DW_CHILDREN_yes,
+ [|
+ (DW_AT_producer, DW_FORM_string);
+ (DW_AT_language, DW_FORM_data4);
+ (DW_AT_name, DW_FORM_string);
+ (DW_AT_comp_dir, DW_FORM_string);
+ (DW_AT_low_pc, DW_FORM_addr);
+ (DW_AT_high_pc, DW_FORM_addr);
+ (DW_AT_use_UTF8, DW_FORM_flag)
+ |])
+ ;;
+
+let (abbrev_meta:abbrev) =
+ (DW_TAG_rust_meta, DW_CHILDREN_no,
+ [|
+ (DW_AT_name, DW_FORM_string);
+ (DW_AT_const_value, DW_FORM_string)
+ |])
+;;
+
+let (abbrev_srcfile_cu:abbrev) =
+ (DW_TAG_compile_unit, DW_CHILDREN_yes,
+ [|
+ (DW_AT_name, DW_FORM_string);
+ (DW_AT_comp_dir, DW_FORM_string);
+ (DW_AT_low_pc, DW_FORM_addr);
+ (DW_AT_high_pc, DW_FORM_addr);
+ |])
+;;
+
+
+let (abbrev_module:abbrev) =
+ (DW_TAG_module, DW_CHILDREN_yes,
+ [|
+ (DW_AT_name, DW_FORM_string);
+ |])
+;;
+
+let (abbrev_subprogram:abbrev) =
+ (DW_TAG_subprogram, DW_CHILDREN_yes,
+ [|
+ (DW_AT_name, DW_FORM_string);
+ (DW_AT_type, DW_FORM_ref_addr);
+ (DW_AT_low_pc, DW_FORM_addr);
+ (DW_AT_high_pc, DW_FORM_addr);
+ (DW_AT_frame_base, DW_FORM_block1);
+ (DW_AT_return_addr, DW_FORM_block1);
+ (DW_AT_mutable, DW_FORM_flag);
+ (DW_AT_pure, DW_FORM_flag);
+ |])
+;;
+
+let (abbrev_typedef:abbrev) =
+ (DW_TAG_typedef, DW_CHILDREN_yes,
+ [|
+ (DW_AT_name, DW_FORM_string);
+ (DW_AT_type, DW_FORM_ref_addr)
+ |])
+;;
+
+let (abbrev_lexical_block:abbrev) =
+ (DW_TAG_lexical_block, DW_CHILDREN_yes,
+ [|
+ (DW_AT_low_pc, DW_FORM_addr);
+ (DW_AT_high_pc, DW_FORM_addr);
+ |])
+;;
+
+let (abbrev_variable:abbrev) =
+ (DW_TAG_variable, DW_CHILDREN_no,
+ [|
+ (DW_AT_name, DW_FORM_string);
+ (DW_AT_location, DW_FORM_block1);
+ (DW_AT_type, DW_FORM_ref_addr)
+ |])
+;;
+
+(* NB: must have same abbrev-body as abbrev_variable. *)
+let (abbrev_formal:abbrev) =
+ (DW_TAG_formal_parameter, DW_CHILDREN_no,
+ [|
+ (DW_AT_name, DW_FORM_string);
+ (DW_AT_location, DW_FORM_block1);
+ (DW_AT_type, DW_FORM_ref_addr)
+ |])
+;;
+
+let (abbrev_unspecified_anon_structure_type:abbrev) =
+ (DW_TAG_structure_type, DW_CHILDREN_no,
+ [|
+ (DW_AT_declaration, DW_FORM_flag);
+ |])
+;;
+
+let (abbrev_unspecified_structure_type:abbrev) =
+ (DW_TAG_structure_type, DW_CHILDREN_no,
+ [|
+ (DW_AT_rust_type_code, DW_FORM_data1);
+ (DW_AT_declaration, DW_FORM_flag);
+ |])
+;;
+
+let (abbrev_unspecified_pointer_type:abbrev) =
+ (DW_TAG_pointer_type, DW_CHILDREN_no,
+ [|
+ (DW_AT_rust_type_code, DW_FORM_data1);
+ (DW_AT_declaration, DW_FORM_flag);
+ (DW_AT_type, DW_FORM_ref_addr)
+ |])
+;;
+
+let (abbrev_native_pointer_type:abbrev) =
+ (DW_TAG_pointer_type, DW_CHILDREN_no,
+ [|
+ (DW_AT_rust_type_code, DW_FORM_data1);
+ (DW_AT_rust_native_type_id, DW_FORM_data4)
+ |])
+;;
+
+let (abbrev_rust_type_param:abbrev) =
+ (DW_TAG_pointer_type, DW_CHILDREN_no,
+ [|
+ (DW_AT_rust_type_code, DW_FORM_data1);
+ (DW_AT_rust_type_param_index, DW_FORM_data4);
+ (DW_AT_mutable, DW_FORM_flag);
+ (DW_AT_pure, DW_FORM_flag);
+ |])
+;;
+
+let (abbrev_rust_type_param_decl:abbrev) =
+ (DW_TAG_formal_parameter, DW_CHILDREN_no,
+ [|
+ (DW_AT_rust_type_code, DW_FORM_data1);
+ (DW_AT_name, DW_FORM_string);
+ (DW_AT_rust_type_param_index, DW_FORM_data4);
+ (DW_AT_mutable, DW_FORM_flag);
+ (DW_AT_pure, DW_FORM_flag);
+ |])
+;;
+
+let (abbrev_base_type:abbrev) =
+ (DW_TAG_base_type, DW_CHILDREN_no,
+ [|
+ (DW_AT_name, DW_FORM_string);
+ (DW_AT_encoding, DW_FORM_data1);
+ (DW_AT_byte_size, DW_FORM_data1)
+ |])
+;;
+
+let (abbrev_alias_slot:abbrev) =
+ (DW_TAG_reference_type, DW_CHILDREN_no,
+ [|
+ (DW_AT_type, DW_FORM_ref_addr);
+ (DW_AT_mutable, DW_FORM_flag);
+ |])
+;;
+
+let (abbrev_exterior_slot:abbrev) =
+ (DW_TAG_reference_type, DW_CHILDREN_no,
+ [|
+ (DW_AT_type, DW_FORM_ref_addr);
+ (DW_AT_mutable, DW_FORM_flag);
+ (DW_AT_data_location, DW_FORM_block1);
+ |])
+;;
+
+let (abbrev_struct_type:abbrev) =
+ (DW_TAG_structure_type, DW_CHILDREN_yes,
+ [|
+ (DW_AT_byte_size, DW_FORM_block4)
+ |])
+;;
+
+let (abbrev_struct_type_member:abbrev) =
+ (DW_TAG_member, DW_CHILDREN_no,
+ [|
+ (DW_AT_name, DW_FORM_string);
+ (DW_AT_type, DW_FORM_ref_addr);
+ (DW_AT_mutable, DW_FORM_flag);
+ (DW_AT_data_member_location, DW_FORM_block4);
+ (DW_AT_byte_size, DW_FORM_block4)
+ |])
+;;
+
+let (abbrev_subroutine_type:abbrev) =
+ (DW_TAG_subroutine_type, DW_CHILDREN_yes,
+ [|
+ (* FIXME: model effects properly. *)
+ (DW_AT_type, DW_FORM_ref_addr); (* NB: output type. *)
+ (DW_AT_mutable, DW_FORM_flag);
+ (DW_AT_pure, DW_FORM_flag);
+ (DW_AT_rust_iterator, DW_FORM_flag);
+ |])
+;;
+
+let (abbrev_formal_type:abbrev) =
+ (DW_TAG_formal_parameter, DW_CHILDREN_no,
+ [|
+ (DW_AT_type, DW_FORM_ref_addr)
+ |])
+;;
+
+
+let (abbrev_obj_subroutine_type:abbrev) =
+ (DW_TAG_subroutine_type, DW_CHILDREN_yes,
+ [|
+ (* FIXME: model effects properly. *)
+ (DW_AT_name, DW_FORM_string);
+ (DW_AT_type, DW_FORM_ref_addr); (* NB: output type. *)
+ (DW_AT_mutable, DW_FORM_flag);
+ (DW_AT_pure, DW_FORM_flag);
+ (DW_AT_rust_iterator, DW_FORM_flag);
+ |])
+;;
+
+let (abbrev_obj_type:abbrev) =
+ (DW_TAG_interface_type, DW_CHILDREN_yes,
+ [|
+ (DW_AT_mutable, DW_FORM_flag);
+ (DW_AT_pure, DW_FORM_flag);
+ |])
+;;
+
+let (abbrev_string_type:abbrev) =
+ (DW_TAG_string_type, DW_CHILDREN_no,
+ [|
+ (DW_AT_string_length, DW_FORM_block1);
+ (DW_AT_data_location, DW_FORM_block1);
+ |])
+;;
+
+
+let prepend lref x = lref := x :: (!lref)
+;;
+
+
+let dwarf_visitor
+ (cx:ctxt)
+ (inner:Walk.visitor)
+ (path:Ast.name_component Stack.t)
+ (cu_info_section_fixup:fixup)
+ (cu_aranges:(frag list) ref)
+ (cu_pubnames:(frag list) ref)
+ (cu_infos:(frag list) ref)
+ (cu_abbrevs:(frag list) ref)
+ (cu_lines:(frag list) ref)
+ (cu_frames:(frag list) ref)
+ : Walk.visitor =
+
+ let (abi:Abi.abi) = cx.ctxt_abi in
+ let (word_sz:int64) = abi.Abi.abi_word_sz in
+ let (word_sz_int:int) = Int64.to_int word_sz in
+ let (word_bits:Il.bits) = abi.Abi.abi_word_bits in
+ let (word_ty_mach:ty_mach) =
+ match word_bits with
+ Il.Bits8 -> TY_u8
+ | Il.Bits16 -> TY_u16
+ | Il.Bits32 -> TY_u32
+ | Il.Bits64 -> TY_u64
+ in
+ let (signed_word_ty_mach:ty_mach) =
+ match word_bits with
+ Il.Bits8 -> TY_i8
+ | Il.Bits16 -> TY_i16
+ | Il.Bits32 -> TY_i32
+ | Il.Bits64 -> TY_i64
+ in
+
+ let path_name _ = Ast.fmt_to_str Ast.fmt_name (Walk.path_to_name path) in
+
+ let (abbrev_table:(abbrev, int) Hashtbl.t) = Hashtbl.create 0 in
+
+ let uleb i = ULEB128 (IMM (Int64.of_int i)) in
+
+ let get_abbrev_code
+ (ab:abbrev)
+ : int =
+ if Hashtbl.mem abbrev_table ab
+ then Hashtbl.find abbrev_table ab
+ else
+ let n = (Hashtbl.length abbrev_table) + 1 in
+ let (tag, children, attrs) = ab in
+ let attr_ulebs = Array.create ((Array.length attrs) * 2) MARK in
+ for i = 0 to (Array.length attrs) - 1 do
+ let (attr, form) = attrs.(i) in
+ attr_ulebs.(2*i) <- uleb (dw_at_to_int attr);
+ attr_ulebs.((2*i)+1) <- uleb (dw_form_to_int form)
+ done;
+ let ab_frag =
+ (SEQ [|
+ uleb n;
+ uleb (dw_tag_to_int tag);
+ BYTE (dw_children_to_int children);
+ SEQ attr_ulebs;
+ uleb 0; uleb 0;
+ |])
+ in
+ prepend cu_abbrevs ab_frag;
+ htab_put abbrev_table ab n;
+ n
+ in
+
+ let (curr_cu_aranges:(frag list) ref) = ref [] in
+ let (curr_cu_pubnames:(frag list) ref) = ref [] in
+ let (curr_cu_infos:(frag list) ref) = ref [] in
+ let (curr_cu_line:(frag list) ref) = ref [] in
+ let (curr_cu_frame:(frag list) ref) = ref [] in
+
+ let emit_die die = prepend curr_cu_infos die in
+ let emit_null_die _ = emit_die (BYTE 0) in
+
+ let dw_form_block1 (ops:dw_op array) : Asm.frag =
+ let frag = SEQ (Array.map (dw_op_to_frag abi) ops) in
+ let block_fixup = new_fixup "DW_FORM_block1 fixup" in
+ SEQ [| WORD (TY_u8, F_SZ block_fixup);
+ DEF (block_fixup, frag) |]
+ in
+
+ let dw_form_ref_addr (fix:fixup) : Asm.frag =
+ WORD (signed_word_ty_mach,
+ SUB ((M_POS fix), M_POS cu_info_section_fixup))
+ in
+
+ let encode_effect eff =
+ (* Note: weird encoding: mutable+pure = unsafe. *)
+ let mut_byte, pure_byte =
+ match eff with
+ Ast.UNSAFE -> (1,1)
+ | Ast.STATE -> (1,0)
+ | Ast.IO -> (0,0)
+ | Ast.PURE -> (0,1)
+ in
+ SEQ [|
+ (* DW_AT_mutable: DW_FORM_flag *)
+ BYTE mut_byte;
+ (* DW_AT_pure: DW_FORM_flag *)
+ BYTE pure_byte;
+ |]
+ in
+
+ (* Type-param DIEs. *)
+
+ let type_param_die (p:(ty_param_idx * Ast.effect)) =
+ let (idx, eff) = p in
+ SEQ [|
+ uleb (get_abbrev_code abbrev_rust_type_param);
+ (* DW_AT_rust_type_code: DW_FORM_data1 *)
+ BYTE (dw_rust_type_to_int DW_RUST_type_param);
+ (* DW_AT_rust_type_param_index: DW_FORM_data4 *)
+ WORD (word_ty_mach, IMM (Int64.of_int idx));
+ encode_effect eff;
+ |]
+ in
+
+ (* Type DIEs. *)
+
+ let (emitted_types:(Ast.ty, Asm.frag) Hashtbl.t) = Hashtbl.create 0 in
+ let (emitted_slots:(Ast.slot, Asm.frag) Hashtbl.t) = Hashtbl.create 0 in
+
+ let rec ref_slot_die
+ (slot:Ast.slot)
+ : frag =
+ if Hashtbl.mem emitted_slots slot
+ then Hashtbl.find emitted_slots slot
+ else
+ let ref_addr_for_fix fix =
+ let res = dw_form_ref_addr fix in
+ Hashtbl.add emitted_slots slot res;
+ res
+ in
+
+ match slot.Ast.slot_mode with
+ Ast.MODE_exterior ->
+ let fix = new_fixup "exterior DIE" in
+ let body_off =
+ word_sz_int * Abi.exterior_rc_slot_field_body
+ in
+ emit_die (DEF (fix, SEQ [|
+ uleb (get_abbrev_code abbrev_exterior_slot);
+ (* DW_AT_type: DW_FORM_ref_addr *)
+ (ref_type_die (slot_ty slot));
+ (* DW_AT_mutable: DW_FORM_flag *)
+ BYTE (if slot.Ast.slot_mutable
+ then 1 else 0);
+ (* DW_AT_data_location: DW_FORM_block1 *)
+ (* This is a DWARF expression for moving
+ from the address of an exterior
+ allocation to the address of its
+ body. *)
+ dw_form_block1
+ [| DW_OP_push_object_address;
+ DW_OP_lit body_off;
+ DW_OP_plus;
+ DW_OP_deref |]
+ |]));
+ ref_addr_for_fix fix
+
+ (* FIXME: encode mutable-ness of interiors. *)
+ | Ast.MODE_interior -> ref_type_die (slot_ty slot)
+
+ | Ast.MODE_alias ->
+ let fix = new_fixup "alias DIE" in
+ emit_die (DEF (fix, SEQ [|
+ uleb (get_abbrev_code abbrev_alias_slot);
+ (* DW_AT_type: DW_FORM_ref_addr *)
+ (ref_type_die (slot_ty slot));
+ (* DW_AT_mutable: DW_FORM_flag *)
+ BYTE (if slot.Ast.slot_mutable then 1 else 0)
+ |]));
+ ref_addr_for_fix fix
+
+
+ and size_block4 (sz:size) (add_to_base:bool) : frag =
+ (* NB: typarams = "words following implicit args" by convention in
+ * ABI/x86.
+ *)
+ let abi = cx.ctxt_abi in
+ let typarams =
+ Int64.add abi.Abi.abi_frame_base_sz abi.Abi.abi_implicit_args_sz
+ in
+ let word_n n = Int64.mul abi.Abi.abi_word_sz (Int64.of_int n) in
+ let param_n n = Int64.add typarams (word_n n) in
+ let param_n_field_k n k =
+ [ DW_OP_fbreg (IMM (param_n n));
+ DW_OP_deref;
+ DW_OP_constu (IMM (word_n k));
+ DW_OP_plus;
+ DW_OP_deref ]
+ in
+ let rec sz_ops (sz:size) : dw_op list =
+ match sz with
+ SIZE_fixed i ->
+ [ DW_OP_constu (IMM i) ]
+
+ | SIZE_fixup_mem_sz fix ->
+ [ DW_OP_constu (M_SZ fix) ]
+
+ | SIZE_fixup_mem_pos fix ->
+ [ DW_OP_constu (M_POS fix) ]
+
+ | SIZE_param_size i ->
+ param_n_field_k i Abi.tydesc_field_size
+
+ | SIZE_param_align i ->
+ param_n_field_k i Abi.tydesc_field_align
+
+ | SIZE_rt_neg s ->
+ (sz_ops s) @ [ DW_OP_neg ]
+
+ | SIZE_rt_add (a, b) ->
+ (sz_ops a) @ (sz_ops b) @ [ DW_OP_plus ]
+
+ | SIZE_rt_mul (a, b) ->
+ (sz_ops a) @ (sz_ops b) @ [ DW_OP_mul ]
+
+ | SIZE_rt_max (a, b) ->
+ (sz_ops a) @ (sz_ops b) @
+ [ DW_OP_over; (* ... a b a *)
+ DW_OP_over; (* ... a b a b *)
+ DW_OP_ge; (* ... a b (a>=b?1:0) *)
+
+ (* jump +1 byte of dwarf ops if 1 *)
+ DW_OP_bra (IMM 1L);
+
+ (* do this if 0, when b is max. *)
+ DW_OP_swap; (* ... b a *)
+
+ (* jump to here when a is max. *)
+ DW_OP_drop; (* ... max *)
+ ]
+
+ | SIZE_rt_align (align, off) ->
+ (*
+ * calculate off + pad where:
+ *
+ * pad = (align - (off mod align)) mod align
+ *
+ * In our case it's always a power of two,
+ * so we can just do:
+ *
+ * mask = align-1
+ * off += mask
+ * off &= ~mask
+ *
+ *)
+ (sz_ops off) @ (sz_ops align) @
+ [
+ DW_OP_lit 1; (* ... off align 1 *)
+ DW_OP_minus; (* ... off mask *)
+ DW_OP_dup; (* ... off mask mask *)
+ DW_OP_not; (* ... off mask ~mask *)
+ DW_OP_rot; (* ... ~mask off mask *)
+ DW_OP_plus; (* ... ~mask (off+mask) *)
+ DW_OP_and; (* ... aligned *)
+ ]
+ in
+ let ops = sz_ops sz in
+ let ops =
+ if add_to_base
+ then ops @ [ DW_OP_plus ]
+ else ops
+ in
+ let frag = SEQ (Array.map (dw_op_to_frag abi) (Array.of_list ops)) in
+ let block_fixup = new_fixup "DW_FORM_block4 fixup" in
+ SEQ [| WORD (TY_u32, F_SZ block_fixup);
+ DEF (block_fixup, frag) |]
+
+
+ and ref_type_die
+ (ty:Ast.ty)
+ : frag =
+ (* Returns a DW_FORM_ref_addr to the type. *)
+ if Hashtbl.mem emitted_types ty
+ then Hashtbl.find emitted_types ty
+ else
+ let ref_addr_for_fix fix =
+ let res = dw_form_ref_addr fix in
+ Hashtbl.add emitted_types ty res;
+ res
+ in
+
+ let record trec =
+ let rty = referent_type abi (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 [|
+ uleb (get_abbrev_code abbrev_struct_type);
+ (* DW_AT_byte_size: DW_FORM_block4 *)
+ size_block4 (rty_sz rty) false
+ |]);
+ in
+ let rtys =
+ match rty with
+ Il.StructTy rtys -> rtys
+ | _ -> bug () "record type became non-struct referent_ty"
+ in
+ emit_die die;
+ Array.iteri
+ begin
+ fun i (ident, slot) ->
+ emit_die (SEQ [|
+ uleb (get_abbrev_code abbrev_struct_type_member);
+ (* DW_AT_name: DW_FORM_string *)
+ ZSTRING ident;
+ (* DW_AT_type: DW_FORM_ref_addr *)
+ (ref_slot_die slot);
+ (* DW_AT_mutable: DW_FORM_flag *)
+ BYTE (if slot.Ast.slot_mutable then 1 else 0);
+ (* DW_AT_data_member_location: DW_FORM_block4 *)
+ size_block4
+ (Il.get_element_offset word_bits rtys i)
+ true;
+ (* DW_AT_byte_size: DW_FORM_block4 *)
+ size_block4 (rty_sz rtys.(i)) false |]);
+ end
+ trec;
+ emit_null_die ();
+ ref_addr_for_fix fix
+ in
+
+ let string_type _ =
+ (*
+ * Strings, like vecs, are &[rc,alloc,fill,data...]
+ *)
+ let fix = new_fixup "string type DIE" in
+ let die =
+ DEF (fix, SEQ [|
+ uleb (get_abbrev_code abbrev_string_type);
+ (* (DW_AT_byte_size, DW_FORM_block1); *)
+ dw_form_block1 [| DW_OP_push_object_address;
+ DW_OP_deref;
+ DW_OP_lit (word_sz_int * 2);
+ DW_OP_plus; |];
+ (* (DW_AT_data_location, DW_FORM_block1); *)
+ dw_form_block1 [| DW_OP_push_object_address;
+ DW_OP_deref;
+ DW_OP_lit (word_sz_int * 3);
+ DW_OP_plus |]
+ |])
+ in
+ emit_die die;
+ ref_addr_for_fix fix
+ in
+
+ let base (name, encoding, byte_size) =
+ let fix = new_fixup ("base type DIE: " ^ name) in
+ let die =
+ DEF (fix, SEQ [|
+ uleb (get_abbrev_code abbrev_base_type);
+ (* DW_AT_name: DW_FORM_string *)
+ ZSTRING name;
+ (* DW_AT_encoding: DW_FORM_data1 *)
+ BYTE (dw_ate_to_int encoding);
+ (* DW_AT_byte_size: DW_FORM_data1 *)
+ BYTE byte_size
+ |])
+ in
+ emit_die die;
+ ref_addr_for_fix fix
+ in
+
+ let unspecified_anon_struct _ =
+ let fix = new_fixup "unspecified-anon-struct DIE" in
+ let die =
+ DEF (fix, SEQ [|
+ uleb (get_abbrev_code
+ abbrev_unspecified_anon_structure_type);
+ (* DW_AT_declaration: DW_FORM_flag *)
+ BYTE 1;
+ |])
+ in
+ emit_die die;
+ ref_addr_for_fix fix
+ in
+
+ let unspecified_struct rust_ty =
+ let fix = new_fixup "unspecified-struct DIE" in
+ let die =
+ DEF (fix, SEQ [|
+ uleb (get_abbrev_code abbrev_unspecified_structure_type);
+ (* DW_AT_rust_type_code: DW_FORM_data1 *)
+ BYTE (dw_rust_type_to_int rust_ty);
+ (* DW_AT_declaration: DW_FORM_flag *)
+ BYTE 1;
+ |])
+ in
+ emit_die die;
+ ref_addr_for_fix fix
+ in
+
+ let rust_type_param (p:(ty_param_idx * Ast.effect)) =
+ let fix = new_fixup "rust-type-param DIE" in
+ let die = DEF (fix, type_param_die p) in
+ emit_die die;
+ ref_addr_for_fix fix
+ in
+
+ let unspecified_ptr_with_ref rust_ty ref_addr =
+ let fix = new_fixup ("unspecified-pointer-type-with-ref DIE") in
+ let die =
+ DEF (fix, SEQ [|
+ uleb (get_abbrev_code abbrev_unspecified_pointer_type);
+ (* DW_AT_rust_type_code: DW_FORM_data1 *)
+ BYTE (dw_rust_type_to_int rust_ty);
+ (* DW_AT_declaration: DW_FORM_flag *)
+ BYTE 1;
+ (* DW_AT_type: DW_FORM_ref_addr *)
+ ref_addr
+ |])
+ in
+ emit_die die;
+ ref_addr_for_fix fix
+ in
+
+ let formal_type slot =
+ let fix = new_fixup "formal type" in
+ let die =
+ DEF (fix, SEQ [|
+ uleb (get_abbrev_code abbrev_formal_type);
+ (* DW_AT_type: DW_FORM_ref_addr *)
+ (ref_slot_die slot);
+ |])
+ in
+ emit_die die;
+ ref_addr_for_fix fix
+ in
+
+ let fn_type tfn =
+ let (tsig, taux) = tfn in
+ let fix = new_fixup "fn type" in
+ let die =
+ DEF (fix, SEQ [|
+ uleb (get_abbrev_code abbrev_subroutine_type);
+ (* DW_AT_type: DW_FORM_ref_addr *)
+ (ref_slot_die tsig.Ast.sig_output_slot);
+ encode_effect taux.Ast.fn_effect;
+ (* DW_AT_rust_iterator: DW_FORM_flag *)
+ BYTE (if taux.Ast.fn_is_iter then 1 else 0)
+ |])
+ in
+ emit_die die;
+ Array.iter
+ (fun s -> ignore (formal_type s))
+ tsig.Ast.sig_input_slots;
+ emit_null_die ();
+ ref_addr_for_fix fix
+ in
+
+ let obj_fn_type ident tfn =
+ let (tsig, taux) = tfn in
+ let fix = new_fixup "fn type" in
+ let die =
+ DEF (fix, SEQ [|
+ uleb (get_abbrev_code abbrev_obj_subroutine_type);
+ (* DW_AT_name: DW_FORM_string *)
+ ZSTRING ident;
+ (* DW_AT_type: DW_FORM_ref_addr *)
+ (ref_slot_die tsig.Ast.sig_output_slot);
+ encode_effect taux.Ast.fn_effect;
+ (* DW_AT_rust_iterator: DW_FORM_flag *)
+ BYTE (if taux.Ast.fn_is_iter then 1 else 0)
+ |])
+ in
+ emit_die die;
+ Array.iter
+ (fun s -> ignore (formal_type s))
+ tsig.Ast.sig_input_slots;
+ emit_null_die ();
+ ref_addr_for_fix fix
+ in
+
+ let obj_type (eff,ob) =
+ let fix = new_fixup "object type" in
+ let die =
+ DEF (fix, SEQ [|
+ uleb (get_abbrev_code abbrev_obj_type);
+ encode_effect eff;
+ |])
+ in
+ emit_die die;
+ Hashtbl.iter (fun k v -> ignore (obj_fn_type k v)) ob;
+ emit_null_die ();
+ ref_addr_for_fix fix
+ in
+
+ let unspecified_ptr_with_ref_ty rust_ty ty =
+ unspecified_ptr_with_ref rust_ty (ref_type_die ty)
+ in
+
+ let unspecified_ptr_with_ref_slot rust_ty slot =
+ unspecified_ptr_with_ref rust_ty (ref_slot_die slot)
+ in
+
+ let unspecified_ptr rust_ty =
+ unspecified_ptr_with_ref rust_ty (unspecified_anon_struct ())
+ in
+
+ let native_ptr_type oid =
+ let fix = new_fixup "native type" in
+ let die =
+ DEF (fix, SEQ [|
+ uleb (get_abbrev_code abbrev_native_pointer_type);
+ (* DW_AT_rust_type_code: DW_FORM_data1 *)
+ BYTE (dw_rust_type_to_int DW_RUST_native);
+ (* DW_AT_rust_native_type_id: DW_FORM_data4 *)
+ WORD (word_ty_mach, IMM (Int64.of_int (int_of_opaque oid)));
+ |])
+ in
+ emit_die die;
+ ref_addr_for_fix fix
+ in
+
+ match ty with
+ Ast.TY_nil -> unspecified_struct DW_RUST_nil
+ | Ast.TY_bool -> base ("bool", DW_ATE_boolean, 1)
+ | Ast.TY_mach (TY_u8) -> base ("u8", DW_ATE_unsigned, 1)
+ | Ast.TY_mach (TY_u16) -> base ("u16", DW_ATE_unsigned, 2)
+ | Ast.TY_mach (TY_u32) -> base ("u32", DW_ATE_unsigned, 4)
+ | Ast.TY_mach (TY_u64) -> base ("u64", DW_ATE_unsigned, 8)
+ | Ast.TY_mach (TY_i8) -> base ("i8", DW_ATE_signed, 1)
+ | Ast.TY_mach (TY_i16) -> base ("i16", DW_ATE_signed, 2)
+ | Ast.TY_mach (TY_i32) -> base ("i32", DW_ATE_signed, 4)
+ | Ast.TY_mach (TY_i64) -> base ("i64", DW_ATE_signed, 8)
+ | Ast.TY_int -> base ("int", DW_ATE_signed, word_sz_int)
+ | Ast.TY_uint -> base ("uint", DW_ATE_unsigned, word_sz_int)
+ | Ast.TY_char -> base ("char", DW_ATE_unsigned_char, 4)
+ | Ast.TY_str -> string_type ()
+ | Ast.TY_rec trec -> record trec
+ | Ast.TY_tup ttup ->
+ record (Array.mapi (fun i s ->
+ ("_" ^ (string_of_int i), s))
+ ttup)
+
+ | Ast.TY_vec s -> unspecified_ptr_with_ref_slot DW_RUST_vec s
+ | Ast.TY_chan t -> unspecified_ptr_with_ref_ty DW_RUST_chan t
+ | Ast.TY_port t -> unspecified_ptr_with_ref_ty DW_RUST_port t
+ | Ast.TY_task -> unspecified_ptr DW_RUST_task
+ | Ast.TY_fn fn -> fn_type fn
+ | Ast.TY_tag _ -> unspecified_ptr DW_RUST_tag
+ | Ast.TY_iso _ -> unspecified_ptr DW_RUST_iso
+ | Ast.TY_type -> unspecified_ptr DW_RUST_type
+ | Ast.TY_native i -> native_ptr_type i
+ | Ast.TY_param p -> rust_type_param p
+ | Ast.TY_obj ob -> obj_type ob
+ | _ ->
+ bug () "unimplemented dwarf encoding for type %a"
+ Ast.sprintf_ty ty
+ in
+
+ let finish_crate_cu_and_compose_headers _ =
+
+ let pubnames_header_and_curr_pubnames =
+ SEQ [| (BYTE 0) |]
+ in
+
+ let aranges_header_and_curr_aranges =
+ SEQ [| (BYTE 0) |]
+ in
+
+ let cu_info_fixup = new_fixup "CU debug_info fixup" in
+ let info_header_fixup = new_fixup "CU debug_info header" in
+ let info_header_and_curr_infos =
+ SEQ
+ [|
+ WORD (TY_u32, (* unit_length: *)
+ (ADD
+ ((F_SZ cu_info_fixup), (* including this header,*)
+ (F_SZ info_header_fixup)))); (* excluding this word. *)
+ DEF (info_header_fixup,
+ (SEQ [|
+ WORD (TY_u16, IMM 2L); (* DWARF version *)
+ (* Since we share abbrevs across all CUs,
+ * offset is always 0.
+ *)
+ WORD (TY_u32, IMM 0L); (* CU-abbrev offset. *)
+ BYTE 4; (* Size of an address. *)
+ |]));
+ DEF (cu_info_fixup,
+ SEQ (Array.of_list (List.rev (!curr_cu_infos))));
+ |]
+ in
+
+ let cu_line_fixup = new_fixup "CU debug_line fixup" in
+ let cu_line_header_fixup = new_fixup "CU debug_line header" in
+ let line_header_fixup = new_fixup "CU debug_line header" in
+ let line_header_and_curr_line =
+ SEQ
+ [|
+ WORD
+ (TY_u32, (* unit_length: *)
+ (ADD
+ ((F_SZ cu_line_fixup), (* including this header,*)
+ (F_SZ cu_line_header_fixup)))); (* excluding this word. *)
+ DEF (cu_line_header_fixup,
+ (SEQ [|
+ WORD (TY_u16, IMM 2L); (* DWARF version. *)
+ WORD
+ (TY_u32,
+ (F_SZ line_header_fixup)); (* Another header-length.*)
+ DEF (line_header_fixup,
+ SEQ [|
+ BYTE 1; (* Minimum insn length. *)
+ BYTE 1; (* default_is_stmt *)
+ BYTE 0; (* line_base *)
+ BYTE 0; (* line_range *)
+ BYTE (max_dw_lns + 1); (* opcode_base *)
+ BYTES (* opcode arity array. *)
+ (Array.init max_dw_lns
+ (fun i ->
+ (dw_lns_arity
+ (int_to_dw_lns
+ (i+1)))));
+ (BYTE 0); (* List of include dirs. *)
+ (BYTE 0); (* List of file entries. *)
+ |])|]));
+ DEF (cu_line_fixup,
+ SEQ (Array.of_list (List.rev (!curr_cu_line))));
+ |]
+ in
+ let frame_header_and_curr_frame =
+ SEQ [| (BYTE 0) |]
+ in
+ let prepend_and_reset (curr_ref, accum_ref, header_and_curr) =
+ prepend accum_ref header_and_curr;
+ curr_ref := []
+ in
+ List.iter prepend_and_reset
+ [ (curr_cu_aranges, cu_aranges, aranges_header_and_curr_aranges);
+ (curr_cu_pubnames, cu_pubnames, pubnames_header_and_curr_pubnames);
+ (curr_cu_infos, cu_infos, info_header_and_curr_infos);
+ (curr_cu_line, cu_lines, line_header_and_curr_line);
+ (curr_cu_frame, cu_frames, frame_header_and_curr_frame) ]
+ in
+
+ let image_base_rel (fix:fixup) : expr64 =
+ SUB (M_POS (fix), M_POS (cx.ctxt_image_base_fixup))
+ 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
+ then image_base_rel fix
+ else M_POS fix
+ in
+ SEQ [|
+ (* DW_AT_low_pc, DW_FORM_addr *)
+ WORD (word_ty_mach, lo);
+ (* DW_AT_high_pc, DW_FORM_addr *)
+ WORD (word_ty_mach, ADD ((lo),
+ (M_SZ fix)))
+ |]
+ in
+
+ let emit_srcfile_cu_die
+ (name:string)
+ (cu_text_fixup:fixup)
+ : unit =
+ let abbrev_code = get_abbrev_code abbrev_srcfile_cu in
+ let srcfile_cu_die =
+ (SEQ [|
+ uleb abbrev_code;
+ (* DW_AT_name: DW_FORM_string *)
+ ZSTRING (Filename.basename name);
+ (* DW_AT_comp_dir: DW_FORM_string *)
+ ZSTRING (Filename.concat (Sys.getcwd()) (Filename.dirname name));
+ addr_ranges cu_text_fixup;
+ |])
+ in
+ emit_die srcfile_cu_die
+ in
+
+ let emit_meta_die
+ (meta:(Ast.ident * string))
+ : unit =
+ let abbrev_code = get_abbrev_code abbrev_meta in
+ let die =
+ SEQ [| uleb abbrev_code;
+ (* DW_AT_name: DW_FORM_string *)
+ ZSTRING (fst meta);
+ (* DW_AT_const_value: DW_FORM_string *)
+ ZSTRING (snd meta);
+ |]
+ in
+ emit_die die
+ in
+
+ let begin_crate_cu_and_emit_cu_die
+ (name:string)
+
+ (cu_text_fixup:fixup)
+ : unit =
+ let abbrev_code = get_abbrev_code abbrev_crate_cu in
+ let crate_cu_die =
+ (SEQ [|
+ uleb abbrev_code;
+ (* DW_AT_producer: DW_FORM_string *)
+ ZSTRING "Rustboot pre-release";
+ (* DW_AT_language: DW_FORM_data4 *)
+ WORD (word_ty_mach, IMM 0x2L); (* DW_LANG_C *)
+ (* DW_AT_name: DW_FORM_string *)
+ ZSTRING (Filename.basename name);
+ (* DW_AT_comp_dir: DW_FORM_string *)
+ ZSTRING (Filename.concat (Sys.getcwd()) (Filename.dirname name));
+ addr_ranges cu_text_fixup;
+ (* DW_AT_use_UTF8, DW_FORM_flag *)
+ BYTE 1
+ |])
+ in
+ curr_cu_infos := [crate_cu_die];
+ curr_cu_line := []
+ in
+
+ let type_param_decl_die (p:(Ast.ident * (ty_param_idx * Ast.effect))) =
+ let (ident, (idx, eff)) = p in
+ SEQ [|
+ uleb (get_abbrev_code abbrev_rust_type_param_decl);
+ (* DW_AT_rust_type_code: DW_FORM_data1 *)
+ BYTE (dw_rust_type_to_int DW_RUST_type_param);
+ (* DW_AT_name: DW_FORM_string *)
+ ZSTRING (Filename.basename ident);
+ (* DW_AT_rust_type_param_index: DW_FORM_data4 *)
+ WORD (word_ty_mach, IMM (Int64.of_int idx));
+ encode_effect eff;
+ |]
+ in
+
+ let emit_type_param_decl_dies
+ (params:(Ast.ty_param identified) array)
+ : unit =
+ Array.iter
+ (fun p ->
+ emit_die (type_param_decl_die p.node))
+ params;
+ in
+
+ let emit_module_die
+ (id:Ast.ident)
+ : unit =
+ let abbrev_code = get_abbrev_code abbrev_module in
+ let module_die =
+ (SEQ [|
+ uleb abbrev_code;
+ (* DW_AT_name *)
+ ZSTRING id;
+ |])
+ in
+ emit_die module_die;
+ in
+
+ let emit_subprogram_die
+ (id:Ast.ident)
+ (ret_slot:Ast.slot)
+ (effect:Ast.effect)
+ (fix:fixup)
+ : unit =
+ (* NB: retpc = "top word of frame-base" by convention in ABI/x86. *)
+ let abi = cx.ctxt_abi in
+ let retpc = Int64.sub abi.Abi.abi_frame_base_sz abi.Abi.abi_word_sz in
+ let abbrev_code = get_abbrev_code abbrev_subprogram in
+ let subprogram_die =
+ (SEQ [|
+ uleb abbrev_code;
+ (* DW_AT_name *)
+ ZSTRING id;
+ (* DW_AT_type: DW_FORM_ref_addr *)
+ ref_slot_die ret_slot;
+ addr_ranges fix;
+ (* DW_AT_frame_base *)
+ dw_form_block1 [| DW_OP_reg abi.Abi.abi_dwarf_fp_reg |];
+ (* DW_AT_return_addr *)
+ dw_form_block1 [| DW_OP_fbreg (Asm.IMM retpc); |];
+ encode_effect effect;
+ |])
+ in
+ emit_die subprogram_die
+ in
+
+ let emit_typedef_die
+ (id:Ast.ident)
+ (ty:Ast.ty)
+ : unit =
+ let abbrev_code = get_abbrev_code abbrev_typedef in
+ let typedef_die =
+ (SEQ [|
+ uleb abbrev_code;
+ (* DW_AT_name: DW_FORM_string *)
+ ZSTRING id;
+ (* DW_AT_type: DW_FORM_ref_addr *)
+ (ref_type_die ty);
+ |])
+ in
+ emit_die typedef_die
+ in
+
+ let visit_crate_pre
+ (crate:Ast.crate)
+ : unit =
+ let filename = (Hashtbl.find cx.ctxt_item_files crate.id) in
+ log cx "walking crate CU '%s'" filename;
+ begin_crate_cu_and_emit_cu_die filename
+ (Hashtbl.find cx.ctxt_file_fixups crate.id);
+ Array.iter emit_meta_die crate.node.Ast.crate_meta;
+ inner.Walk.visit_crate_pre crate
+ in
+
+ let visit_mod_item_pre
+ (id:Ast.ident)
+ (params:(Ast.ty_param identified) array)
+ (item:Ast.mod_item)
+ : unit =
+ if Hashtbl.mem cx.ctxt_item_files item.id
+ then
+ begin
+ let filename = (Hashtbl.find cx.ctxt_item_files item.id) in
+ log cx "walking srcfile CU '%s'" filename;
+ emit_srcfile_cu_die filename
+ (Hashtbl.find cx.ctxt_file_fixups item.id);
+ end
+ else
+ ();
+ begin
+ match item.node.Ast.decl_item with
+ Ast.MOD_ITEM_mod _ ->
+ begin
+ log cx "walking module '%s' with %d type params"
+ (path_name())
+ (Array.length item.node.Ast.decl_params);
+ emit_module_die id;
+ emit_type_param_decl_dies item.node.Ast.decl_params;
+ end
+ | Ast.MOD_ITEM_fn _ ->
+ begin
+ let ty = Hashtbl.find cx.ctxt_all_item_types item.id in
+ let (tsig,taux) =
+ match ty with
+ Ast.TY_fn tfn -> tfn
+ | _ ->
+ bug ()
+ "non-fn type when emitting dwarf for MOD_ITEM_fn"
+ in
+ log cx "walking function '%s' with %d type params"
+ (path_name())
+ (Array.length item.node.Ast.decl_params);
+ emit_subprogram_die
+ id tsig.Ast.sig_output_slot taux.Ast.fn_effect
+ (Hashtbl.find cx.ctxt_fn_fixups item.id);
+ emit_type_param_decl_dies item.node.Ast.decl_params;
+ end
+ | Ast.MOD_ITEM_type _ ->
+ begin
+ log cx "walking typedef '%s' with %d type params"
+ (path_name())
+ (Array.length item.node.Ast.decl_params);
+ emit_typedef_die
+ id (Hashtbl.find cx.ctxt_all_type_items item.id);
+ emit_type_param_decl_dies item.node.Ast.decl_params;
+ end
+ | _ -> ()
+ end;
+ inner.Walk.visit_mod_item_pre id params item
+ in
+
+ let visit_crate_post
+ (crate:Ast.crate)
+ : unit =
+ inner.Walk.visit_crate_post crate;
+ assert (Hashtbl.mem cx.ctxt_item_files crate.id);
+ emit_null_die();
+ log cx
+ "finishing crate CU and composing headers (%d DIEs collected)"
+ (List.length (!curr_cu_infos));
+ finish_crate_cu_and_compose_headers ()
+ in
+
+ let visit_mod_item_post
+ (id:Ast.ident)
+ (params:(Ast.ty_param identified) array)
+ (item:Ast.mod_item)
+ : unit =
+ inner.Walk.visit_mod_item_post id params item;
+ begin
+ match item.node.Ast.decl_item with
+ Ast.MOD_ITEM_mod _
+ | Ast.MOD_ITEM_fn _
+ | Ast.MOD_ITEM_type _ -> emit_null_die ()
+ | _ -> ()
+ end;
+ if Hashtbl.mem cx.ctxt_item_files item.id
+ then emit_null_die()
+ in
+
+ let visit_block_pre (b:Ast.block) : unit =
+ log cx "entering lexical block";
+ let fix = Hashtbl.find cx.ctxt_block_fixups b.id in
+ let abbrev_code = get_abbrev_code abbrev_lexical_block in
+ let block_die =
+ SEQ [|
+ uleb abbrev_code;
+ addr_ranges fix;
+ |]
+ in
+ emit_die block_die;
+ inner.Walk.visit_block_pre b
+ in
+
+ let visit_block_post (b:Ast.block) : unit =
+ inner.Walk.visit_block_post b;
+ log cx "leaving lexical block, terminating with NULL DIE";
+ emit_null_die ()
+ in
+
+ let visit_slot_identified_pre (s:Ast.slot identified) : unit =
+ begin
+ match htab_search cx.ctxt_slot_keys s.id with
+ None
+ | Some Ast.KEY_temp _ -> ()
+ | Some Ast.KEY_ident ident ->
+ begin
+ let abbrev_code =
+ if Hashtbl.mem cx.ctxt_slot_is_arg s.id
+ then get_abbrev_code abbrev_formal
+ else get_abbrev_code abbrev_variable
+ in
+ let resolved_slot = referent_to_slot cx s.id in
+ let emit_var_die slot_loc =
+ let var_die =
+ SEQ [|
+ uleb abbrev_code;
+ (* DW_AT_name: DW_FORM_string *)
+ ZSTRING ident;
+ (* DW_AT_location: DW_FORM_block1 *)
+ dw_form_block1 slot_loc;
+ (* DW_AT_type: DW_FORM_ref_addr *)
+ ref_slot_die resolved_slot
+ |]
+ in
+ emit_die var_die;
+ in
+ match htab_search cx.ctxt_slot_offsets s.id with
+ Some off ->
+ begin
+ match Il.size_to_expr64 off with
+ (* FIXME: handle dynamic-size slots. *)
+ None -> ()
+ | Some off ->
+ emit_var_die
+ [| DW_OP_fbreg off |]
+ end
+ | None ->
+ (* FIXME (issue #28): handle slots assigned to
+ * vregs.
+ *)
+ ()
+ end
+ end;
+ inner.Walk.visit_slot_identified_pre s
+ in
+
+ { inner with
+ Walk.visit_crate_pre = visit_crate_pre;
+ Walk.visit_crate_post = visit_crate_post;
+ Walk.visit_mod_item_pre = visit_mod_item_pre;
+ Walk.visit_mod_item_post = visit_mod_item_post;
+ Walk.visit_block_pre = visit_block_pre;
+ Walk.visit_block_post = visit_block_post;
+ Walk.visit_slot_identified_pre = visit_slot_identified_pre
+ }
+;;
+
+
+let process_crate
+ (cx:ctxt)
+ (crate:Ast.crate)
+ : debug_records =
+
+ let cu_aranges = ref [] in
+ let cu_pubnames = ref [] in
+ let cu_infos = ref [] in
+ let cu_abbrevs = ref [] in
+ let cu_lines = ref [] in
+ let cu_frames = ref [] in
+
+ let path = Stack.create () in
+
+ let passes =
+ [|
+ dwarf_visitor cx Walk.empty_visitor path
+ cx.ctxt_debug_info_fixup
+ cu_aranges cu_pubnames
+ cu_infos cu_abbrevs
+ cu_lines cu_frames
+ |];
+ in
+
+ log cx "emitting DWARF records";
+ run_passes cx "dwarf" path passes (log cx "%s") crate;
+
+ (* Terminate the tables. *)
+ {
+ debug_aranges = SEQ (Array.of_list (List.rev (!cu_aranges)));
+ debug_pubnames = SEQ (Array.of_list (List.rev (!cu_pubnames)));
+ debug_info = SEQ (Array.of_list (List.rev (!cu_infos)));
+ debug_abbrev = SEQ (Array.of_list (List.rev (!cu_abbrevs)));
+ debug_line = SEQ (Array.of_list (List.rev (!cu_lines)));
+ debug_frame = SEQ (Array.of_list (List.rev (!cu_frames)));
+ }
+;;
+
+(*
+ * Support for reconstituting a DWARF tree from a file, and various
+ * artifacts we can distill back from said DWARF.
+ *)
+
+let log sess = Session.log "dwarf"
+ sess.Session.sess_log_dwarf
+ sess.Session.sess_log_out
+;;
+
+
+let iflog (sess:Session.sess) (thunk:(unit -> unit)) : unit =
+ if sess.Session.sess_log_dwarf
+ then thunk ()
+ else ()
+;;
+
+let read_abbrevs
+ (sess:Session.sess)
+ (ar:asm_reader)
+ ((off:int),(sz:int))
+ : (int,abbrev) Hashtbl.t =
+ ar.asm_seek off;
+ let abs = Hashtbl.create 0 in
+ let rec read_abbrevs _ =
+ if ar.asm_get_off() >= (off + sz)
+ then abs
+ else
+ begin
+ let n = ar.asm_get_uleb() in
+ let tag = ar.asm_get_uleb() in
+ let has_children = ar.asm_get_u8() in
+ let pairs = ref [] in
+ let _ =
+ log sess "abbrev: %d, tag: %d, has_children: %d"
+ n tag has_children
+ in
+ let rec read_pairs _ =
+ let attr = ar.asm_get_uleb() in
+ let form = ar.asm_get_uleb() in
+ let _ = log sess "attr: %d, form: %d" attr form in
+ match (attr,form) with
+ (0,0) -> Array.of_list (List.rev (!pairs))
+ | _ ->
+ begin
+ pairs := (dw_at_of_int attr,
+ dw_form_of_int form) :: (!pairs);
+ read_pairs()
+ end
+ in
+ let pairs = read_pairs() in
+ Hashtbl.add abs n (dw_tag_of_int tag,
+ dw_children_of_int has_children,
+ pairs);
+ read_abbrevs()
+ end;
+ in
+ read_abbrevs()
+;;
+
+type data =
+ DATA_str of string
+ | DATA_num of int
+ | DATA_other
+;;
+
+type die =
+ { die_off: int;
+ die_tag: dw_tag;
+ die_attrs: (dw_at * (dw_form * data)) array;
+ die_children: die array; }
+;;
+
+type rooted_dies = (int * (int,die) Hashtbl.t)
+;;
+
+let fmt_dies
+ (ff:Format.formatter)
+ (dies:rooted_dies)
+ : unit =
+ let ((root:int),(dies:(int,die) Hashtbl.t)) = dies in
+ let rec fmt_die die =
+ Ast.fmt ff "@\nDIE <0x%x> %s" die.die_off (dw_tag_to_string die.die_tag);
+ Array.iter
+ begin
+ fun (at,(form,data)) ->
+ Ast.fmt ff "@\n %s = " (dw_at_to_string at);
+ begin
+ match data with
+ DATA_num n -> Ast.fmt ff "0x%x" n
+ | DATA_str s -> Ast.fmt ff "\"%s\"" s
+ | DATA_other -> Ast.fmt ff "<other>"
+ end;
+ Ast.fmt ff " (%s)" (dw_form_to_string form)
+ end
+ die.die_attrs;
+ if (Array.length die.die_children) != 0
+ then
+ begin
+ Ast.fmt ff "@\n";
+ Ast.fmt_obox ff;
+ Ast.fmt ff " children: ";
+ Ast.fmt_obr ff;
+ Array.iter fmt_die die.die_children;
+ Ast.fmt_cbb ff
+ end;
+ in
+ fmt_die (Hashtbl.find dies root)
+;;
+
+let read_dies
+ (sess:Session.sess)
+ (ar:asm_reader)
+ ((off:int),(sz:int))
+ (abbrevs:(int,abbrev) Hashtbl.t)
+ : (int * ((int,die) Hashtbl.t)) =
+ ar.asm_seek off;
+ let cu_len = ar.asm_get_u32() in
+ let _ = log sess "debug_info cu_len: %d, section size %d" cu_len sz in
+ let _ = assert ((cu_len + 4) = sz) in
+ let dwarf_vers = ar.asm_get_u16() in
+ let _ = assert (dwarf_vers >= 2) in
+ let cu_abbrev_off = ar.asm_get_u32() in
+ let _ = assert (cu_abbrev_off = 0) in
+ let sizeof_addr = ar.asm_get_u8() in
+ let _ = assert (sizeof_addr = 4) in
+
+ let adv_block1 _ =
+ let len = ar.asm_get_u8() in
+ ar.asm_adv len
+ in
+
+ let adv_block4 _ =
+ let len = ar.asm_get_u32() in
+ ar.asm_adv len
+ in
+
+ let all_dies = Hashtbl.create 0 in
+ let root = (ar.asm_get_off()) - off in
+
+ let rec read_dies (dies:(die list) ref) =
+ let die_arr _ = Array.of_list (List.rev (!dies)) in
+ if ar.asm_get_off() >= (off + sz)
+ then die_arr()
+ else
+ begin
+ let die_off = (ar.asm_get_off()) - off in
+ let abbrev_num = ar.asm_get_uleb() in
+ if abbrev_num = 0
+ then die_arr()
+ else
+ let _ =
+ log sess "DIE at off <%d> with abbrev %d"
+ die_off abbrev_num
+ in
+ let abbrev = Hashtbl.find abbrevs abbrev_num in
+ let (tag, children, attrs) = abbrev in
+ let attrs =
+ Array.map
+ begin
+ fun (attr,form) ->
+ let data =
+ match form with
+ DW_FORM_string -> DATA_str (ar.asm_get_zstr())
+ | DW_FORM_addr -> DATA_num (ar.asm_get_u32())
+ | DW_FORM_ref_addr -> DATA_num (ar.asm_get_u32())
+ | DW_FORM_data1 -> DATA_num (ar.asm_get_u8())
+ | DW_FORM_data4 -> DATA_num (ar.asm_get_u32())
+ | DW_FORM_flag -> DATA_num (ar.asm_get_u8())
+ | DW_FORM_block1 -> (adv_block1(); DATA_other)
+ | DW_FORM_block4 -> (adv_block4(); DATA_other)
+ | _ ->
+ bug () "unknown DWARF form %d"
+ (dw_form_to_int form)
+ in
+ (attr, (form, data))
+ end
+ attrs;
+ in
+ let children =
+ match children with
+ DW_CHILDREN_yes -> read_dies (ref [])
+ | DW_CHILDREN_no -> [| |]
+ in
+ let die = { die_off = die_off;
+ die_tag = tag;
+ die_attrs = attrs;
+ die_children = children }
+ in
+ prepend dies die;
+ htab_put all_dies die_off die;
+ read_dies dies
+ end
+ in
+ ignore (read_dies (ref []));
+ iflog sess
+ begin
+ fun _ ->
+ log sess "read DIEs:";
+ log sess "%s" (Ast.fmt_to_str fmt_dies (root, all_dies));
+ end;
+ (root, all_dies)
+;;
+
+let rec extract_meta
+ ((i:int),(dies:(int,die) Hashtbl.t))
+ : (Ast.ident * string) array =
+ let meta = Queue.create () in
+
+ let get_attr die attr =
+ atab_find die.die_attrs attr
+ in
+
+ let get_str die attr =
+ match get_attr die attr with
+ (_, DATA_str s) -> s
+ | _ -> bug () "unexpected num form for %s" (dw_at_to_string attr)
+ in
+
+ let die = Hashtbl.find dies i in
+ begin
+ match die.die_tag with
+ DW_TAG_rust_meta ->
+ let n = get_str die DW_AT_name in
+ let v = get_str die DW_AT_const_value in
+ Queue.add (n,v) meta
+
+ | DW_TAG_compile_unit ->
+ Array.iter
+ (fun child ->
+ Array.iter (fun m -> Queue.add m meta)
+ (extract_meta (child.die_off,dies)))
+ die.die_children
+
+ | _ -> ()
+ end;
+ queue_to_arr meta
+;;
+
+
+let rec extract_mod_items
+ (nref:node_id ref)
+ (oref:opaque_id ref)
+ (abi:Abi.abi)
+ (mis:Ast.mod_items)
+ ((i:int),(dies:(int,die) Hashtbl.t))
+ : unit =
+
+ let next_node_id _ : node_id =
+ let id = !nref in
+ nref:= Node ((int_of_node id)+1);
+ id
+ in
+
+ let next_opaque_id _ : opaque_id =
+ let id = !oref in
+ oref:= Opaque ((int_of_opaque id)+1);
+ 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())
+ in
+
+
+ let (word_sz:int64) = abi.Abi.abi_word_sz in
+ let (word_sz_int:int) = Int64.to_int word_sz in
+
+ let get_die i =
+ Hashtbl.find dies i
+ in
+
+ let get_attr die attr =
+ atab_find die.die_attrs attr
+ in
+
+ let get_str die attr =
+ match get_attr die attr with
+ (_, DATA_str s) -> s
+ | _ -> bug () "unexpected num form for %s" (dw_at_to_string attr)
+ in
+
+ let get_num die attr =
+ match get_attr die attr with
+ (_, DATA_num n) -> n
+ | _ -> bug () "unexpected str form for %s" (dw_at_to_string attr)
+ in
+
+ let get_flag die attr =
+ match get_attr die attr with
+ (_, DATA_num 0) -> false
+ | (_, DATA_num 1) -> true
+ | _ -> bug () "unexpected non-flag form for %s" (dw_at_to_string attr)
+ in
+
+ let get_effect die =
+ match (get_flag die DW_AT_mutable, get_flag die DW_AT_pure) with
+ (* Note: weird encoding: mutable+pure = unsafe. *)
+ (true, true) -> Ast.UNSAFE
+ | (true, false) -> Ast.STATE
+ | (false, false) -> Ast.IO
+ | (false, true) -> Ast.PURE
+ in
+
+ let get_name die = get_str die DW_AT_name in
+
+ let get_type_param die =
+ let idx = get_num die DW_AT_rust_type_param_index in
+ let e = get_effect die in
+ (idx, e)
+ in
+
+ let get_native_id die =
+ get_num die DW_AT_rust_native_type_id
+ in
+
+ let get_type_param_decl die =
+ ((get_str die DW_AT_name), (get_type_param die))
+ in
+
+ let is_rust_type die t =
+ match atab_search die.die_attrs DW_AT_rust_type_code with
+ Some (_, DATA_num n) -> (dw_rust_type_of_int n) = t
+ | _ -> false
+ in
+
+ let rec get_ty die : Ast.ty =
+ match die.die_tag with
+
+ DW_TAG_structure_type
+ when is_rust_type die DW_RUST_nil ->
+ Ast.TY_nil
+
+ | DW_TAG_pointer_type
+ when is_rust_type die DW_RUST_task ->
+ Ast.TY_task
+
+ | DW_TAG_pointer_type
+ when is_rust_type die DW_RUST_type ->
+ Ast.TY_type
+
+ | DW_TAG_pointer_type
+ when is_rust_type die DW_RUST_port ->
+ Ast.TY_port (get_referenced_ty die)
+
+ | DW_TAG_pointer_type
+ when is_rust_type die DW_RUST_chan ->
+ Ast.TY_chan (get_referenced_ty die)
+
+ | DW_TAG_pointer_type
+ when is_rust_type die DW_RUST_vec ->
+ Ast.TY_vec (get_referenced_slot die)
+
+ | DW_TAG_pointer_type
+ when is_rust_type die DW_RUST_type_param ->
+ Ast.TY_param (get_type_param die)
+
+ | DW_TAG_pointer_type
+ when is_rust_type die DW_RUST_native ->
+ Ast.TY_native (get_opaque_of (get_native_id die))
+
+ | DW_TAG_string_type -> Ast.TY_str
+
+ | DW_TAG_base_type ->
+ begin
+ match ((get_name die),
+ (dw_ate_of_int (get_num die DW_AT_encoding)),
+ (get_num die DW_AT_byte_size)) with
+ ("bool", DW_ATE_boolean, 1) -> Ast.TY_bool
+ | ("u8", DW_ATE_unsigned, 1) -> Ast.TY_mach TY_u8
+ | ("u16", DW_ATE_unsigned, 2) -> Ast.TY_mach TY_u16
+ | ("u32", DW_ATE_unsigned, 4) -> Ast.TY_mach TY_u32
+ | ("u64", DW_ATE_unsigned, 8) -> Ast.TY_mach TY_u64
+ | ("i8", DW_ATE_signed, 1) -> Ast.TY_mach TY_i8
+ | ("i16", DW_ATE_signed, 2) -> Ast.TY_mach TY_i16
+ | ("i32", DW_ATE_signed, 4) -> Ast.TY_mach TY_i32
+ | ("i64", DW_ATE_signed, 8) -> Ast.TY_mach TY_i64
+ | ("char", DW_ATE_unsigned_char, 4) -> Ast.TY_char
+ | ("int", DW_ATE_signed, sz)
+ when sz = word_sz_int -> Ast.TY_int
+ | ("uint", DW_ATE_unsigned, sz)
+ when sz = word_sz_int -> Ast.TY_uint
+ | _ -> bug () "unexpected type of DW_TAG_base_type"
+ end
+
+ | DW_TAG_structure_type ->
+ begin
+ let is_num_idx s =
+ let len = String.length s in
+ if len >= 2 && s.[0] = '_'
+ then
+ let ok = ref true in
+ String.iter
+ (fun c -> ok := (!ok) && '0' <= c && c <= '9')
+ (String.sub s 1 (len-1));
+ !ok
+ else
+ false
+ in
+ let members = arr_map_partial
+ die.die_children
+ begin
+ fun child ->
+ if child.die_tag = DW_TAG_member
+ then Some child
+ else None
+ end
+ in
+ assert ((Array.length members) > 0);
+ if is_num_idx (get_name members.(0))
+ then
+ let slots = Array.map get_referenced_slot members in
+ Ast.TY_tup slots
+ else
+ let entries =
+ Array.map
+ (fun member_die -> ((get_name member_die),
+ (get_referenced_slot member_die)))
+ members
+ in
+ Ast.TY_rec entries
+ end
+
+ | DW_TAG_interface_type ->
+ let eff = get_effect die in
+ let fns = Hashtbl.create 0 in
+ Array.iter
+ begin
+ fun child ->
+ if child.die_tag = DW_TAG_subroutine_type
+ then
+ Hashtbl.add fns (get_name child) (get_ty_fn child)
+ end
+ die.die_children;
+ Ast.TY_obj (eff,fns)
+
+ | DW_TAG_subroutine_type ->
+ Ast.TY_fn (get_ty_fn die)
+
+ | _ ->
+ bug () "unexpected tag in get_ty: %s"
+ (dw_tag_to_string die.die_tag)
+
+ and get_slot die : Ast.slot =
+ match die.die_tag with
+ DW_TAG_reference_type ->
+ let ty = get_referenced_ty die in
+ let mut = get_flag die DW_AT_mutable in
+ let mode =
+ (* Exterior slots have a 'data_location' attr. *)
+ match atab_search die.die_attrs DW_AT_data_location with
+ Some _ -> Ast.MODE_exterior
+ | None -> Ast.MODE_alias
+ in
+ { Ast.slot_mode = mode;
+ Ast.slot_mutable = mut;
+ Ast.slot_ty = Some ty }
+ | _ ->
+ let ty = get_ty die in
+ (* FIXME: encode mutability of interior slots properly. *)
+ { Ast.slot_mode = Ast.MODE_interior;
+ Ast.slot_mutable = false;
+ Ast.slot_ty = Some ty }
+
+ and get_referenced_ty die =
+ match get_attr die DW_AT_type with
+ (DW_FORM_ref_addr, DATA_num n) -> get_ty (get_die n)
+ | _ -> bug () "unexpected form of DW_AT_type in get_referenced_ty"
+
+ and get_referenced_slot die =
+ match get_attr die DW_AT_type with
+ (DW_FORM_ref_addr, DATA_num n) -> get_slot (get_die n)
+ | _ -> bug () "unexpected form of DW_AT_type in get_referenced_slot"
+
+ and get_ty_fn die =
+ let out = get_referenced_slot die in
+ let ins =
+ arr_map_partial
+ die.die_children
+ begin
+ fun child ->
+ if child.die_tag = DW_TAG_formal_parameter
+ then Some (get_referenced_slot child)
+ else None
+ end
+ in
+ let effect = get_effect die in
+ let iter = get_flag die DW_AT_rust_iterator in
+ let tsig =
+ { Ast.sig_input_slots = ins;
+ Ast.sig_input_constrs = [| |];
+ Ast.sig_output_slot = out; }
+ in
+ let taux =
+ { Ast.fn_is_iter = iter;
+ Ast.fn_effect = effect }
+ in
+ (tsig, taux)
+ in
+
+ let wrap n =
+ { id = next_node_id ();
+ node = n }
+ in
+
+ let decl p i =
+ wrap { Ast.decl_params = p;
+ Ast.decl_item = i; }
+ in
+
+ let get_formals die =
+ let islots = Queue.create () in
+ let params = Queue.create () in
+ Array.iter
+ begin
+ fun child ->
+ match child.die_tag with
+ DW_TAG_formal_parameter ->
+ if (is_rust_type child DW_RUST_type_param)
+ then Queue.push (wrap (get_type_param_decl child)) params
+ else Queue.push (get_referenced_slot child) islots
+ | _ -> ()
+ end
+ die.die_children;
+ (queue_to_arr params, queue_to_arr islots)
+ in
+
+ let extract_children mis die =
+ Array.iter
+ (fun child ->
+ extract_mod_items nref oref abi mis (child.die_off,dies))
+ die.die_children
+ in
+
+ let get_mod_items die =
+ let len = Array.length die.die_children in
+ let mis = Hashtbl.create len in
+ extract_children mis die;
+ mis
+ in
+
+ let form_header_slots slots =
+ Array.mapi
+ (fun i slot -> (wrap slot, "_" ^ (string_of_int i)))
+ slots
+ in
+
+ let die = Hashtbl.find dies i in
+ match die.die_tag with
+ DW_TAG_typedef ->
+ let ident = get_name die in
+ let ty = get_referenced_ty die in
+ let tyi = Ast.MOD_ITEM_type ty in
+ let (params, islots) = get_formals die in
+ assert ((Array.length islots) = 0);
+ htab_put mis ident (decl params tyi)
+
+ | DW_TAG_compile_unit ->
+ extract_children mis die
+
+ | DW_TAG_module ->
+ let ident = get_name die in
+ let sub_mis = get_mod_items die in
+ let exports = Hashtbl.create 0 in
+ let _ = Hashtbl.add exports Ast.EXPORT_all_decls () in
+ let view = { Ast.view_imports = Hashtbl.create 0;
+ Ast.view_exports = exports }
+ in
+ let mi = Ast.MOD_ITEM_mod (view, sub_mis) in
+ htab_put mis ident (decl [||] mi)
+
+ | DW_TAG_subprogram ->
+ (* FIXME: finish this. *)
+ let ident = get_name die in
+ let oslot = get_referenced_slot die in
+ let effect = get_effect die in
+ let (params, islots) = get_formals die in
+ let taux = { Ast.fn_effect = effect;
+ Ast.fn_is_iter = false }
+ in
+ let tfn = { Ast.fn_input_slots = form_header_slots islots;
+ Ast.fn_input_constrs = [| |];
+ Ast.fn_output_slot = wrap oslot;
+ Ast.fn_aux = taux;
+ Ast.fn_body = (wrap [||]); }
+ in
+ let fn = Ast.MOD_ITEM_fn tfn in
+ htab_put mis ident (decl params fn)
+
+ | _ -> ()
+;;
+
+(*
+ * 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/me/effect.ml b/src/boot/me/effect.ml
new file mode 100644
index 00000000..515cfa21
--- /dev/null
+++ b/src/boot/me/effect.ml
@@ -0,0 +1,313 @@
+open Semant;;
+open Common;;
+
+let log cx = Session.log "effect"
+ cx.ctxt_sess.Session.sess_log_effect
+ cx.ctxt_sess.Session.sess_log_out
+;;
+
+let iflog cx thunk =
+ if cx.ctxt_sess.Session.sess_log_effect
+ then thunk ()
+ else ()
+;;
+
+let mutability_checking_visitor
+ (cx:ctxt)
+ (inner:Walk.visitor)
+ : Walk.visitor =
+ (*
+ * This visitor enforces the following rules:
+ *
+ * - A channel type carrying a mutable type is illegal.
+ *
+ * - Writing to an immutable slot is illegal.
+ *
+ * - Forming a mutable alias to an immutable slot is illegal.
+ *
+ *)
+ let visit_ty_pre t =
+ match t with
+ Ast.TY_chan t' when type_has_state t' ->
+ err None "channel of mutable type: %a " Ast.sprintf_ty t'
+ | _ -> ()
+ in
+
+ let check_write id dst =
+ let dst_slot = lval_slot cx dst in
+ if (dst_slot.Ast.slot_mutable or
+ (Hashtbl.mem cx.ctxt_copy_stmt_is_init id))
+ then ()
+ else err (Some id) "writing to non-mutable slot"
+ in
+ (* FIXME: enforce the no-write-alias-to-immutable-slot rule. *)
+ let visit_stmt_pre s =
+ begin
+ match s.node with
+ Ast.STMT_copy (dst, _) -> check_write s.id dst
+ | Ast.STMT_copy_binop (dst, _, _) -> check_write s.id dst
+ | Ast.STMT_call (dst, _, _) -> check_write s.id dst
+ | Ast.STMT_recv (dst, _) -> check_write s.id dst
+ | _ -> ()
+ end;
+ inner.Walk.visit_stmt_pre s
+ in
+
+ { inner with
+ Walk.visit_ty_pre = visit_ty_pre;
+ Walk.visit_stmt_pre = visit_stmt_pre }
+;;
+
+let function_effect_propagation_visitor
+ (item_effect:(node_id, Ast.effect) Hashtbl.t)
+ (cx:ctxt)
+ (inner:Walk.visitor)
+ : Walk.visitor =
+ (*
+ * This visitor calculates the effect of each function according to
+ * its statements:
+ *
+ * - Communication lowers to 'io'
+ * - Native calls lower to 'unsafe'
+ * - Calling a function with effect e lowers to e.
+ *)
+ let curr_fn = Stack.create () in
+ let visit_mod_item_pre n p i =
+ begin
+ match i.node.Ast.decl_item with
+ Ast.MOD_ITEM_fn _ -> Stack.push i.id curr_fn
+ | _ -> ()
+ end;
+ inner.Walk.visit_mod_item_pre n p i
+ in
+ let visit_mod_item_post n p i =
+ inner.Walk.visit_mod_item_post n p i;
+ match i.node.Ast.decl_item with
+ Ast.MOD_ITEM_fn _ -> ignore (Stack.pop curr_fn)
+ | _ -> ()
+ in
+ let visit_obj_drop_pre o b =
+ Stack.push b.id curr_fn;
+ inner.Walk.visit_obj_drop_pre o b
+ in
+ let visit_obj_drop_post o b =
+ inner.Walk.visit_obj_drop_post o b;
+ ignore (Stack.pop curr_fn);
+ in
+
+ let lower_to s ne =
+ let fn_id = Stack.top curr_fn in
+ let e =
+ match htab_search item_effect fn_id with
+ None -> Ast.PURE
+ | Some e -> e
+ in
+ let ne = lower_effect_of ne e in
+ if ne <> e
+ then
+ begin
+ iflog cx
+ begin
+ fun _ ->
+ let name = Hashtbl.find cx.ctxt_all_item_names fn_id in
+ log cx "lowering calculated effect on '%a': '%a' -> '%a'"
+ Ast.sprintf_name name
+ Ast.sprintf_effect e
+ Ast.sprintf_effect ne;
+ log cx "at stmt %a" Ast.sprintf_stmt s
+ end;
+ Hashtbl.replace item_effect fn_id ne
+ end;
+ in
+
+ let visit_stmt_pre s =
+ begin
+ match s.node with
+ Ast.STMT_send _
+ | Ast.STMT_recv _ -> lower_to s Ast.IO
+
+ | Ast.STMT_call (_, fn, _) ->
+ let lower_to_callee_ty t =
+ match t with
+ Ast.TY_fn (_, taux) ->
+ lower_to s taux.Ast.fn_effect;
+ | _ -> bug () "non-fn callee"
+ in
+ if lval_is_slot cx fn
+ then
+ let t = lval_slot cx fn in
+ lower_to_callee_ty (slot_ty t)
+ else
+ begin
+ let item = lval_item cx fn in
+ let t = Hashtbl.find cx.ctxt_all_item_types item.id in
+ lower_to_callee_ty t;
+ match htab_search cx.ctxt_required_items item.id with
+ None -> ()
+ | Some (REQUIRED_LIB_rust _, _) -> ()
+ | Some _ -> lower_to s Ast.UNSAFE
+ end
+ | _ -> ()
+ end;
+ inner.Walk.visit_stmt_pre s
+ in
+
+ { inner with
+ Walk.visit_mod_item_pre = visit_mod_item_pre;
+ Walk.visit_mod_item_post = visit_mod_item_post;
+ Walk.visit_obj_drop_pre = visit_obj_drop_pre;
+ Walk.visit_obj_drop_post = visit_obj_drop_post;
+ Walk.visit_stmt_pre = visit_stmt_pre }
+;;
+
+let binding_effect_propagation_visitor
+ ((*cx*)_:ctxt)
+ (inner:Walk.visitor)
+ : Walk.visitor =
+ (* This visitor lowers the effect of an object or binding according
+ * to its slots: holding a 'state' slot lowers any obj item, or
+ * bind-stmt LHS, to 'state'.
+ *
+ * Binding (or implicitly just making a native 1st-class) makes the LHS
+ * unsafe.
+ *)
+ inner
+;;
+
+let effect_checking_visitor
+ (item_auth:(node_id, Ast.effect) Hashtbl.t)
+ (item_effect:(node_id, Ast.effect) Hashtbl.t)
+ (cx:ctxt)
+ (inner:Walk.visitor)
+ : Walk.visitor =
+ (*
+ * This visitor checks that each type, item and obj declares
+ * effects consistent with what we calculated.
+ *)
+ let auth_stack = Stack.create () in
+ let visit_mod_item_pre n p i =
+ begin
+ match htab_search item_auth i.id with
+ None -> ()
+ | Some e ->
+ let curr =
+ if Stack.is_empty auth_stack
+ then Ast.PURE
+ else Stack.top auth_stack
+ in
+ let next = lower_effect_of e curr in
+ Stack.push next auth_stack;
+ iflog cx
+ begin
+ fun _ ->
+ let name = Hashtbl.find cx.ctxt_all_item_names i.id in
+ log cx
+ "entering '%a', adjusting auth effect: '%a' -> '%a'"
+ Ast.sprintf_name name
+ Ast.sprintf_effect curr
+ Ast.sprintf_effect next
+ end
+ end;
+ begin
+ match i.node.Ast.decl_item with
+ Ast.MOD_ITEM_fn f ->
+ let e =
+ match htab_search item_effect i.id with
+ None -> Ast.PURE
+ | Some e -> e
+ in
+ let fe = f.Ast.fn_aux.Ast.fn_effect in
+ let ae =
+ if Stack.is_empty auth_stack
+ then None
+ else Some (Stack.top auth_stack)
+ in
+ if e <> fe && (ae <> (Some e))
+ then
+ begin
+ let name = Hashtbl.find cx.ctxt_all_item_names i.id in
+ err (Some i.id)
+ "%a claims effect '%a' but calculated effect is '%a'%s"
+ Ast.sprintf_name name
+ Ast.sprintf_effect fe
+ Ast.sprintf_effect e
+ begin
+ match ae with
+ Some ae when ae <> fe ->
+ Printf.sprintf " (auth effect is '%a')"
+ Ast.sprintf_effect ae
+ | _ -> ""
+ end
+ end
+ | _ -> ()
+ end;
+ inner.Walk.visit_mod_item_pre n p i
+ in
+ let visit_mod_item_post n p i =
+ inner.Walk.visit_mod_item_post n p i;
+ match htab_search item_auth i.id with
+ None -> ()
+ | Some _ ->
+ let curr = Stack.pop auth_stack in
+ let next =
+ if Stack.is_empty auth_stack
+ then Ast.PURE
+ else Stack.top auth_stack
+ in
+ iflog cx
+ begin
+ fun _ ->
+ let name = Hashtbl.find cx.ctxt_all_item_names i.id in
+ log cx
+ "leaving '%a', restoring auth effect: '%a' -> '%a'"
+ Ast.sprintf_name name
+ Ast.sprintf_effect curr
+ Ast.sprintf_effect next
+ end
+ in
+ { inner with
+ Walk.visit_mod_item_pre = visit_mod_item_pre;
+ Walk.visit_mod_item_post = visit_mod_item_post; }
+;;
+
+
+let process_crate
+ (cx:ctxt)
+ (crate:Ast.crate)
+ : unit =
+ let path = Stack.create () in
+ let item_auth = Hashtbl.create 0 in
+ let item_effect = Hashtbl.create 0 in
+ let passes =
+ [|
+ (mutability_checking_visitor cx
+ Walk.empty_visitor);
+ (function_effect_propagation_visitor item_effect cx
+ Walk.empty_visitor);
+ (binding_effect_propagation_visitor cx
+ Walk.empty_visitor);
+ (effect_checking_visitor item_auth item_effect cx
+ Walk.empty_visitor);
+ |]
+ in
+ let root_scope = [ SCOPE_crate crate ] in
+ let auth_effect name eff =
+ match lookup_by_name cx root_scope name with
+ None -> ()
+ | Some (_, id) ->
+ if referent_is_item cx id
+ then htab_put item_auth id eff
+ else err (Some id) "auth clause in crate refers to non-item"
+ in
+ Hashtbl.iter auth_effect crate.node.Ast.crate_auth;
+ run_passes cx "effect" path passes (log cx "%s") crate
+;;
+
+(*
+ * 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/me/layout.ml b/src/boot/me/layout.ml
new file mode 100644
index 00000000..6c4567fd
--- /dev/null
+++ b/src/boot/me/layout.ml
@@ -0,0 +1,470 @@
+open Semant;;
+open Common;;
+
+let log cx = Session.log "layout"
+ cx.ctxt_sess.Session.sess_log_layout
+ cx.ctxt_sess.Session.sess_log_out
+;;
+
+type slot_stack = Il.referent_ty Stack.t;;
+type frame_blocks = slot_stack Stack.t;;
+
+let layout_visitor
+ (cx:ctxt)
+ (inner:Walk.visitor)
+ : Walk.visitor =
+ (*
+ * - Frames look, broadly, like this (growing downward):
+ *
+ * +----------------------------+ <-- Rewind tail calls to here.
+ * |caller args |
+ * |... |
+ * |... |
+ * +----------------------------+ <-- fp + abi_frame_base_sz
+ * |task ptr (implicit arg) | + abi_implicit_args_sz
+ * |output ptr (implicit arg) |
+ * +----------------------------+ <-- fp + abi_frame_base_sz
+ * |return pc |
+ * |callee-save registers |
+ * |... |
+ * +----------------------------+ <-- fp
+ * |crate ptr |
+ * |crate-rel frame info disp |
+ * +----------------------------+ <-- fp - abi_frame_info_sz
+ * |spills determined in ra |
+ * |... |
+ * |... |
+ * +----------------------------+ <-- fp - (abi_frame_info_sz
+ * |... | + spillsz)
+ * |frame-allocated stuff |
+ * |determined in resolve |
+ * |laid out in layout |
+ * |... |
+ * |... |
+ * +----------------------------+ <-- fp - framesz
+ * |call space | == sp + callsz
+ * |... |
+ * |... |
+ * +----------------------------+ <-- fp - (framesz + callsz) == sp
+ *
+ * - Slot offsets fall into three classes:
+ *
+ * #1 frame-locals are negative offsets from fp
+ * (beneath the frame-info and spills)
+ *
+ * #2 incoming arg slots are positive offsets from fp
+ * (above the frame-base)
+ *
+ * #3 outgoing arg slots are positive offsets from sp
+ *
+ * - Slots are split into two classes:
+ *
+ * #1 those that are never aliased and fit in a word, so are
+ * vreg-allocated
+ *
+ * #2 all others
+ *
+ * - Non-aliased, word-fitting slots consume no frame space
+ * *yet*; they are given a generic value that indicates "try a
+ * vreg". The register allocator may spill them later, if it
+ * needs to, but that's not our concern.
+ *
+ * - Aliased / too-big slots are frame-allocated, need to be
+ * laid out in the frame at fixed offsets.
+ *
+ * - The frame size is the maximum of all the block sizes contained
+ * within it. Though at the moment it's the sum of them, due to
+ * the blood-curdling hack we use to ensure proper unwind/drop
+ * behavior in absence of CFI or similar precise frame-evolution
+ * tracking. See visit_block_post below (issue #27).
+ *
+ * - Each call is examined and the size of the call tuple required
+ * for that call is calculated. The call size is the maximum of all
+ * such call tuples.
+ *
+ * - In frames that have a tail call (in fact, currently, all frames
+ * because we're lazy) we double the call size in order to handle
+ * the possible need to *execute* a call (to drop glue) while
+ * destroying the frame, after we've built the outgoing args. This is
+ * done in the backend though; the logic in this file is ignorant of the
+ * doubling (some platforms may not require it? Hard to guess)
+ *
+ *)
+
+ let force_slot_to_mem (slot:Ast.slot) : bool =
+ (* FIXME (issue #26): For the time being we force any slot that
+ * points into memory or is of opaque/code type to be stored in the
+ * frame rather than in a vreg. This can probably be relaxed in the
+ * future.
+ *)
+ let rec st_in_mem st =
+ match st with
+ Il.ValTy _ -> false
+ | Il.AddrTy _ -> true
+
+ and rt_in_mem rt =
+ match rt with
+ Il.ScalarTy st -> st_in_mem st
+ | Il.StructTy rts
+ | Il.UnionTy rts -> List.exists rt_in_mem (Array.to_list rts)
+ | Il.OpaqueTy
+ | Il.ParamTy _
+ | Il.CodeTy -> true
+ | Il.NilTy -> false
+ in
+ rt_in_mem (slot_referent_type cx.ctxt_abi slot)
+ in
+
+ let rty_sz rty = Il.referent_ty_size cx.ctxt_abi.Abi.abi_word_bits rty in
+ let rty_layout rty =
+ Il.referent_ty_layout cx.ctxt_abi.Abi.abi_word_bits rty
+ in
+
+ let is_subword_size sz =
+ match sz with
+ SIZE_fixed i -> i64_le i cx.ctxt_abi.Abi.abi_word_sz
+ | _ -> false
+ in
+
+ let iflog thunk =
+ if cx.ctxt_sess.Session.sess_log_layout
+ then thunk ()
+ else ()
+ in
+
+ let layout_slot_ids
+ (slot_accum:slot_stack)
+ (upwards:bool)
+ (vregs_ok:bool)
+ (offset:size)
+ (slots:node_id array)
+ : unit =
+ let accum (off,align) id : (size * size) =
+ let slot = referent_to_slot cx id in
+ let rt = slot_referent_type cx.ctxt_abi slot in
+ let (elt_size, elt_align) = rty_layout rt in
+ if vregs_ok
+ && (is_subword_size elt_size)
+ && (not (type_is_structured (slot_ty slot)))
+ && (not (force_slot_to_mem slot))
+ && (not (Hashtbl.mem cx.ctxt_slot_aliased id))
+ then
+ begin
+ iflog
+ begin
+ fun _ ->
+ let k = Hashtbl.find cx.ctxt_slot_keys id in
+ log cx "assigning slot #%d = %a to vreg"
+ (int_of_node id)
+ Ast.sprintf_slot_key k;
+ end;
+ htab_put cx.ctxt_slot_vregs id (ref None);
+ (off,align)
+ end
+ else
+ begin
+ let elt_off = align_sz elt_align off in
+ let frame_off =
+ if upwards
+ then elt_off
+ else neg_sz (add_sz elt_off elt_size)
+ in
+ Stack.push (slot_referent_type cx.ctxt_abi slot) slot_accum;
+ iflog
+ begin
+ fun _ ->
+ let k = Hashtbl.find cx.ctxt_slot_keys id in
+ log cx "assigning slot #%d = %a frame-offset %s"
+ (int_of_node id)
+ Ast.sprintf_slot_key k
+ (string_of_size frame_off);
+ end;
+ if (not (Hashtbl.mem cx.ctxt_slot_offsets id))
+ then htab_put cx.ctxt_slot_offsets id frame_off;
+ (add_sz elt_off elt_size, max_sz elt_align align)
+ end
+ in
+ ignore (Array.fold_left accum (offset, SIZE_fixed 0L) slots)
+ in
+
+ let layout_block
+ (slot_accum:slot_stack)
+ (offset:size)
+ (block:Ast.block)
+ : unit =
+ log cx "laying out block #%d at fp offset %s"
+ (int_of_node block.id) (string_of_size offset);
+ let block_slot_ids =
+ Array.of_list (htab_vals (Hashtbl.find cx.ctxt_block_slots block.id))
+ in
+ layout_slot_ids slot_accum false true offset block_slot_ids
+ in
+
+ let layout_header (id:node_id) (input_slot_ids:node_id array) : unit =
+ let rty = direct_call_args_referent_type cx id in
+ let offset =
+ match rty with
+ Il.StructTy elts ->
+ (add_sz
+ (SIZE_fixed cx.ctxt_abi.Abi.abi_frame_base_sz)
+ (Il.get_element_offset
+ cx.ctxt_abi.Abi.abi_word_bits
+ elts Abi.calltup_elt_args))
+ | _ -> bug () "call tuple has non-StructTy"
+ in
+ log cx "laying out header for node #%d at fp offset %s"
+ (int_of_node id) (string_of_size offset);
+ layout_slot_ids (Stack.create()) true false offset input_slot_ids
+ in
+
+ let layout_obj_state (id:node_id) (state_slot_ids:node_id array) : unit =
+ let offset =
+ let word_sz = cx.ctxt_abi.Abi.abi_word_sz in
+ let word_n (n:int) = Int64.mul word_sz (Int64.of_int n) in
+ SIZE_fixed (word_n (Abi.exterior_rc_slot_field_body
+ + 1 (* the state tydesc. *)))
+ in
+ log cx "laying out object-state for node #%d at offset %s"
+ (int_of_node id) (string_of_size offset);
+ layout_slot_ids (Stack.create()) true false offset state_slot_ids
+ in
+
+ let (frame_stack:(node_id * frame_blocks) Stack.t) = Stack.create() in
+
+ let block_rty (block:slot_stack) : Il.referent_ty =
+ Il.StructTy (Array.of_list (stk_elts_from_bot block))
+ in
+
+ let frame_rty (frame:frame_blocks) : Il.referent_ty =
+ Il.StructTy (Array.of_list (List.map block_rty (stk_elts_from_bot frame)))
+ in
+
+ let update_frame_size _ =
+ let (frame_id, frame_blocks) = Stack.top frame_stack in
+ let frame_spill = Hashtbl.find cx.ctxt_spill_fixups frame_id in
+ let sz =
+ add_sz
+ (add_sz
+ (rty_sz (frame_rty frame_blocks))
+ (SIZE_fixup_mem_sz frame_spill))
+ (SIZE_fixed cx.ctxt_abi.Abi.abi_frame_info_sz)
+ in
+ let curr = Hashtbl.find cx.ctxt_frame_sizes frame_id in
+ let sz = max_sz curr sz in
+ log cx "extending frame #%d frame to size %s"
+ (int_of_node frame_id) (string_of_size sz);
+ Hashtbl.replace cx.ctxt_frame_sizes frame_id sz
+ in
+
+ (*
+ * FIXME: this is a little aggressive for default callsz; it can be
+ * narrowed in frames with no drop glue and/or no indirect drop glue.
+ *)
+
+ let glue_callsz =
+ let word = interior_slot Ast.TY_int in
+ let glue_fn =
+ mk_simple_ty_fn
+ (Array.init Abi.worst_case_glue_call_args (fun _ -> word))
+ in
+ rty_sz (indirect_call_args_referent_type cx 0 glue_fn Il.OpaqueTy)
+ in
+
+ let enter_frame id =
+ Stack.push (id, (Stack.create())) frame_stack;
+ htab_put cx.ctxt_frame_sizes id (SIZE_fixed 0L);
+ htab_put cx.ctxt_call_sizes id glue_callsz;
+ htab_put cx.ctxt_spill_fixups id (new_fixup "frame spill fixup");
+ htab_put cx.ctxt_frame_blocks id [];
+ update_frame_size ();
+ in
+
+ let leave_frame _ =
+ ignore (Stack.pop frame_stack);
+ in
+
+ let header_slot_ids hdr = Array.map (fun (sid,_) -> sid.id) hdr in
+
+ let visit_mod_item_pre n p i =
+ begin
+ match i.node.Ast.decl_item with
+ Ast.MOD_ITEM_fn f ->
+ enter_frame i.id;
+ layout_header i.id
+ (header_slot_ids f.Ast.fn_input_slots)
+
+ | Ast.MOD_ITEM_tag (header_slots, _, _) ->
+ enter_frame i.id;
+ layout_header i.id
+ (Array.map (fun sid -> sid.id) header_slots)
+
+ | Ast.MOD_ITEM_obj obj ->
+ enter_frame i.id;
+ let ids = header_slot_ids obj.Ast.obj_state in
+ layout_obj_state i.id ids;
+ Array.iter
+ (fun id -> htab_put cx.ctxt_slot_is_obj_state id ())
+ ids
+
+ | _ -> ()
+ end;
+ inner.Walk.visit_mod_item_pre n p i
+ in
+
+ let visit_mod_item_post n p i =
+ inner.Walk.visit_mod_item_post n p i;
+ begin
+ match i.node.Ast.decl_item with
+ Ast.MOD_ITEM_fn _
+ | Ast.MOD_ITEM_tag _
+ | Ast.MOD_ITEM_obj _ -> leave_frame ()
+ | _ -> ()
+ end
+ in
+
+ let visit_obj_fn_pre obj ident fn =
+ enter_frame fn.id;
+ layout_header fn.id
+ (header_slot_ids fn.node.Ast.fn_input_slots);
+ inner.Walk.visit_obj_fn_pre obj ident fn
+ in
+
+ let visit_obj_fn_post obj ident fn =
+ inner.Walk.visit_obj_fn_post obj ident fn;
+ leave_frame ()
+ in
+
+ let visit_obj_drop_pre obj b =
+ enter_frame b.id;
+ inner.Walk.visit_obj_drop_pre obj b
+ in
+
+ let visit_obj_drop_post obj b =
+ inner.Walk.visit_obj_drop_post obj b;
+ leave_frame ()
+ in
+
+ let visit_block_pre b =
+ if Hashtbl.mem cx.ctxt_block_is_loop_body b.id
+ then enter_frame b.id;
+ let (frame_id, frame_blocks) = Stack.top frame_stack in
+ let frame_spill = Hashtbl.find cx.ctxt_spill_fixups frame_id in
+ let spill_sz = SIZE_fixup_mem_sz frame_spill in
+ let info_sz = SIZE_fixed cx.ctxt_abi.Abi.abi_frame_info_sz in
+ let locals_off = add_sz spill_sz info_sz in
+ let off =
+ if Stack.is_empty frame_blocks
+ then locals_off
+ else
+ add_sz locals_off (rty_sz (frame_rty frame_blocks))
+ in
+ let block_slots = Stack.create() in
+ let frame_block_ids = Hashtbl.find cx.ctxt_frame_blocks frame_id in
+ Hashtbl.replace cx.ctxt_frame_blocks frame_id (b.id :: frame_block_ids);
+ layout_block block_slots off b;
+ Stack.push block_slots frame_blocks;
+ update_frame_size ();
+ inner.Walk.visit_block_pre b
+ in
+
+ let visit_block_post b =
+ inner.Walk.visit_block_post b;
+ if Hashtbl.mem cx.ctxt_block_is_loop_body b.id
+ then leave_frame();
+ (* FIXME (issue #27): In earlier versions of this file, multiple
+ * lexical blocks in the same frame would reuse space from one to
+ * the next so long as they were not nested; The (commented-out)
+ * code here supports that logic. Unfortunately since our marking
+ * and unwinding strategy is very simplistic for now (analogous to
+ * shadow stacks) we're going to give each lexical block in a frame
+ * its own space in the frame, even if they seem like they *should*
+ * be able to reuse space. This makes it possible to arrive at the
+ * frame and work out which variables are live (and which frame
+ * memory corresponds to them) w/o paying attention to the current
+ * pc in the function; a greatly-simplifying assumption.
+ *
+ * This is of course not optimal for the long term, but in the
+ * longer term we'll have time to form proper DWARF CFI
+ * records. We're in a hurry at the moment. *)
+ (*
+ let stk = Stack.top block_stacks in
+ ignore (Stack.pop stk)
+ *)
+ in
+
+ let visit_stmt_pre (s:Ast.stmt) : unit =
+
+ (* Call-size calculation. *)
+ begin
+ let callees =
+ match s.node with
+ Ast.STMT_call (_, lv, _)
+ | Ast.STMT_spawn (_, _, lv, _) -> [| lv |]
+ | Ast.STMT_check (_, calls) -> Array.map (fun (lv, _) -> lv) calls
+ | _ -> [| |]
+ in
+ Array.iter
+ begin
+ fun (callee:Ast.lval) ->
+ let lv_ty = lval_ty cx callee in
+ let abi = cx.ctxt_abi in
+ let static = lval_is_static cx callee in
+ let closure = if static then None else Some Il.OpaqueTy in
+ let n_ty_params =
+ match resolve_lval cx callee with
+ DEFN_item i -> Array.length i.Ast.decl_params
+ | _ -> 0
+ in
+ let rty =
+ call_args_referent_type cx n_ty_params lv_ty closure
+ in
+ let sz = Il.referent_ty_size abi.Abi.abi_word_bits rty in
+ let frame_id = fst (Stack.top frame_stack) in
+ let curr = Hashtbl.find cx.ctxt_call_sizes frame_id in
+ log cx "extending frame #%d call size to %s"
+ (int_of_node frame_id) (string_of_size (max_sz curr sz));
+ Hashtbl.replace cx.ctxt_call_sizes frame_id (max_sz curr sz)
+ end
+ callees
+ end;
+ inner.Walk.visit_stmt_pre s
+ in
+
+
+ { inner with
+ Walk.visit_mod_item_pre = visit_mod_item_pre;
+ Walk.visit_mod_item_post = visit_mod_item_post;
+
+ Walk.visit_obj_fn_pre = visit_obj_fn_pre;
+ Walk.visit_obj_fn_post = visit_obj_fn_post;
+ Walk.visit_obj_drop_pre = visit_obj_drop_pre;
+ Walk.visit_obj_drop_post = visit_obj_drop_post;
+
+ Walk.visit_stmt_pre = visit_stmt_pre;
+ Walk.visit_block_pre = visit_block_pre;
+ Walk.visit_block_post = visit_block_post }
+;;
+
+let process_crate
+ (cx:ctxt)
+ (crate:Ast.crate)
+ : unit =
+ let path = Stack.create () in
+ let passes =
+ [|
+ (layout_visitor cx
+ Walk.empty_visitor)
+ |];
+ in
+ run_passes cx "layout" path passes (log cx "%s") crate
+;;
+
+
+(*
+ * 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/me/loop.ml b/src/boot/me/loop.ml
new file mode 100644
index 00000000..c23c4afd
--- /dev/null
+++ b/src/boot/me/loop.ml
@@ -0,0 +1,163 @@
+(*
+ * Computes iterator-loop nesting depths and max depth of each function.
+ *)
+
+open Semant;;
+open Common;;
+
+let log cx = Session.log "loop"
+ cx.ctxt_sess.Session.sess_log_loop
+ cx.ctxt_sess.Session.sess_log_out
+;;
+
+type fn_ctxt = { current_depth: int; }
+;;
+
+let incr_depth (fcx:fn_ctxt) =
+ { current_depth = fcx.current_depth + 1; }
+;;
+
+let decr_depth (fcx:fn_ctxt) =
+ { current_depth = fcx.current_depth - 1; }
+;;
+
+let top_fcx = { current_depth = 0; }
+;;
+
+let loop_depth_visitor
+ (cx:ctxt)
+ (inner:Walk.visitor)
+ : Walk.visitor =
+
+ let (fcxs : fn_ctxt Stack.t) = Stack.create () in
+
+ let push_loop () =
+ let fcx = Stack.pop fcxs in
+ Stack.push (incr_depth fcx) fcxs
+ in
+
+ let pop_loop () =
+ let fcx = Stack.pop fcxs in
+ Stack.push (decr_depth fcx) fcxs
+ in
+
+ let visit_mod_item_pre
+ (ident:Ast.ident)
+ (ty_params:(Ast.ty_param identified) array)
+ (item:Ast.mod_item)
+ : unit =
+ Stack.push top_fcx fcxs;
+ inner.Walk.visit_mod_item_pre ident ty_params item
+ in
+
+ let visit_mod_item_post
+ (ident:Ast.ident)
+ (ty_params:(Ast.ty_param identified) array)
+ (item:Ast.mod_item)
+ : unit =
+ inner.Walk.visit_mod_item_post ident ty_params item;
+ ignore (Stack.pop fcxs);
+ in
+
+ let visit_obj_fn_pre
+ (obj:Ast.obj identified)
+ (ident:Ast.ident)
+ (fn:Ast.fn identified)
+ : unit =
+ Stack.push top_fcx fcxs;
+ inner.Walk.visit_obj_fn_pre obj ident fn
+ in
+
+ let visit_obj_fn_post
+ (obj:Ast.obj identified)
+ (ident:Ast.ident)
+ (fn:Ast.fn identified)
+ : unit =
+ inner.Walk.visit_obj_fn_pre obj ident fn;
+ ignore (Stack.pop fcxs)
+ in
+
+ let visit_obj_drop_pre
+ (obj:Ast.obj identified)
+ (b:Ast.block)
+ : unit =
+ Stack.push top_fcx fcxs;
+ inner.Walk.visit_obj_drop_pre obj b
+ in
+
+ let visit_obj_drop_post
+ (obj:Ast.obj identified)
+ (b:Ast.block)
+ : unit =
+ inner.Walk.visit_obj_drop_post obj b;
+ ignore (Stack.pop fcxs)
+ in
+
+ let visit_slot_identified_pre sloti =
+ let fcx = Stack.top fcxs in
+ htab_put cx.ctxt_slot_loop_depths sloti.id fcx.current_depth;
+ inner.Walk.visit_slot_identified_pre sloti
+ in
+
+ let visit_stmt_pre s =
+ let fcx = Stack.top fcxs in
+ htab_put cx.ctxt_stmt_loop_depths s.id fcx.current_depth;
+ begin
+ match s.node with
+ | Ast.STMT_for_each fe ->
+ htab_put cx.ctxt_block_is_loop_body fe.Ast.for_each_body.id ();
+ | _ -> ()
+ end;
+ inner.Walk.visit_stmt_pre s
+ in
+
+ let visit_block_pre b =
+ if Hashtbl.mem cx.ctxt_block_is_loop_body b.id
+ then push_loop ();
+ inner.Walk.visit_block_pre b
+ in
+
+ let visit_block_post b =
+ inner.Walk.visit_block_post b;
+ if Hashtbl.mem cx.ctxt_block_is_loop_body b.id
+ then pop_loop ()
+ in
+
+ { inner with
+ Walk.visit_mod_item_pre = visit_mod_item_pre;
+ Walk.visit_mod_item_post = visit_mod_item_post;
+ Walk.visit_obj_fn_pre = visit_obj_fn_pre;
+ Walk.visit_obj_fn_post = visit_obj_fn_post;
+ Walk.visit_obj_drop_pre = visit_obj_drop_pre;
+ Walk.visit_obj_drop_post = visit_obj_drop_post;
+ Walk.visit_slot_identified_pre = visit_slot_identified_pre;
+ Walk.visit_stmt_pre = visit_stmt_pre;
+ Walk.visit_block_pre = visit_block_pre;
+ Walk.visit_block_post = visit_block_post }
+;;
+
+let process_crate
+ (cx:ctxt)
+ (crate:Ast.crate)
+ : unit =
+ let path = Stack.create () in
+ let passes =
+ [|
+ (loop_depth_visitor cx
+ Walk.empty_visitor)
+ |]
+ in
+
+ run_passes cx "loop" path passes (log cx "%s") crate;
+ ()
+;;
+
+
+(*
+ * 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/me/resolve.ml b/src/boot/me/resolve.ml
new file mode 100644
index 00000000..8f034aee
--- /dev/null
+++ b/src/boot/me/resolve.ml
@@ -0,0 +1,959 @@
+open Semant;;
+open Common;;
+
+(*
+ * Resolution passes:
+ *
+ * - build multiple 'scope' hashtables mapping slot_key -> node_id
+ * - build single 'type inference' hashtable mapping node_id -> slot
+ *
+ * (note: not every slot is identified; only those that are declared
+ * in statements and/or can participate in local type inference.
+ * Those in function signatures are not, f.e. Also no type values
+ * are identified, though module items are. )
+ *
+ *)
+
+
+let log cx = Session.log "resolve"
+ cx.ctxt_sess.Session.sess_log_resolve
+ cx.ctxt_sess.Session.sess_log_out
+;;
+
+let iflog cx thunk =
+ if cx.ctxt_sess.Session.sess_log_resolve
+ then thunk ()
+ else ()
+;;
+
+
+let block_scope_forming_visitor
+ (cx:ctxt)
+ (inner:Walk.visitor)
+ : Walk.visitor =
+ let visit_block_pre b =
+ if not (Hashtbl.mem cx.ctxt_block_items b.id)
+ then htab_put cx.ctxt_block_items b.id (Hashtbl.create 0);
+ if not (Hashtbl.mem cx.ctxt_block_slots b.id)
+ then htab_put cx.ctxt_block_slots b.id (Hashtbl.create 0);
+ inner.Walk.visit_block_pre b
+ in
+ { inner with Walk.visit_block_pre = visit_block_pre }
+;;
+
+
+let stmt_collecting_visitor
+ (cx:ctxt)
+ (inner:Walk.visitor)
+ : Walk.visitor =
+ let block_ids = Stack.create () in
+ let visit_block_pre (b:Ast.block) =
+ Stack.push b.id block_ids;
+ inner.Walk.visit_block_pre b
+ in
+ let visit_block_post (b:Ast.block) =
+ inner.Walk.visit_block_post b;
+ ignore (Stack.pop block_ids)
+ in
+
+ let visit_for_block
+ ((si:Ast.slot identified),(ident:Ast.ident))
+ (block_id:node_id)
+ : unit =
+ let slots = Hashtbl.find cx.ctxt_block_slots block_id in
+ let key = Ast.KEY_ident ident in
+ log cx "found decl of '%s' in for-loop block header" ident;
+ htab_put slots key si.id;
+ htab_put cx.ctxt_slot_keys si.id key
+ in
+
+ let visit_stmt_pre stmt =
+ begin
+ htab_put cx.ctxt_all_stmts stmt.id stmt;
+ match stmt.node with
+ Ast.STMT_decl d ->
+ begin
+ let bid = Stack.top block_ids in
+ let items = Hashtbl.find cx.ctxt_block_items bid in
+ let slots = Hashtbl.find cx.ctxt_block_slots bid in
+ let check_and_log_ident id ident =
+ if Hashtbl.mem items ident ||
+ Hashtbl.mem slots (Ast.KEY_ident ident)
+ then
+ err (Some id)
+ "duplicate declaration '%s' in block" ident
+ else
+ log cx "found decl of '%s' in block" ident
+ in
+ let check_and_log_tmp id tmp =
+ if Hashtbl.mem slots (Ast.KEY_temp tmp)
+ then
+ err (Some id)
+ "duplicate declaration of temp #%d in block"
+ (int_of_temp tmp)
+ else
+ log cx "found decl of temp #%d in block" (int_of_temp tmp)
+ in
+ let check_and_log_key id key =
+ match key with
+ Ast.KEY_ident i -> check_and_log_ident id i
+ | Ast.KEY_temp t -> check_and_log_tmp id t
+ in
+ match d with
+ Ast.DECL_mod_item (ident, item) ->
+ check_and_log_ident item.id ident;
+ htab_put items ident item.id
+ | Ast.DECL_slot (key, sid) ->
+ check_and_log_key sid.id key;
+ htab_put slots key sid.id;
+ htab_put cx.ctxt_slot_keys sid.id key
+ end
+ | Ast.STMT_for f ->
+ visit_for_block f.Ast.for_slot f.Ast.for_body.id
+ | Ast.STMT_for_each f ->
+ visit_for_block f.Ast.for_each_slot f.Ast.for_each_head.id
+ | Ast.STMT_alt_tag { Ast.alt_tag_arms = arms } ->
+ let rec resolve_pat block pat =
+ match pat with
+ Ast.PAT_slot ({ id = slot_id }, ident) ->
+ let slots = Hashtbl.find cx.ctxt_block_slots block.id in
+ let key = Ast.KEY_ident ident in
+ htab_put slots key slot_id;
+ htab_put cx.ctxt_slot_keys slot_id key
+ | Ast.PAT_tag (_, pats) -> Array.iter (resolve_pat block) pats
+ | Ast.PAT_lit _ | Ast.PAT_wild -> ()
+ in
+ Array.iter (fun { node = (p, b) } -> resolve_pat b p) arms
+ | _ -> ()
+ end;
+ inner.Walk.visit_stmt_pre stmt
+ in
+ { inner with
+ Walk.visit_block_pre = visit_block_pre;
+ Walk.visit_block_post = visit_block_post;
+ Walk.visit_stmt_pre = visit_stmt_pre }
+;;
+
+
+let all_item_collecting_visitor
+ (cx:ctxt)
+ (path:Ast.name_component Stack.t)
+ (inner:Walk.visitor)
+ : Walk.visitor =
+
+ let items = Stack.create () in
+
+ let push_on_item_arg_list item_id arg_id =
+ let existing =
+ match htab_search cx.ctxt_frame_args item_id with
+ None -> []
+ | Some x -> x
+ in
+ htab_put cx.ctxt_slot_is_arg arg_id ();
+ Hashtbl.replace cx.ctxt_frame_args item_id (arg_id :: existing)
+ in
+
+ let note_header item_id header =
+ Array.iter
+ (fun (sloti,ident) ->
+ let key = Ast.KEY_ident ident in
+ htab_put cx.ctxt_slot_keys sloti.id key;
+ push_on_item_arg_list item_id sloti.id)
+ header;
+ in
+
+ let visit_mod_item_pre n p i =
+ Stack.push i.id items;
+ Array.iter (fun p -> htab_put cx.ctxt_all_defns p.id
+ (DEFN_ty_param p.node)) p;
+ htab_put cx.ctxt_all_defns i.id (DEFN_item i.node);
+ htab_put cx.ctxt_all_item_names i.id (Walk.path_to_name path);
+ log cx "collected item #%d: %s" (int_of_node i.id) n;
+ begin
+ (* FIXME: this is incomplete. *)
+ match i.node.Ast.decl_item with
+ Ast.MOD_ITEM_fn f ->
+ note_header i.id f.Ast.fn_input_slots;
+ | Ast.MOD_ITEM_obj ob ->
+ note_header i.id ob.Ast.obj_state;
+ | Ast.MOD_ITEM_tag (header_slots, _, _) ->
+ let skey i = Printf.sprintf "_%d" i in
+ note_header i.id
+ (Array.mapi (fun i s -> (s, skey i)) header_slots)
+ | _ -> ()
+ end;
+ inner.Walk.visit_mod_item_pre n p i
+ in
+
+ let visit_mod_item_post n p i =
+ inner.Walk.visit_mod_item_post n p i;
+ ignore (Stack.pop items)
+ in
+
+ let visit_obj_fn_pre obj ident fn =
+ htab_put cx.ctxt_all_defns fn.id (DEFN_obj_fn (obj.id, fn.node));
+ htab_put cx.ctxt_all_item_names fn.id (Walk.path_to_name path);
+ note_header fn.id fn.node.Ast.fn_input_slots;
+ inner.Walk.visit_obj_fn_pre obj ident fn
+ in
+
+ let visit_obj_drop_pre obj b =
+ htab_put cx.ctxt_all_defns b.id (DEFN_obj_drop obj.id);
+ htab_put cx.ctxt_all_item_names b.id (Walk.path_to_name path);
+ inner.Walk.visit_obj_drop_pre obj b
+ in
+
+ let visit_stmt_pre s =
+ begin
+ match s.node with
+ Ast.STMT_for_each fe ->
+ let id = fe.Ast.for_each_body.id in
+ htab_put cx.ctxt_all_defns id
+ (DEFN_loop_body (Stack.top items));
+ htab_put cx.ctxt_all_item_names id
+ (Walk.path_to_name path);
+ | _ -> ()
+ end;
+ inner.Walk.visit_stmt_pre s;
+ in
+
+ { inner with
+ Walk.visit_mod_item_pre = visit_mod_item_pre;
+ Walk.visit_mod_item_post = visit_mod_item_post;
+ Walk.visit_obj_fn_pre = visit_obj_fn_pre;
+ Walk.visit_obj_drop_pre = visit_obj_drop_pre;
+ Walk.visit_stmt_pre = visit_stmt_pre; }
+;;
+
+
+let lookup_type_node_by_name
+ (cx:ctxt)
+ (scopes:scope list)
+ (name:Ast.name)
+ : node_id =
+ iflog cx (fun _ ->
+ log cx "lookup_simple_type_by_name %a"
+ Ast.sprintf_name name);
+ match lookup_by_name cx scopes name with
+ None -> err None "unknown name: %a" Ast.sprintf_name name
+ | Some (_, id) ->
+ match htab_search cx.ctxt_all_defns id with
+ Some (DEFN_item { Ast.decl_item = Ast.MOD_ITEM_type _ })
+ | Some (DEFN_item { Ast.decl_item = Ast.MOD_ITEM_obj _ })
+ | Some (DEFN_ty_param _) -> id
+ | _ ->
+ err None "Found non-type binding for %a"
+ Ast.sprintf_name name
+;;
+
+
+let get_ty_references
+ (t:Ast.ty)
+ (cx:ctxt)
+ (scopes:scope list)
+ : node_id list =
+ let base = ty_fold_list_concat () in
+ let ty_fold_named n =
+ [ lookup_type_node_by_name cx scopes n ]
+ in
+ let fold = { base with ty_fold_named = ty_fold_named } in
+ fold_ty fold t
+;;
+
+
+let type_reference_and_tag_extracting_visitor
+ (cx:ctxt)
+ (scopes:(scope list) ref)
+ (node_to_references:(node_id,node_id list) Hashtbl.t)
+ (all_tags:(node_id,(Ast.ty_tag * (scope list))) Hashtbl.t)
+ (inner:Walk.visitor)
+ : Walk.visitor =
+ let visit_mod_item_pre id params item =
+ begin
+ match item.node.Ast.decl_item with
+ Ast.MOD_ITEM_type ty ->
+ begin
+ log cx "extracting references for type node %d"
+ (int_of_node item.id);
+ let referenced = get_ty_references ty cx (!scopes) in
+ List.iter
+ (fun i -> log cx "type %d references type %d"
+ (int_of_node item.id) (int_of_node i)) referenced;
+ htab_put node_to_references item.id referenced;
+ match ty with
+ Ast.TY_tag ttag ->
+ htab_put all_tags item.id (ttag, (!scopes))
+ | _ -> ()
+ end
+ | _ -> ()
+ end;
+ inner.Walk.visit_mod_item_pre id params item
+ in
+ { inner with
+ Walk.visit_mod_item_pre = visit_mod_item_pre }
+;;
+
+
+type recur_info =
+ { recur_all_nodes: node_id list;
+ recur_curr_iso: (node_id array) option; }
+;;
+
+let empty_recur_info =
+ { recur_all_nodes = [];
+ recur_curr_iso = None }
+;;
+
+let push_node r n =
+ { r with recur_all_nodes = n :: r.recur_all_nodes }
+;;
+
+let set_iso r i =
+ { r with recur_curr_iso = Some i }
+;;
+
+
+let index_in_curr_iso (recur:recur_info) (node:node_id) : int option =
+ match recur.recur_curr_iso with
+ None -> None
+ | Some iso ->
+ let rec search i =
+ if i >= (Array.length iso)
+ then None
+ else
+ if iso.(i) = node
+ then Some i
+ else search (i+1)
+ in
+ search 0
+;;
+
+let need_ty_tag t =
+ match t with
+ Ast.TY_tag ttag -> ttag
+ | _ -> err None "needed ty_tag"
+;;
+
+
+let rec ty_iso_of
+ (cx:ctxt)
+ (recursive_tag_groups:(node_id,(node_id,unit) Hashtbl.t) Hashtbl.t)
+ (all_tags:(node_id,(Ast.ty_tag * (scope list))) Hashtbl.t)
+ (n:node_id)
+ : Ast.ty =
+ let _ = iflog cx (fun _ -> log cx "+++ ty_iso_of #%d" (int_of_node n)) in
+ let group_table = Hashtbl.find recursive_tag_groups n in
+ let group_array = Array.of_list (htab_keys group_table) in
+ let compare_nodes a_id b_id =
+ (* FIXME: this should sort by the sorted name-lists of the
+ *constructors* of the tag, not the tag type name. *)
+ let a_name = Hashtbl.find cx.ctxt_all_item_names a_id in
+ let b_name = Hashtbl.find cx.ctxt_all_item_names b_id in
+ compare a_name b_name
+ in
+ let recur = set_iso (push_node empty_recur_info n) group_array in
+ let resolve_member member =
+ let (tag, scopes) = Hashtbl.find all_tags member in
+ let ty = Ast.TY_tag tag in
+ let ty = resolve_type cx scopes recursive_tag_groups all_tags recur ty in
+ need_ty_tag ty
+ in
+ Array.sort compare_nodes group_array;
+ log cx "resolving node %d, %d-member iso group"
+ (int_of_node n) (Array.length group_array);
+ Array.iteri (fun i n -> log cx "member %d: %d" i
+ (int_of_node n)) group_array;
+ let group = Array.map resolve_member group_array in
+ let rec search i =
+ if i >= (Array.length group_array)
+ then err None "node is not a member of its own iso group"
+ else
+ if group_array.(i) = n
+ then i
+ else search (i+1)
+ in
+ let iso =
+ Ast.TY_iso { Ast.iso_index = (search 0);
+ Ast.iso_group = group }
+ in
+ iflog cx (fun _ ->
+ log cx "--- ty_iso_of #%d ==> %a"
+ (int_of_node n) Ast.sprintf_ty iso);
+ iso
+
+
+and lookup_type_by_name
+ (cx:ctxt)
+ (scopes:scope list)
+ (recursive_tag_groups:(node_id,(node_id,unit) Hashtbl.t) Hashtbl.t)
+ (all_tags:(node_id,(Ast.ty_tag * (scope list))) Hashtbl.t)
+ (recur:recur_info)
+ (name:Ast.name)
+ : ((scope list) * node_id * Ast.ty) =
+ iflog cx (fun _ ->
+ log cx "+++ lookup_type_by_name %a"
+ Ast.sprintf_name name);
+ match lookup_by_name cx scopes name with
+ None -> err None "unknown name: %a" Ast.sprintf_name name
+ | Some (scopes', id) ->
+ let ty, params =
+ match htab_search cx.ctxt_all_defns id with
+ Some (DEFN_item { Ast.decl_item = Ast.MOD_ITEM_type t;
+ Ast.decl_params = params }) ->
+ (t, Array.map (fun p -> p.node) params)
+ | Some (DEFN_item { Ast.decl_item = Ast.MOD_ITEM_obj ob;
+ Ast.decl_params = params }) ->
+ (Ast.TY_obj (ty_obj_of_obj ob),
+ Array.map (fun p -> p.node) params)
+ | Some (DEFN_ty_param (_, x)) ->
+ (Ast.TY_param x, [||])
+ | _ ->
+ err None "Found non-type binding for %a"
+ Ast.sprintf_name name
+ in
+ let args =
+ match name with
+ Ast.NAME_ext (_, Ast.COMP_app (_, args)) -> args
+ | Ast.NAME_base (Ast.BASE_app (_, args)) -> args
+ | _ -> [| |]
+ in
+ let args =
+ iflog cx (fun _ -> log cx
+ "lookup_type_by_name %a resolving %d type args"
+ Ast.sprintf_name name
+ (Array.length args));
+ Array.mapi
+ begin
+ fun i t ->
+ let t =
+ resolve_type cx scopes recursive_tag_groups
+ all_tags recur t
+ in
+ iflog cx (fun _ -> log cx
+ "lookup_type_by_name resolved arg %d to %a" i
+ Ast.sprintf_ty t);
+ t
+ end
+ args
+ in
+ iflog cx
+ begin
+ fun _ ->
+ log cx
+ "lookup_type_by_name %a found ty %a"
+ Ast.sprintf_name name Ast.sprintf_ty ty;
+ log cx "applying %d type args to %d params"
+ (Array.length args) (Array.length params);
+ log cx "params: %s"
+ (Ast.fmt_to_str Ast.fmt_decl_params params);
+ log cx "args: %s"
+ (Ast.fmt_to_str Ast.fmt_app_args args);
+ end;
+ let ty = rebuild_ty_under_params ty params args true in
+ iflog cx (fun _ -> log cx "--- lookup_type_by_name %a ==> %a"
+ Ast.sprintf_name name
+ Ast.sprintf_ty ty);
+ (scopes', id, ty)
+
+and resolve_type
+ (cx:ctxt)
+ (scopes:(scope list))
+ (recursive_tag_groups:(node_id,(node_id,unit) Hashtbl.t) Hashtbl.t)
+ (all_tags:(node_id,(Ast.ty_tag * (scope list))) Hashtbl.t)
+ (recur:recur_info)
+ (t:Ast.ty)
+ : Ast.ty =
+ let _ = iflog cx (fun _ -> log cx "+++ resolve_type %a" Ast.sprintf_ty t) in
+ let base = ty_fold_rebuild (fun t -> t) in
+ let ty_fold_named name =
+ let (scopes, node, t) =
+ lookup_type_by_name cx scopes recursive_tag_groups all_tags recur name
+ in
+ iflog cx (fun _ ->
+ log cx "resolved type name '%a' to item %d with ty %a"
+ Ast.sprintf_name name (int_of_node node) Ast.sprintf_ty t);
+ match index_in_curr_iso recur node with
+ Some i -> Ast.TY_idx i
+ | None ->
+ if Hashtbl.mem recursive_tag_groups node
+ then
+ begin
+ let ttag = need_ty_tag t in
+ Hashtbl.replace all_tags node (ttag, scopes);
+ ty_iso_of cx recursive_tag_groups all_tags node
+ end
+ else
+ if List.mem node recur.recur_all_nodes
+ then (err (Some node) "infinite recursive type definition: '%a'"
+ Ast.sprintf_name name)
+ else
+ let recur = push_node recur node in
+ iflog cx (fun _ -> log cx "recursively resolving type %a"
+ Ast.sprintf_ty t);
+ resolve_type cx scopes recursive_tag_groups all_tags recur t
+ in
+ let fold =
+ { base with
+ ty_fold_named = ty_fold_named; }
+ in
+ let t' = fold_ty fold t in
+ iflog cx (fun _ ->
+ log cx "--- resolve_type %a ==> %a"
+ Ast.sprintf_ty t Ast.sprintf_ty t');
+ t'
+;;
+
+
+let type_resolving_visitor
+ (cx:ctxt)
+ (scopes:(scope list) ref)
+ (recursive_tag_groups:(node_id,(node_id,unit) Hashtbl.t) Hashtbl.t)
+ (all_tags:(node_id,(Ast.ty_tag * (scope list))) Hashtbl.t)
+ (inner:Walk.visitor)
+ : Walk.visitor =
+
+ let resolve_ty (t:Ast.ty) : Ast.ty =
+ resolve_type cx (!scopes) recursive_tag_groups all_tags empty_recur_info t
+ in
+
+ let resolve_slot (s:Ast.slot) : Ast.slot =
+ match s.Ast.slot_ty with
+ None -> s
+ | Some ty -> { s with Ast.slot_ty = Some (resolve_ty ty) }
+ in
+
+ let resolve_slot_identified
+ (s:Ast.slot identified)
+ : (Ast.slot identified) =
+ try
+ let slot = resolve_slot s.node in
+ { s with node = slot }
+ with
+ Semant_err (None, e) -> raise (Semant_err ((Some s.id), e))
+ in
+
+ let visit_slot_identified_pre slot =
+ let slot = resolve_slot_identified slot in
+ htab_put cx.ctxt_all_defns slot.id (DEFN_slot slot.node);
+ log cx "collected resolved slot #%d with type %s" (int_of_node slot.id)
+ (match slot.node.Ast.slot_ty with
+ None -> "??"
+ | Some t -> (Ast.fmt_to_str Ast.fmt_ty t));
+ inner.Walk.visit_slot_identified_pre slot
+ in
+
+ let visit_mod_item_pre id params item =
+ begin
+ try
+ match item.node.Ast.decl_item with
+ Ast.MOD_ITEM_type ty ->
+ let ty =
+ resolve_type cx (!scopes) recursive_tag_groups
+ all_tags empty_recur_info ty
+ in
+ log cx "resolved item %s, defining type %a"
+ id Ast.sprintf_ty ty;
+ htab_put cx.ctxt_all_type_items item.id ty;
+ htab_put cx.ctxt_all_item_types item.id Ast.TY_type
+
+ (*
+ * Don't resolve the "type" of a mod item; just resolve its
+ * members.
+ *)
+ | Ast.MOD_ITEM_mod _ -> ()
+
+ | Ast.MOD_ITEM_tag (header_slots, _, nid)
+ when Hashtbl.mem recursive_tag_groups nid ->
+ begin
+ match ty_of_mod_item true item with
+ Ast.TY_fn (tsig, taux) ->
+ let input_slots =
+ Array.map
+ (fun sloti -> resolve_slot sloti.node)
+ header_slots
+ in
+ let output_slot =
+ interior_slot (ty_iso_of cx recursive_tag_groups
+ all_tags nid)
+ in
+ let ty =
+ Ast.TY_fn
+ ({tsig with
+ Ast.sig_input_slots = input_slots;
+ Ast.sig_output_slot = output_slot }, taux)
+ in
+ log cx "resolved recursive tag %s, type as %a"
+ id Ast.sprintf_ty ty;
+ htab_put cx.ctxt_all_item_types item.id ty
+ | _ -> bug () "recursive tag with non-function type"
+ end
+
+ | _ ->
+ let t = ty_of_mod_item true item in
+ let ty =
+ resolve_type cx (!scopes) recursive_tag_groups
+ all_tags empty_recur_info t
+ in
+ log cx "resolved item %s, type as %a" id Ast.sprintf_ty ty;
+ htab_put cx.ctxt_all_item_types item.id ty;
+ with
+ Semant_err (None, e) -> raise (Semant_err ((Some item.id), e))
+ end;
+ inner.Walk.visit_mod_item_pre id params item
+ in
+
+ let visit_obj_fn_pre obj ident fn =
+ let fty =
+ resolve_type cx (!scopes) recursive_tag_groups all_tags
+ empty_recur_info (Ast.TY_fn (ty_fn_of_fn fn.node))
+ in
+ log cx "resolved obj fn %s as %a" ident Ast.sprintf_ty fty;
+ htab_put cx.ctxt_all_item_types fn.id fty;
+ inner.Walk.visit_obj_fn_pre obj ident fn
+ in
+
+ let visit_obj_drop_pre obj b =
+ let fty = mk_simple_ty_fn [| |] in
+ htab_put cx.ctxt_all_item_types b.id fty;
+ inner.Walk.visit_obj_drop_pre obj b
+ in
+
+ let visit_stmt_pre stmt =
+ begin
+ match stmt.node with
+ Ast.STMT_for_each fe ->
+ let id = fe.Ast.for_each_body.id in
+ let fty = mk_simple_ty_iter [| |] in
+ htab_put cx.ctxt_all_item_types id fty;
+ | Ast.STMT_copy (_, Ast.EXPR_unary (Ast.UNOP_cast t, _)) ->
+ let ty = resolve_ty t.node in
+ htab_put cx.ctxt_all_cast_types t.id ty
+ | _ -> ()
+ end;
+ inner.Walk.visit_stmt_pre stmt
+ in
+
+ let visit_lval_pre lv =
+ let rec rebuild_lval' lv =
+ match lv with
+ Ast.LVAL_ext (base, ext) ->
+ let ext =
+ match ext with
+ Ast.COMP_named (Ast.COMP_ident _)
+ | Ast.COMP_named (Ast.COMP_idx _)
+ | Ast.COMP_atom (Ast.ATOM_literal _) -> ext
+ | Ast.COMP_atom (Ast.ATOM_lval lv) ->
+ Ast.COMP_atom (Ast.ATOM_lval (rebuild_lval lv))
+ | Ast.COMP_named (Ast.COMP_app (ident, params)) ->
+ Ast.COMP_named
+ (Ast.COMP_app (ident, Array.map resolve_ty params))
+ in
+ Ast.LVAL_ext (rebuild_lval' base, ext)
+
+ | Ast.LVAL_base nb ->
+ let node =
+ match nb.node with
+ Ast.BASE_ident _
+ | Ast.BASE_temp _ -> nb.node
+ | Ast.BASE_app (ident, params) ->
+ Ast.BASE_app (ident, Array.map resolve_ty params)
+ in
+ Ast.LVAL_base {nb with node = node}
+
+ and rebuild_lval lv =
+ let id = lval_base_id lv in
+ let lv' = rebuild_lval' lv in
+ iflog cx (fun _ -> log cx "rebuilt lval %a as %a (#%d)"
+ Ast.sprintf_lval lv Ast.sprintf_lval lv'
+ (int_of_node id));
+ htab_put cx.ctxt_all_lvals id lv';
+ lv'
+ in
+ ignore (rebuild_lval lv);
+ inner.Walk.visit_lval_pre lv
+ in
+
+ { inner with
+ Walk.visit_slot_identified_pre = visit_slot_identified_pre;
+ Walk.visit_mod_item_pre = visit_mod_item_pre;
+ Walk.visit_obj_fn_pre = visit_obj_fn_pre;
+ Walk.visit_obj_drop_pre = visit_obj_drop_pre;
+ Walk.visit_stmt_pre = visit_stmt_pre;
+ Walk.visit_lval_pre = visit_lval_pre; }
+;;
+
+
+let lval_base_resolving_visitor
+ (cx:ctxt)
+ (scopes:(scope list) ref)
+ (inner:Walk.visitor)
+ : Walk.visitor =
+ let lookup_referent_by_ident id ident =
+ log cx "looking up slot or item with ident '%s'" ident;
+ match lookup cx (!scopes) (Ast.KEY_ident ident) with
+ None -> err (Some id) "unresolved identifier '%s'" ident
+ | Some (_, id) -> (log cx "resolved to node id #%d"
+ (int_of_node id); id)
+ in
+ let lookup_slot_by_temp id temp =
+ log cx "looking up temp slot #%d" (int_of_temp temp);
+ let res = lookup cx (!scopes) (Ast.KEY_temp temp) in
+ match res with
+ None -> err
+ (Some id) "unresolved temp node #%d" (int_of_temp temp)
+ | Some (_, id) ->
+ (log cx "resolved to node id #%d" (int_of_node id); id)
+ in
+ let lookup_referent_by_name_base id nb =
+ match nb with
+ Ast.BASE_ident ident
+ | Ast.BASE_app (ident, _) -> lookup_referent_by_ident id ident
+ | Ast.BASE_temp temp -> lookup_slot_by_temp id temp
+ in
+
+ let visit_lval_pre lv =
+ let rec lookup_lval lv =
+ iflog cx (fun _ ->
+ log cx "looking up lval #%d"
+ (int_of_node (lval_base_id lv)));
+ match lv with
+ Ast.LVAL_ext (base, ext) ->
+ begin
+ lookup_lval base;
+ match ext with
+ Ast.COMP_atom (Ast.ATOM_lval lv') -> lookup_lval lv'
+ | _ -> ()
+ end
+ | Ast.LVAL_base nb ->
+ let referent_id = lookup_referent_by_name_base nb.id nb.node in
+ iflog cx (fun _ -> log cx "resolved lval #%d to referent #%d"
+ (int_of_node nb.id) (int_of_node referent_id));
+ htab_put cx.ctxt_lval_to_referent nb.id referent_id
+ in
+ lookup_lval lv;
+ inner.Walk.visit_lval_pre lv
+ in
+ { inner with
+ Walk.visit_lval_pre = visit_lval_pre }
+;;
+
+
+
+(*
+ * iso-recursion groups are very complicated.
+ *
+ * - iso groups are always rooted at *named* ty_tag nodes
+ *
+ * - consider:
+ *
+ * type colour = tag(red, green, blue);
+ * type list = tag(cons(colour, @list), nil())
+ *
+ * this should include list as an iso but not colour,
+ * should result in:
+ *
+ * type list = iso[<0>:tag(cons(tag(red,green,blue),@#1))]
+ *
+ * - consider:
+ *
+ * type colour = tag(red, green, blue);
+ * type tree = tag(children(@list), leaf(colour))
+ * type list = tag(cons(@tree, @list), nil())
+ *
+ * this should result in:
+ *
+ * type list = iso[<0>:tag(cons(@#2, @#1),nil());
+ * 1: tag(children(@#1),leaf(tag(red,green,blue)))]
+ *
+ * - how can you calculate these?
+ *
+ * - start by making a map from named-tag-node-id -> referenced-other-nodes
+ * - for each member in the set, if you can get from itself to itself, keep
+ * it, otherwise it's non-recursive => non-interesting, delete it.
+ * - group the members (now all recursive) by dependency
+ * - assign index-number to each elt of group
+ * - fully resolve each elt of group, turning names into numbers or chasing
+ * through to fully-resolving targets as necessary
+ * - place group in iso, store differently-indexed value in table for each
+ *
+ *
+ * - what are the illegal forms?
+ * - recursion that takes indefinite storage to form a tag, eg.
+ *
+ * type t = tag(foo(t));
+ *
+ * - recursion that makes a tag unconstructable, eg:
+ *
+ * type t = tag(foo(@t));
+ *)
+
+let resolve_recursion
+ (cx:ctxt)
+ (node_to_references:(node_id,node_id list) Hashtbl.t)
+ (recursive_tag_groups:(node_id,(node_id,unit) Hashtbl.t) Hashtbl.t)
+ : unit =
+
+ let recursive_tag_types = Hashtbl.create 0 in
+
+ let rec can_reach
+ (target:node_id)
+ (visited:node_id list)
+ (curr:node_id)
+ : bool =
+ if List.mem curr visited
+ then false
+ else
+ match htab_search node_to_references curr with
+ None -> false
+ | Some referenced ->
+ if List.mem target referenced
+ then true
+ else List.exists (can_reach target (curr :: visited)) referenced
+ in
+
+ let extract_recursive_tags _ =
+ Hashtbl.iter
+ begin fun id _ ->
+ if can_reach id [] id
+ then begin
+ match Hashtbl.find cx.ctxt_all_defns id with
+ DEFN_item
+ { Ast.decl_item = Ast.MOD_ITEM_type (Ast.TY_tag _) } ->
+ log cx "type %d is a recursive tag" (int_of_node id);
+ Hashtbl.replace recursive_tag_types id ()
+ | _ ->
+ log cx "type %d is recursive, but not a tag" (int_of_node id);
+ end
+ else log cx "type %d is non-recursive" (int_of_node id);
+ end
+ node_to_references
+ in
+
+ let group_recursive_tags _ =
+ while (Hashtbl.length recursive_tag_types) != 0 do
+ let keys = htab_keys recursive_tag_types in
+ let root = List.hd keys in
+ let group = Hashtbl.create 0 in
+ let rec walk visited node =
+ if List.mem node visited
+ then ()
+ else
+ begin
+ if Hashtbl.mem recursive_tag_types node
+ then
+ begin
+ Hashtbl.remove recursive_tag_types node;
+ htab_put recursive_tag_groups node group;
+ htab_put group node ();
+ log cx "recursion group rooted at tag %d contains tag %d"
+ (int_of_node root) (int_of_node node);
+ end;
+ match htab_search node_to_references node with
+ None -> ()
+ | Some referenced ->
+ List.iter (walk (node :: visited)) referenced
+ end
+ in
+ walk [] root;
+ done
+ in
+
+ begin
+ extract_recursive_tags ();
+ group_recursive_tags ();
+ log cx "found %d independent type-recursion groups"
+ (Hashtbl.length recursive_tag_groups);
+ end
+;;
+
+let pattern_resolving_visitor
+ (cx:ctxt)
+ (scopes:scope list ref)
+ (inner:Walk.visitor) : Walk.visitor =
+ let visit_stmt_pre stmt =
+ begin
+ match stmt.node with
+ Ast.STMT_alt_tag { Ast.alt_tag_lval = _; Ast.alt_tag_arms = arms } ->
+ let resolve_arm { node = arm } =
+ match fst arm with
+ Ast.PAT_tag (ident, _) ->
+ begin
+ match lookup_by_ident cx !scopes ident with
+ None ->
+ err None "unresolved tag constructor '%s'" ident
+ | Some (_, tag_id) ->
+ match Hashtbl.find cx.ctxt_all_defns tag_id with
+ DEFN_item {
+ Ast.decl_item = Ast.MOD_ITEM_tag _
+ } -> ()
+ | _ ->
+ err None "'%s' is not a tag constructor" ident
+ end
+ | _ -> ()
+
+ in
+ Array.iter resolve_arm arms
+ | _ -> ()
+ end;
+ inner.Walk.visit_stmt_pre stmt
+ in
+ { inner with Walk.visit_stmt_pre = visit_stmt_pre }
+;;
+
+let process_crate
+ (cx:ctxt)
+ (crate:Ast.crate)
+ : unit =
+ let (scopes:(scope list) ref) = ref [] in
+ let path = Stack.create () in
+
+ let node_to_references = Hashtbl.create 0 in
+ let all_tags = Hashtbl.create 0 in
+ let recursive_tag_groups = Hashtbl.create 0 in
+
+ let passes_0 =
+ [|
+ (block_scope_forming_visitor cx Walk.empty_visitor);
+ (stmt_collecting_visitor cx
+ (all_item_collecting_visitor cx path
+ Walk.empty_visitor));
+ (scope_stack_managing_visitor scopes
+ (type_reference_and_tag_extracting_visitor
+ cx scopes node_to_references all_tags
+ Walk.empty_visitor))
+ |]
+ in
+ let passes_1 =
+ [|
+ (scope_stack_managing_visitor scopes
+ (type_resolving_visitor cx scopes
+ recursive_tag_groups all_tags
+ (lval_base_resolving_visitor cx scopes
+ Walk.empty_visitor)));
+ |]
+ in
+ let passes_2 =
+ [|
+ (scope_stack_managing_visitor scopes
+ (pattern_resolving_visitor cx scopes
+ Walk.empty_visitor))
+ |]
+ in
+ log cx "running primary resolve passes";
+ run_passes cx "resolve collect" path passes_0 (log cx "%s") crate;
+ resolve_recursion cx node_to_references recursive_tag_groups;
+ log cx "running secondary resolve passes";
+ run_passes cx "resolve bind" path passes_1 (log cx "%s") crate;
+ log cx "running tertiary resolve passes";
+ run_passes cx "resolve patterns" path passes_2 (log cx "%s") crate
+;;
+
+(*
+ * 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/me/semant.ml b/src/boot/me/semant.ml
new file mode 100644
index 00000000..b5000ff3
--- /dev/null
+++ b/src/boot/me/semant.ml
@@ -0,0 +1,1969 @@
+
+open Common;;
+
+type slots_table = (Ast.slot_key,node_id) Hashtbl.t
+type items_table = (Ast.ident,node_id) Hashtbl.t
+type block_slots_table = (node_id,slots_table) Hashtbl.t
+type block_items_table = (node_id,items_table) Hashtbl.t
+;;
+
+
+type code = {
+ code_fixup: fixup;
+ code_quads: Il.quads;
+ code_vregs_and_spill: (int * fixup) option;
+}
+;;
+
+type glue =
+ GLUE_activate
+ | GLUE_yield
+ | GLUE_exit_main_task
+ | GLUE_exit_task
+ | GLUE_mark of Ast.ty
+ | GLUE_drop of Ast.ty
+ | GLUE_free of Ast.ty
+ | GLUE_copy of Ast.ty (* One-level copy. *)
+ | GLUE_clone of Ast.ty (* Deep copy. *)
+ | GLUE_compare of Ast.ty
+ | GLUE_hash of Ast.ty
+ | GLUE_write of Ast.ty
+ | GLUE_read of Ast.ty
+ | GLUE_unwind
+ | GLUE_get_next_pc
+ | GLUE_mark_frame of node_id (* node is the frame *)
+ | GLUE_drop_frame of node_id (* node is the frame *)
+ | GLUE_reloc_frame of node_id (* node is the frame *)
+ | GLUE_fn_binding of node_id (* node is the 'bind' stmt *)
+ | GLUE_obj_drop of node_id (* node is the obj *)
+ | GLUE_loop_body of node_id (* node is the 'for each' body block *)
+ | GLUE_forward of (Ast.ident * Ast.ty_obj * Ast.ty_obj)
+;;
+
+type data =
+ DATA_str of string
+ | DATA_name of Ast.name
+ | DATA_tydesc of Ast.ty
+ | DATA_frame_glue_fns of node_id
+ | DATA_obj_vtbl of node_id
+ | DATA_forwarding_vtbl of (Ast.ty_obj * Ast.ty_obj)
+ | DATA_crate
+;;
+
+type defn =
+ DEFN_slot of Ast.slot
+ | DEFN_item of Ast.mod_item_decl
+ | DEFN_ty_param of Ast.ty_param
+ | DEFN_obj_fn of (node_id * Ast.fn)
+ | DEFN_obj_drop of node_id
+ | DEFN_loop_body of node_id
+;;
+
+type glue_code = (glue, code) Hashtbl.t;;
+type item_code = (node_id, code) Hashtbl.t;;
+type file_code = (node_id, item_code) Hashtbl.t;;
+type data_frags = (data, (fixup * Asm.frag)) Hashtbl.t;;
+
+let string_of_name (n:Ast.name) : string =
+ Ast.fmt_to_str Ast.fmt_name n
+;;
+
+(* The only need for a carg is to uniquely identify a constraint-arg
+ * in a scope-independent fashion. So we just look up the node that's
+ * used as the base of any such arg and glue it on the front of the
+ * symbolic name.
+ *)
+
+type constr_key_arg = Constr_arg_node of (node_id * Ast.carg_path)
+ | Constr_arg_lit of Ast.lit
+type constr_key =
+ Constr_pred of (node_id * constr_key_arg array)
+ | Constr_init of node_id
+
+type ctxt =
+ { ctxt_sess: Session.sess;
+ ctxt_frame_args: (node_id,node_id list) Hashtbl.t;
+ ctxt_frame_blocks: (node_id,node_id list) Hashtbl.t;
+ ctxt_block_slots: block_slots_table;
+ ctxt_block_items: block_items_table;
+ ctxt_slot_is_arg: (node_id,unit) Hashtbl.t;
+ ctxt_slot_keys: (node_id,Ast.slot_key) Hashtbl.t;
+ ctxt_all_item_names: (node_id,Ast.name) Hashtbl.t;
+ ctxt_all_item_types: (node_id,Ast.ty) Hashtbl.t;
+ ctxt_all_lval_types: (node_id,Ast.ty) Hashtbl.t;
+ 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_item_files: (node_id,filename) Hashtbl.t;
+ ctxt_all_lvals: (node_id,Ast.lval) Hashtbl.t;
+
+ (* definition id --> definition *)
+ ctxt_all_defns: (node_id,defn) Hashtbl.t;
+
+ (* reference id --> definition id *)
+ ctxt_lval_to_referent: (node_id,node_id) Hashtbl.t;
+
+ ctxt_required_items: (node_id, (required_lib * nabi_conv)) Hashtbl.t;
+ ctxt_required_syms: (node_id, string) Hashtbl.t;
+
+ (* Layout-y stuff. *)
+ ctxt_slot_aliased: (node_id,unit) Hashtbl.t;
+ ctxt_slot_is_obj_state: (node_id,unit) Hashtbl.t;
+ ctxt_slot_vregs: (node_id,((int option) ref)) Hashtbl.t;
+ ctxt_slot_offsets: (node_id,size) Hashtbl.t;
+ ctxt_frame_sizes: (node_id,size) Hashtbl.t;
+ ctxt_call_sizes: (node_id,size) Hashtbl.t;
+ ctxt_block_is_loop_body: (node_id,unit) Hashtbl.t;
+ ctxt_stmt_loop_depths: (node_id,int) Hashtbl.t;
+ ctxt_slot_loop_depths: (node_id,int) Hashtbl.t;
+
+ (* Typestate-y stuff. *)
+ ctxt_constrs: (constr_id,constr_key) Hashtbl.t;
+ ctxt_constr_ids: (constr_key,constr_id) Hashtbl.t;
+ ctxt_preconditions: (node_id,Bits.t) Hashtbl.t;
+ ctxt_postconditions: (node_id,Bits.t) Hashtbl.t;
+ ctxt_prestates: (node_id,Bits.t) Hashtbl.t;
+ ctxt_poststates: (node_id,Bits.t) Hashtbl.t;
+ ctxt_call_lval_params: (node_id,Ast.ty array) Hashtbl.t;
+ ctxt_copy_stmt_is_init: (node_id,unit) Hashtbl.t;
+ ctxt_post_stmt_slot_drops: (node_id,node_id list) Hashtbl.t;
+
+ (* Translation-y stuff. *)
+ ctxt_fn_fixups: (node_id,fixup) Hashtbl.t;
+ ctxt_block_fixups: (node_id,fixup) Hashtbl.t;
+ ctxt_file_fixups: (node_id,fixup) Hashtbl.t;
+ ctxt_spill_fixups: (node_id,fixup) Hashtbl.t;
+ ctxt_abi: Abi.abi;
+ ctxt_activate_fixup: fixup;
+ ctxt_yield_fixup: fixup;
+ ctxt_unwind_fixup: fixup;
+ ctxt_exit_task_fixup: fixup;
+
+ ctxt_debug_aranges_fixup: fixup;
+ ctxt_debug_pubnames_fixup: fixup;
+ ctxt_debug_info_fixup: fixup;
+ ctxt_debug_abbrev_fixup: fixup;
+ ctxt_debug_line_fixup: fixup;
+ ctxt_debug_frame_fixup: fixup;
+
+ ctxt_image_base_fixup: fixup;
+ ctxt_crate_fixup: fixup;
+
+ ctxt_file_code: file_code;
+ ctxt_all_item_code: item_code;
+ ctxt_glue_code: glue_code;
+ ctxt_data: data_frags;
+
+ ctxt_native_required:
+ (required_lib,((string,fixup) Hashtbl.t)) Hashtbl.t;
+ ctxt_native_provided:
+ (segment,((string, fixup) Hashtbl.t)) Hashtbl.t;
+
+ ctxt_required_rust_sym_num: (node_id, int) Hashtbl.t;
+ ctxt_required_c_sym_num: ((required_lib * string), int) Hashtbl.t;
+ ctxt_required_lib_num: (required_lib, int) Hashtbl.t;
+
+ ctxt_main_fn_fixup: fixup option;
+ ctxt_main_name: string option;
+ }
+;;
+
+let new_ctxt sess abi crate =
+ { ctxt_sess = sess;
+ ctxt_frame_args = Hashtbl.create 0;
+ ctxt_frame_blocks = Hashtbl.create 0;
+ ctxt_block_slots = Hashtbl.create 0;
+ ctxt_block_items = Hashtbl.create 0;
+ ctxt_slot_is_arg = Hashtbl.create 0;
+ ctxt_slot_keys = Hashtbl.create 0;
+ ctxt_all_item_names = Hashtbl.create 0;
+ ctxt_all_item_types = Hashtbl.create 0;
+ ctxt_all_lval_types = Hashtbl.create 0;
+ ctxt_all_cast_types = Hashtbl.create 0;
+ ctxt_all_type_items = Hashtbl.create 0;
+ ctxt_all_stmts = Hashtbl.create 0;
+ ctxt_item_files = crate.Ast.crate_files;
+ ctxt_all_lvals = Hashtbl.create 0;
+ ctxt_all_defns = Hashtbl.create 0;
+ ctxt_lval_to_referent = Hashtbl.create 0;
+ ctxt_required_items = crate.Ast.crate_required;
+ ctxt_required_syms = crate.Ast.crate_required_syms;
+
+ ctxt_constrs = Hashtbl.create 0;
+ ctxt_constr_ids = Hashtbl.create 0;
+ ctxt_preconditions = Hashtbl.create 0;
+ ctxt_postconditions = Hashtbl.create 0;
+ ctxt_prestates = Hashtbl.create 0;
+ ctxt_poststates = Hashtbl.create 0;
+ ctxt_copy_stmt_is_init = Hashtbl.create 0;
+ ctxt_post_stmt_slot_drops = Hashtbl.create 0;
+ ctxt_call_lval_params = Hashtbl.create 0;
+
+ ctxt_slot_aliased = Hashtbl.create 0;
+ ctxt_slot_is_obj_state = Hashtbl.create 0;
+ ctxt_slot_vregs = Hashtbl.create 0;
+ ctxt_slot_offsets = Hashtbl.create 0;
+ ctxt_frame_sizes = Hashtbl.create 0;
+ ctxt_call_sizes = Hashtbl.create 0;
+
+ ctxt_block_is_loop_body = Hashtbl.create 0;
+ ctxt_slot_loop_depths = Hashtbl.create 0;
+ ctxt_stmt_loop_depths = Hashtbl.create 0;
+
+ ctxt_fn_fixups = Hashtbl.create 0;
+ ctxt_block_fixups = Hashtbl.create 0;
+ ctxt_file_fixups = Hashtbl.create 0;
+ ctxt_spill_fixups = Hashtbl.create 0;
+ ctxt_abi = abi;
+ ctxt_activate_fixup = new_fixup "activate glue";
+ ctxt_yield_fixup = new_fixup "yield glue";
+ ctxt_unwind_fixup = new_fixup "unwind glue";
+ ctxt_exit_task_fixup = new_fixup "exit-task glue";
+
+ ctxt_debug_aranges_fixup = new_fixup "debug_aranges section";
+ ctxt_debug_pubnames_fixup = new_fixup "debug_pubnames section";
+ ctxt_debug_info_fixup = new_fixup "debug_info section";
+ ctxt_debug_abbrev_fixup = new_fixup "debug_abbrev section";
+ ctxt_debug_line_fixup = new_fixup "debug_line section";
+ ctxt_debug_frame_fixup = new_fixup "debug_frame section";
+
+ ctxt_image_base_fixup = new_fixup "loaded image base";
+ ctxt_crate_fixup = new_fixup "root crate structure";
+ ctxt_file_code = Hashtbl.create 0;
+ ctxt_all_item_code = Hashtbl.create 0;
+ ctxt_glue_code = Hashtbl.create 0;
+ ctxt_data = Hashtbl.create 0;
+
+ ctxt_native_required = Hashtbl.create 0;
+ ctxt_native_provided = Hashtbl.create 0;
+
+ ctxt_required_rust_sym_num = Hashtbl.create 0;
+ ctxt_required_c_sym_num = Hashtbl.create 0;
+ ctxt_required_lib_num = Hashtbl.create 0;
+
+ ctxt_main_fn_fixup =
+ (match crate.Ast.crate_main with
+ None -> None
+ | Some n -> Some (new_fixup (string_of_name n)));
+
+ ctxt_main_name =
+ (match crate.Ast.crate_main with
+ None -> None
+ | Some n -> Some (string_of_name n));
+ }
+;;
+
+let report_err cx ido str =
+ let sess = cx.ctxt_sess in
+ let spano = match ido with
+ None -> None
+ | Some id -> (Session.get_span sess id)
+ in
+ match spano with
+ None ->
+ Session.fail sess "Error: %s\n%!" str
+ | Some span ->
+ Session.fail sess "%s:E:Error: %s\n%!"
+ (Session.string_of_span span) str
+;;
+
+let bugi (cx:ctxt) (i:node_id) =
+ let k s =
+ report_err cx (Some i) s;
+ failwith s
+ in Printf.ksprintf k
+;;
+
+(* Convenience accessors. *)
+
+(* resolve an lval reference id to the id of its definition *)
+let lval_to_referent (cx:ctxt) (id:node_id) : node_id =
+ if Hashtbl.mem cx.ctxt_lval_to_referent id
+ then Hashtbl.find cx.ctxt_lval_to_referent id
+ else bug () "unresolved lval"
+;;
+
+(* resolve an lval reference id to its definition *)
+let resolve_lval_id (cx:ctxt) (id:node_id) : defn =
+ Hashtbl.find cx.ctxt_all_defns (lval_to_referent cx id)
+;;
+
+let referent_is_slot (cx:ctxt) (id:node_id) : bool =
+ match Hashtbl.find cx.ctxt_all_defns id with
+ DEFN_slot _ -> true
+ | _ -> false
+;;
+
+let referent_is_item (cx:ctxt) (id:node_id) : bool =
+ match Hashtbl.find cx.ctxt_all_defns id with
+ DEFN_item _ -> true
+ | _ -> false
+;;
+
+(* coerce an lval definition id to a slot *)
+let referent_to_slot (cx:ctxt) (id:node_id) : Ast.slot =
+ match Hashtbl.find cx.ctxt_all_defns id with
+ DEFN_slot slot -> slot
+ | _ -> bugi cx id "unknown slot"
+;;
+
+(* coerce an lval reference id to its definition slot *)
+let lval_to_slot (cx:ctxt) (id:node_id) : Ast.slot =
+ match resolve_lval_id cx id with
+ DEFN_slot slot -> slot
+ | _ -> bugi cx id "unknown slot"
+;;
+
+let get_stmt_depth (cx:ctxt) (id:node_id) : int =
+ Hashtbl.find cx.ctxt_stmt_loop_depths id
+;;
+
+let get_slot_depth (cx:ctxt) (id:node_id) : int =
+ Hashtbl.find cx.ctxt_slot_loop_depths id
+;;
+
+let get_fn_fixup (cx:ctxt) (id:node_id) : fixup =
+ if Hashtbl.mem cx.ctxt_fn_fixups id
+ then Hashtbl.find cx.ctxt_fn_fixups id
+ else bugi cx id "fn without fixup"
+;;
+
+let get_framesz (cx:ctxt) (id:node_id) : size =
+ if Hashtbl.mem cx.ctxt_frame_sizes id
+ then Hashtbl.find cx.ctxt_frame_sizes id
+ else bugi cx id "missing framesz"
+;;
+
+let get_callsz (cx:ctxt) (id:node_id) : size =
+ if Hashtbl.mem cx.ctxt_call_sizes id
+ then Hashtbl.find cx.ctxt_call_sizes id
+ else bugi cx id "missing callsz"
+;;
+
+let rec n_item_ty_params (cx:ctxt) (id:node_id) : int =
+ match Hashtbl.find cx.ctxt_all_defns id with
+ DEFN_item i -> Array.length i.Ast.decl_params
+ | DEFN_obj_fn (oid,_) -> n_item_ty_params cx oid
+ | DEFN_obj_drop oid -> n_item_ty_params cx oid
+ | DEFN_loop_body fid -> n_item_ty_params cx fid
+ | _ -> bugi cx id "n_item_ty_params on non-item"
+;;
+
+let item_is_obj_fn (cx:ctxt) (id:node_id) : bool =
+ match Hashtbl.find cx.ctxt_all_defns id with
+ DEFN_obj_fn _
+ | DEFN_obj_drop _ -> true
+ | _ -> false
+;;
+
+let get_spill (cx:ctxt) (id:node_id) : fixup =
+ if Hashtbl.mem cx.ctxt_spill_fixups id
+ then Hashtbl.find cx.ctxt_spill_fixups id
+ else bugi cx id "missing spill fixup"
+;;
+
+let require_native (cx:ctxt) (lib:required_lib) (name:string) : fixup =
+ let lib_tab = (htab_search_or_add cx.ctxt_native_required lib
+ (fun _ -> Hashtbl.create 0))
+ in
+ htab_search_or_add lib_tab name
+ (fun _ -> new_fixup ("require: " ^ name))
+;;
+
+let provide_native (cx:ctxt) (seg:segment) (name:string) : fixup =
+ let seg_tab = (htab_search_or_add cx.ctxt_native_provided seg
+ (fun _ -> Hashtbl.create 0))
+ in
+ htab_search_or_add seg_tab name
+ (fun _ -> new_fixup ("provide: " ^ name))
+;;
+
+let provide_existing_native
+ (cx:ctxt)
+ (seg:segment)
+ (name:string)
+ (fix:fixup)
+ : unit =
+ let seg_tab = (htab_search_or_add cx.ctxt_native_provided seg
+ (fun _ -> Hashtbl.create 0))
+ in
+ htab_put seg_tab name fix
+;;
+
+let slot_ty (s:Ast.slot) : Ast.ty =
+ match s.Ast.slot_ty with
+ Some t -> t
+ | None -> bug () "untyped slot"
+;;
+
+let defn_is_slot (d:defn) : bool =
+ match d with
+ DEFN_slot _ -> true
+ | _ -> false
+;;
+
+let defn_is_item (d:defn) : bool =
+ match d with
+ DEFN_item _ -> true
+ | _ -> false
+;;
+
+let slot_is_obj_state (cx:ctxt) (sid:node_id) : bool =
+ Hashtbl.mem cx.ctxt_slot_is_obj_state sid
+;;
+
+
+(* determines whether d defines a statically-known value *)
+let defn_is_static (d:defn) : bool =
+ not (defn_is_slot d)
+;;
+
+let defn_is_callable (d:defn) : bool =
+ match d with
+ DEFN_slot { Ast.slot_ty = Some Ast.TY_fn _ }
+ | DEFN_item { Ast.decl_item = (Ast.MOD_ITEM_fn _ ) } -> true
+ | _ -> false
+;;
+
+(* Constraint manipulation. *)
+
+let rec apply_names_to_carg_path
+ (names:(Ast.name_base option) array)
+ (cp:Ast.carg_path)
+ : Ast.carg_path =
+ match cp with
+ Ast.CARG_ext (Ast.CARG_base Ast.BASE_formal,
+ Ast.COMP_idx i) ->
+ begin
+ match names.(i) with
+ Some nb ->
+ Ast.CARG_base (Ast.BASE_named nb)
+ | None -> bug () "Indexing off non-named carg"
+ end
+ | Ast.CARG_ext (cp', e) ->
+ Ast.CARG_ext (apply_names_to_carg_path names cp', e)
+ | _ -> cp
+;;
+
+let apply_names_to_carg
+ (names:(Ast.name_base option) array)
+ (carg:Ast.carg)
+ : Ast.carg =
+ match carg with
+ Ast.CARG_path cp ->
+ Ast.CARG_path (apply_names_to_carg_path names cp)
+ | Ast.CARG_lit _ -> carg
+;;
+
+let apply_names_to_constr
+ (names:(Ast.name_base option) array)
+ (constr:Ast.constr)
+ : Ast.constr =
+ { constr with
+ Ast.constr_args =
+ Array.map (apply_names_to_carg names) constr.Ast.constr_args }
+;;
+
+let atoms_to_names (atoms:Ast.atom array)
+ : (Ast.name_base option) array =
+ Array.map
+ begin
+ fun atom ->
+ match atom with
+ Ast.ATOM_lval (Ast.LVAL_base nbi) -> Some nbi.node
+ | _ -> None
+ end
+ atoms
+;;
+
+let rec lval_base_id (lv:Ast.lval) : node_id =
+ match lv with
+ Ast.LVAL_base nbi -> nbi.id
+ | Ast.LVAL_ext (lv, _) -> lval_base_id lv
+;;
+
+let rec lval_base_slot (cx:ctxt) (lv:Ast.lval) : node_id option =
+ match lv with
+ Ast.LVAL_base nbi ->
+ let referent = lval_to_referent cx nbi.id in
+ if referent_is_slot cx referent
+ then Some referent
+ else None
+ | Ast.LVAL_ext (lv, _) -> lval_base_slot cx lv
+;;
+
+let rec lval_slots (cx:ctxt) (lv:Ast.lval) : node_id array =
+ match lv with
+ Ast.LVAL_base nbi ->
+ let referent = lval_to_referent cx nbi.id in
+ if referent_is_slot cx referent
+ then [| referent |]
+ else [| |]
+ | Ast.LVAL_ext (lv, Ast.COMP_named _) -> lval_slots cx lv
+ | Ast.LVAL_ext (lv, Ast.COMP_atom a) ->
+ Array.append (lval_slots cx lv) (atom_slots cx a)
+
+and atom_slots (cx:ctxt) (a:Ast.atom) : node_id array =
+ match a with
+ Ast.ATOM_literal _ -> [| |]
+ | Ast.ATOM_lval lv -> lval_slots cx lv
+;;
+
+let lval_option_slots (cx:ctxt) (lv:Ast.lval option) : node_id array =
+ match lv with
+ None -> [| |]
+ | Some lv -> lval_slots cx lv
+;;
+
+let resolve_lval (cx:ctxt) (lv:Ast.lval) : defn =
+ resolve_lval_id cx (lval_base_id lv)
+;;
+
+let atoms_slots (cx:ctxt) (az:Ast.atom array) : node_id array =
+ Array.concat (List.map (atom_slots cx) (Array.to_list az))
+;;
+
+let tup_inputs_slots (cx:ctxt) (az:Ast.tup_input array) : node_id array =
+ Array.concat (List.map
+ (fun (_,_,a) -> atom_slots cx a)
+ (Array.to_list az))
+;;
+
+let rec_inputs_slots (cx:ctxt)
+ (inputs:Ast.rec_input array) : node_id array =
+ Array.concat (List.map
+ (fun (_, _, _, atom) -> atom_slots cx atom)
+ (Array.to_list inputs))
+;;
+
+let expr_slots (cx:ctxt) (e:Ast.expr) : node_id array =
+ match e with
+ Ast.EXPR_binary (_, a, b) ->
+ Array.append (atom_slots cx a) (atom_slots cx b)
+ | Ast.EXPR_unary (_, u) -> atom_slots cx u
+ | Ast.EXPR_atom a -> atom_slots cx a
+;;
+
+
+(* Type extraction. *)
+
+let interior_slot_full mut ty : Ast.slot =
+ { Ast.slot_mode = Ast.MODE_interior;
+ Ast.slot_mutable = mut;
+ Ast.slot_ty = Some ty }
+;;
+
+let exterior_slot_full mut ty : Ast.slot =
+ { Ast.slot_mode = Ast.MODE_exterior;
+ Ast.slot_mutable = mut;
+ Ast.slot_ty = Some ty }
+;;
+
+let interior_slot ty : Ast.slot = interior_slot_full false ty
+;;
+
+let exterior_slot ty : Ast.slot = exterior_slot_full false ty
+;;
+
+
+(* General folds of Ast.ty. *)
+
+type ('ty, 'slot, 'slots, 'tag) ty_fold =
+ {
+ (* Functions that correspond to interior nodes in Ast.ty. *)
+ ty_fold_slot : (Ast.mode * bool * 'ty) -> 'slot;
+ ty_fold_slots : ('slot array) -> 'slots;
+ ty_fold_tags : (Ast.name, 'slots) Hashtbl.t -> 'tag;
+
+ (* Functions that correspond to the Ast.ty constructors. *)
+ ty_fold_any: unit -> 'ty;
+ ty_fold_nil : unit -> 'ty;
+ ty_fold_bool : unit -> 'ty;
+ ty_fold_mach : ty_mach -> 'ty;
+ ty_fold_int : unit -> 'ty;
+ ty_fold_uint : unit -> 'ty;
+ ty_fold_char : unit -> 'ty;
+ ty_fold_str : unit -> 'ty;
+ ty_fold_tup : 'slots -> 'ty;
+ ty_fold_vec : 'slot -> 'ty;
+ ty_fold_rec : (Ast.ident * 'slot) array -> 'ty;
+ ty_fold_tag : 'tag -> 'ty;
+ ty_fold_iso : (int * 'tag array) -> 'ty;
+ ty_fold_idx : int -> 'ty;
+ ty_fold_fn : (('slots * Ast.constrs * 'slot) * Ast.ty_fn_aux) -> 'ty;
+ ty_fold_obj : (Ast.effect
+ * (Ast.ident, (('slots * Ast.constrs * 'slot) *
+ Ast.ty_fn_aux)) Hashtbl.t) -> 'ty;
+ ty_fold_chan : 'ty -> 'ty;
+ ty_fold_port : 'ty -> 'ty;
+ ty_fold_task : unit -> 'ty;
+ ty_fold_native : opaque_id -> 'ty;
+ ty_fold_param : (int * Ast.effect) -> 'ty;
+ ty_fold_named : Ast.name -> 'ty;
+ ty_fold_type : unit -> 'ty;
+ ty_fold_constrained : ('ty * Ast.constrs) -> 'ty }
+;;
+
+let rec fold_ty (f:('ty, 'slot, 'slots, 'tag) ty_fold) (ty:Ast.ty) : 'ty =
+ let fold_slot (s:Ast.slot) : 'slot =
+ f.ty_fold_slot (s.Ast.slot_mode,
+ s.Ast.slot_mutable,
+ fold_ty f (slot_ty s))
+ in
+ let fold_slots (slots:Ast.slot array) : 'slots =
+ f.ty_fold_slots (Array.map fold_slot slots)
+ in
+ let fold_tags (ttag:Ast.ty_tag) : 'tag =
+ f.ty_fold_tags (htab_map ttag (fun k v -> (k, fold_slots v)))
+ in
+ let fold_sig tsig =
+ (fold_slots tsig.Ast.sig_input_slots,
+ tsig.Ast.sig_input_constrs,
+ fold_slot tsig.Ast.sig_output_slot)
+ in
+ let fold_obj fns =
+ htab_map fns (fun i (tsig, taux) -> (i, (fold_sig tsig, taux)))
+ in
+ match ty with
+ Ast.TY_any -> f.ty_fold_any ()
+ | Ast.TY_nil -> f.ty_fold_nil ()
+ | Ast.TY_bool -> f.ty_fold_bool ()
+ | Ast.TY_mach m -> f.ty_fold_mach m
+ | Ast.TY_int -> f.ty_fold_int ()
+ | Ast.TY_uint -> f.ty_fold_uint ()
+ | Ast.TY_char -> f.ty_fold_char ()
+ | Ast.TY_str -> f.ty_fold_str ()
+
+ | Ast.TY_tup t -> f.ty_fold_tup (fold_slots t)
+ | Ast.TY_vec s -> f.ty_fold_vec (fold_slot s)
+ | Ast.TY_rec r -> f.ty_fold_rec (Array.map (fun (k,v) -> (k,fold_slot v)) r)
+
+ | Ast.TY_tag tt -> f.ty_fold_tag (fold_tags tt)
+ | Ast.TY_iso ti -> f.ty_fold_iso (ti.Ast.iso_index,
+ (Array.map fold_tags ti.Ast.iso_group))
+ | Ast.TY_idx i -> f.ty_fold_idx i
+
+ | Ast.TY_fn (tsig,taux) -> f.ty_fold_fn (fold_sig tsig, taux)
+ | Ast.TY_chan t -> f.ty_fold_chan (fold_ty f t)
+ | Ast.TY_port t -> f.ty_fold_port (fold_ty f t)
+
+ | Ast.TY_obj (eff,t) -> f.ty_fold_obj (eff, (fold_obj t))
+ | Ast.TY_task -> f.ty_fold_task ()
+
+ | Ast.TY_native x -> f.ty_fold_native x
+ | Ast.TY_param x -> f.ty_fold_param x
+ | Ast.TY_named n -> f.ty_fold_named n
+ | Ast.TY_type -> f.ty_fold_type ()
+
+ | Ast.TY_constrained (t, constrs) ->
+ f.ty_fold_constrained (fold_ty f t, constrs)
+
+;;
+
+type 'a simple_ty_fold = ('a, 'a, 'a, 'a) ty_fold
+;;
+
+let ty_fold_default (default:'a) : 'a simple_ty_fold =
+ { ty_fold_slot = (fun _ -> default);
+ ty_fold_slots = (fun _ -> default);
+ ty_fold_tags = (fun _ -> default);
+ ty_fold_any = (fun _ -> default);
+ ty_fold_nil = (fun _ -> default);
+ ty_fold_bool = (fun _ -> default);
+ ty_fold_mach = (fun _ -> default);
+ ty_fold_int = (fun _ -> default);
+ ty_fold_uint = (fun _ -> default);
+ ty_fold_char = (fun _ -> default);
+ ty_fold_str = (fun _ -> default);
+ ty_fold_tup = (fun _ -> default);
+ ty_fold_vec = (fun _ -> default);
+ ty_fold_rec = (fun _ -> default);
+ ty_fold_tag = (fun _ -> default);
+ ty_fold_iso = (fun _ -> default);
+ ty_fold_idx = (fun _ -> default);
+ ty_fold_fn = (fun _ -> default);
+ ty_fold_obj = (fun _ -> default);
+ ty_fold_chan = (fun _ -> default);
+ ty_fold_port = (fun _ -> default);
+ ty_fold_task = (fun _ -> default);
+ ty_fold_native = (fun _ -> default);
+ ty_fold_param = (fun _ -> default);
+ ty_fold_named = (fun _ -> default);
+ ty_fold_type = (fun _ -> default);
+ ty_fold_constrained = (fun _ -> default) }
+;;
+
+let ty_fold_rebuild (id:Ast.ty -> Ast.ty)
+ : (Ast.ty, Ast.slot, Ast.slot array, Ast.ty_tag) ty_fold =
+ let rebuild_fn ((islots, constrs, oslot), aux) =
+ ({ Ast.sig_input_slots = islots;
+ Ast.sig_input_constrs = constrs;
+ Ast.sig_output_slot = oslot }, aux)
+ in
+ { ty_fold_slot = (fun (mode, mut, t) ->
+ { Ast.slot_mode = mode;
+ Ast.slot_mutable = mut;
+ Ast.slot_ty = Some t });
+ ty_fold_slots = (fun slots -> slots);
+ ty_fold_tags = (fun htab -> htab);
+ ty_fold_any = (fun _ -> id Ast.TY_any);
+ ty_fold_nil = (fun _ -> id Ast.TY_nil);
+ ty_fold_bool = (fun _ -> id Ast.TY_bool);
+ ty_fold_mach = (fun m -> id (Ast.TY_mach m));
+ ty_fold_int = (fun _ -> id Ast.TY_int);
+ ty_fold_uint = (fun _ -> id Ast.TY_uint);
+ ty_fold_char = (fun _ -> id Ast.TY_char);
+ ty_fold_str = (fun _ -> id Ast.TY_str);
+ ty_fold_tup = (fun slots -> id (Ast.TY_tup slots));
+ ty_fold_vec = (fun slot -> id (Ast.TY_vec slot));
+ ty_fold_rec = (fun entries -> id (Ast.TY_rec entries));
+ ty_fold_tag = (fun tag -> id (Ast.TY_tag tag));
+ ty_fold_iso = (fun (i, tags) -> id (Ast.TY_iso { Ast.iso_index = i;
+ Ast.iso_group = tags }));
+ ty_fold_idx = (fun i -> id (Ast.TY_idx i));
+ ty_fold_fn = (fun t -> id (Ast.TY_fn (rebuild_fn t)));
+ ty_fold_obj = (fun (eff,fns) ->
+ id (Ast.TY_obj
+ (eff, (htab_map fns
+ (fun id fn -> (id, rebuild_fn fn))))));
+ ty_fold_chan = (fun t -> id (Ast.TY_chan t));
+ ty_fold_port = (fun t -> id (Ast.TY_port t));
+ ty_fold_task = (fun _ -> id Ast.TY_task);
+ ty_fold_native = (fun oid -> id (Ast.TY_native oid));
+ ty_fold_param = (fun (i, mut) -> id (Ast.TY_param (i, mut)));
+ ty_fold_named = (fun n -> id (Ast.TY_named n));
+ ty_fold_type = (fun _ -> id (Ast.TY_type));
+ ty_fold_constrained = (fun (t, constrs) ->
+ id (Ast.TY_constrained (t, constrs))) }
+;;
+
+let rebuild_ty_under_params
+ (ty:Ast.ty)
+ (params:Ast.ty_param array)
+ (args:Ast.ty array)
+ (resolve_names:bool)
+ : Ast.ty =
+ if (Array.length params) <> (Array.length args)
+ then err None "mismatched type-params"
+ else
+ let nmap = Hashtbl.create (Array.length args) in
+ let pmap = Hashtbl.create (Array.length args) in
+ let _ =
+ Array.iteri
+ begin
+ fun i (ident, param) ->
+ htab_put pmap (Ast.TY_param param) args.(i);
+ if resolve_names
+ then
+ htab_put nmap ident args.(i)
+ end
+ params
+ in
+ let substituted = ref false in
+ let rec rebuild_ty t =
+ let base = ty_fold_rebuild (fun t -> t) in
+ let ty_fold_param (i, mut) =
+ let param = Ast.TY_param (i, mut) in
+ match htab_search pmap param with
+ None -> param
+ | Some arg -> (substituted := true; arg)
+ in
+ let ty_fold_named n =
+ let rec rebuild_name n =
+ match n with
+ Ast.NAME_base nb ->
+ Ast.NAME_base (rebuild_name_base nb)
+ | Ast.NAME_ext (n, nc) ->
+ Ast.NAME_ext (rebuild_name n,
+ rebuild_name_component nc)
+
+ and rebuild_name_base nb =
+ match nb with
+ Ast.BASE_ident i ->
+ Ast.BASE_ident i
+ | Ast.BASE_temp t ->
+ Ast.BASE_temp t
+ | Ast.BASE_app (i, tys) ->
+ Ast.BASE_app (i, rebuild_tys tys)
+
+ and rebuild_name_component nc =
+ match nc with
+ Ast.COMP_ident i ->
+ Ast.COMP_ident i
+ | Ast.COMP_app (i, tys) ->
+ Ast.COMP_app (i, rebuild_tys tys)
+ | Ast.COMP_idx i ->
+ Ast.COMP_idx i
+
+ and rebuild_tys tys =
+ Array.map (fun t -> rebuild_ty t) tys
+ in
+ let n = rebuild_name n in
+ match n with
+ Ast.NAME_base (Ast.BASE_ident id)
+ when resolve_names ->
+ begin
+ match htab_search nmap id with
+ None -> Ast.TY_named n
+ | Some arg -> (substituted := true; arg)
+ end
+ | _ -> Ast.TY_named n
+ in
+ let fold =
+ { base with
+ ty_fold_param = ty_fold_param;
+ ty_fold_named = ty_fold_named;
+ }
+ in
+ let t' = fold_ty fold t in
+ (*
+ * FIXME: "substituted" and "ty'" here are only required
+ * because the current type-equality-comparison code in Type
+ * uses <> and will judge some cases, such as rebuilt tags, as
+ * unequal simply due to the different hashtable order in the
+ * fold.
+ *)
+ if !substituted
+ then t'
+ else t
+ in
+ rebuild_ty ty
+;;
+
+let associative_binary_op_ty_fold
+ (default:'a)
+ (fn:'a -> 'a -> 'a)
+ : 'a simple_ty_fold =
+ let base = ty_fold_default default in
+ let reduce ls =
+ match ls with
+ [] -> default
+ | x::xs -> List.fold_left fn x xs
+ in
+ let reduce_fn ((islots, _, oslot), _) =
+ fn islots oslot
+ in
+ { base with
+ ty_fold_slots = (fun slots -> reduce (Array.to_list slots));
+ ty_fold_slot = (fun (_, _, a) -> a);
+ ty_fold_tags = (fun tab -> reduce (htab_vals tab));
+ ty_fold_tup = (fun a -> a);
+ ty_fold_vec = (fun a -> a);
+ ty_fold_rec = (fun sz ->
+ reduce (Array.to_list
+ (Array.map (fun (_, s) -> s) sz)));
+ ty_fold_tag = (fun a -> a);
+ ty_fold_iso = (fun (_,iso) -> reduce (Array.to_list iso));
+ ty_fold_fn = reduce_fn;
+ ty_fold_obj = (fun (_,fns) ->
+ reduce (List.map reduce_fn (htab_vals fns)));
+ ty_fold_chan = (fun a -> a);
+ ty_fold_port = (fun a -> a);
+ ty_fold_constrained = (fun (a, _) -> a) }
+
+let ty_fold_bool_and (default:bool) : bool simple_ty_fold =
+ associative_binary_op_ty_fold default (fun a b -> a & b)
+;;
+
+let ty_fold_bool_or (default:bool) : bool simple_ty_fold =
+ associative_binary_op_ty_fold default (fun a b -> a || b)
+;;
+
+let ty_fold_int_max (default:int) : int simple_ty_fold =
+ associative_binary_op_ty_fold default (fun a b -> max a b)
+;;
+
+let ty_fold_list_concat _ : ('a list) simple_ty_fold =
+ associative_binary_op_ty_fold [] (fun a b -> a @ b)
+;;
+
+let type_is_structured (t:Ast.ty) : bool =
+ let fold = ty_fold_bool_or false in
+ let fold = { fold with
+ ty_fold_tup = (fun _ -> true);
+ ty_fold_vec = (fun _ -> true);
+ ty_fold_rec = (fun _ -> true);
+ ty_fold_tag = (fun _ -> true);
+ ty_fold_iso = (fun _ -> true);
+ ty_fold_idx = (fun _ -> true);
+ ty_fold_fn = (fun _ -> true);
+ ty_fold_obj = (fun _ -> true) }
+ in
+ fold_ty fold t
+;;
+
+(* Effect analysis. *)
+let effect_le x y =
+ match (x,y) with
+ (Ast.UNSAFE, _) -> true
+ | (Ast.STATE, Ast.PURE) -> true
+ | (Ast.STATE, Ast.IO) -> true
+ | (Ast.STATE, Ast.STATE) -> true
+ | (Ast.IO, Ast.PURE) -> true
+ | (Ast.IO, Ast.IO) -> true
+ | (Ast.PURE, Ast.PURE) -> true
+ | _ -> false
+;;
+
+let lower_effect_of x y =
+ if effect_le x y then x else y
+;;
+
+let type_effect (t:Ast.ty) : Ast.effect =
+ let fold_slot ((*mode*)_, mut, eff) =
+ if mut
+ then lower_effect_of Ast.STATE eff
+ else eff
+ in
+ let fold = associative_binary_op_ty_fold Ast.PURE lower_effect_of in
+ let fold = { fold with ty_fold_slot = fold_slot } in
+ fold_ty fold t
+;;
+
+let type_has_state (t:Ast.ty) : bool =
+ effect_le (type_effect t) Ast.STATE
+;;
+
+
+(* Various type analyses. *)
+
+let is_prim_type (t:Ast.ty) : bool =
+ match t with
+ Ast.TY_int
+ | Ast.TY_uint
+ | Ast.TY_char
+ | Ast.TY_mach _
+ | Ast.TY_bool -> true
+ | _ -> false
+;;
+
+let type_contains_chan (t:Ast.ty) : bool =
+ let fold_chan _ = true in
+ let fold = ty_fold_bool_or false in
+ let fold = { fold with ty_fold_chan = fold_chan } in
+ fold_ty fold t
+;;
+
+
+let type_is_unsigned_2s_complement t =
+ match t with
+ Ast.TY_mach TY_u8
+ | Ast.TY_mach TY_u16
+ | Ast.TY_mach TY_u32
+ | Ast.TY_mach TY_u64
+ | Ast.TY_char
+ | Ast.TY_uint
+ | Ast.TY_bool -> true
+ | _ -> false
+;;
+
+
+let type_is_signed_2s_complement t =
+ match t with
+ Ast.TY_mach TY_i8
+ | Ast.TY_mach TY_i16
+ | Ast.TY_mach TY_i32
+ | Ast.TY_mach TY_i64
+ | Ast.TY_int -> true
+ | _ -> false
+;;
+
+
+let type_is_2s_complement t =
+ (type_is_unsigned_2s_complement t)
+ || (type_is_signed_2s_complement t)
+;;
+
+let n_used_type_params t =
+ let fold_param (i,_) = i+1 in
+ let fold = ty_fold_int_max 0 in
+ let fold = { fold with ty_fold_param = fold_param } in
+ fold_ty fold t
+;;
+
+
+
+let check_concrete params thing =
+ if Array.length params = 0
+ then thing
+ else bug () "unhandled parametric binding"
+;;
+
+
+let project_type_to_slot
+ (base_ty:Ast.ty)
+ (comp:Ast.lval_component)
+ : Ast.slot =
+ match (base_ty, comp) with
+ (Ast.TY_rec elts, Ast.COMP_named (Ast.COMP_ident id)) ->
+ begin
+ match atab_search elts id with
+ Some slot -> slot
+ | None -> err None "unknown record-member '%s'" id
+ end
+
+ | (Ast.TY_tup elts, Ast.COMP_named (Ast.COMP_idx i)) ->
+ if 0 <= i && i < (Array.length elts)
+ then elts.(i)
+ else err None "out-of-range tuple index %d" i
+
+ | (Ast.TY_vec slot, Ast.COMP_atom _) ->
+ slot
+
+ | (Ast.TY_str, Ast.COMP_atom _) ->
+ interior_slot (Ast.TY_mach TY_u8)
+
+ | (Ast.TY_obj (_, fns), Ast.COMP_named (Ast.COMP_ident id)) ->
+ interior_slot (Ast.TY_fn (Hashtbl.find fns id))
+
+ | (_,_) ->
+ bug ()
+ "unhandled form of lval-ext in Semant."
+ "project_slot: %a indexed by %a"
+ Ast.sprintf_ty base_ty Ast.sprintf_lval_component comp
+;;
+
+
+(* NB: this will fail if lval is not a slot. *)
+let rec lval_slot (cx:ctxt) (lval:Ast.lval) : Ast.slot =
+ match lval with
+ Ast.LVAL_base nb -> lval_to_slot cx nb.id
+ | Ast.LVAL_ext (base, comp) ->
+ let base_ty = slot_ty (lval_slot cx base) in
+ project_type_to_slot base_ty comp
+;;
+
+let exports_permit (view:Ast.mod_view) (ident:Ast.ident) : bool =
+ (Hashtbl.mem view.Ast.view_exports Ast.EXPORT_all_decls) ||
+ (Hashtbl.mem view.Ast.view_exports (Ast.EXPORT_ident ident))
+;;
+
+(* NB: this will fail if lval is not an item. *)
+let rec lval_item (cx:ctxt) (lval:Ast.lval) : Ast.mod_item =
+ match lval with
+ Ast.LVAL_base nb ->
+ begin
+ let referent = lval_to_referent cx nb.id in
+ match htab_search cx.ctxt_all_defns referent with
+ Some (DEFN_item item) -> {node=item; id=referent}
+ | _ -> err (Some (lval_base_id lval))
+ "lval does not name an item"
+ end
+ | Ast.LVAL_ext (base, comp) ->
+ let base_item = lval_item cx base in
+ match base_item.node.Ast.decl_item with
+ Ast.MOD_ITEM_mod (view, items) ->
+ begin
+ let i, args =
+ match comp with
+ Ast.COMP_named (Ast.COMP_ident i) -> (i, [||])
+ | Ast.COMP_named (Ast.COMP_app (i, args)) -> (i, args)
+ | _ ->
+ bug ()
+ "unhandled lval-component '%a' in Semant.lval_item"
+ Ast.sprintf_lval_component comp
+ in
+ match htab_search items i with
+ | Some sub when exports_permit view i ->
+ assert
+ ((Array.length sub.node.Ast.decl_params) =
+ (Array.length args));
+ check_concrete base_item.node.Ast.decl_params sub
+ | _ -> err (Some (lval_base_id lval))
+ "unknown module item '%s'" i
+ end
+ | _ -> err (Some (lval_base_id lval))
+ "lval base %a does not name a module" Ast.sprintf_lval base
+;;
+
+let lval_is_slot (cx:ctxt) (lval:Ast.lval) : bool =
+ match resolve_lval cx lval with
+ DEFN_slot _ -> true
+ | _ -> false
+;;
+
+let lval_is_item (cx:ctxt) (lval:Ast.lval) : bool =
+ match resolve_lval cx lval with
+ DEFN_item _ -> true
+ | _ -> false
+;;
+
+let lval_is_direct_fn (cx:ctxt) (lval:Ast.lval) : bool =
+ let defn = resolve_lval cx lval in
+ (defn_is_static defn) && (defn_is_callable defn)
+;;
+
+let lval_is_direct_mod (cx:ctxt) (lval:Ast.lval) : bool =
+ let defn = resolve_lval cx lval in
+ if not (defn_is_static defn)
+ then false
+ else
+ match defn with
+ DEFN_item { Ast.decl_item = Ast.MOD_ITEM_mod _ } -> true
+ | _ -> false
+;;
+
+let lval_is_static (cx:ctxt) (lval:Ast.lval) : bool =
+ defn_is_static (resolve_lval cx lval)
+;;
+
+let lval_is_callable (cx:ctxt) (lval:Ast.lval) : bool =
+ defn_is_callable (resolve_lval cx lval)
+;;
+
+let lval_is_obj_vtbl (cx:ctxt) (lval:Ast.lval) : bool =
+ if lval_is_slot cx lval
+ then
+ match lval with
+ Ast.LVAL_ext (base, _) ->
+ begin
+ match slot_ty (lval_slot cx base) with
+ Ast.TY_obj _ -> true
+ | _ -> false
+ end
+ | _ -> false
+ else false
+;;
+
+let rec lval_ty (cx:ctxt) (lval:Ast.lval) : Ast.ty =
+ let base_id = lval_base_id lval in
+ Hashtbl.find cx.ctxt_all_lval_types base_id
+;;
+
+let rec atom_type (cx:ctxt) (at:Ast.atom) : Ast.ty =
+ match at with
+ Ast.ATOM_literal {node=(Ast.LIT_int _); id=_} -> Ast.TY_int
+ | Ast.ATOM_literal {node=(Ast.LIT_uint _); id=_} -> Ast.TY_uint
+ | Ast.ATOM_literal {node=(Ast.LIT_bool _); id=_} -> Ast.TY_bool
+ | Ast.ATOM_literal {node=(Ast.LIT_char _); id=_} -> Ast.TY_char
+ | Ast.ATOM_literal {node=(Ast.LIT_nil); id=_} -> Ast.TY_nil
+ | Ast.ATOM_literal {node=(Ast.LIT_mach (m,_,_)); id=_} -> Ast.TY_mach m
+ | Ast.ATOM_lval lv -> lval_ty cx lv
+;;
+
+let expr_type (cx:ctxt) (e:Ast.expr) : Ast.ty =
+ match e with
+ Ast.EXPR_binary (op, a, _) ->
+ begin
+ match op with
+ Ast.BINOP_eq | Ast.BINOP_ne | Ast.BINOP_lt | Ast.BINOP_le
+ | Ast.BINOP_ge | Ast.BINOP_gt -> Ast.TY_bool
+ | _ -> atom_type cx a
+ end
+ | Ast.EXPR_unary (Ast.UNOP_not, _) -> Ast.TY_bool
+ | Ast.EXPR_unary (_, a) -> atom_type cx a
+ | Ast.EXPR_atom a -> atom_type cx a
+;;
+
+(* Mappings between mod items and their respective types. *)
+
+let arg_slots (slots:Ast.header_slots) : Ast.slot array =
+ Array.map (fun (sid,_) -> sid.node) slots
+;;
+
+let tup_slots (slots:Ast.header_tup) : Ast.slot array =
+ Array.map (fun sid -> sid.node) slots
+;;
+
+let ty_fn_of_fn (fn:Ast.fn) : Ast.ty_fn =
+ ({ Ast.sig_input_slots = arg_slots fn.Ast.fn_input_slots;
+ Ast.sig_input_constrs = fn.Ast.fn_input_constrs;
+ Ast.sig_output_slot = fn.Ast.fn_output_slot.node },
+ fn.Ast.fn_aux )
+;;
+
+let ty_obj_of_obj (obj:Ast.obj) : Ast.ty_obj =
+ (obj.Ast.obj_effect,
+ htab_map obj.Ast.obj_fns (fun i f -> (i, ty_fn_of_fn f.node)))
+;;
+
+let ty_of_mod_item ((*inside*)_:bool) (item:Ast.mod_item) : Ast.ty =
+ match item.node.Ast.decl_item with
+ Ast.MOD_ITEM_type _ -> Ast.TY_type
+ | Ast.MOD_ITEM_fn f -> (Ast.TY_fn (ty_fn_of_fn f))
+ | Ast.MOD_ITEM_mod _ -> bug () "Semant.ty_of_mod_item on mod"
+ | Ast.MOD_ITEM_obj ob ->
+ let taux = { Ast.fn_effect = Ast.PURE;
+ Ast.fn_is_iter = false }
+ in
+ let tobj = Ast.TY_obj (ty_obj_of_obj ob) in
+ let tsig = { Ast.sig_input_slots = arg_slots ob.Ast.obj_state;
+ Ast.sig_input_constrs = ob.Ast.obj_constrs;
+ Ast.sig_output_slot = interior_slot tobj }
+ in
+ (Ast.TY_fn (tsig, taux))
+
+ | Ast.MOD_ITEM_tag (htup, ttag, _) ->
+ let taux = { Ast.fn_effect = Ast.PURE;
+ Ast.fn_is_iter = false }
+ in
+ let tsig = { Ast.sig_input_slots = tup_slots htup;
+ Ast.sig_input_constrs = [| |];
+ Ast.sig_output_slot = interior_slot (Ast.TY_tag ttag) }
+ in
+ (Ast.TY_fn (tsig, taux))
+;;
+
+(* Scopes and the visitor that builds them. *)
+
+type scope =
+ SCOPE_block of node_id
+ | SCOPE_mod_item of Ast.mod_item
+ | SCOPE_obj_fn of (Ast.fn identified)
+ | SCOPE_crate of Ast.crate
+;;
+
+let id_of_scope (sco:scope) : node_id =
+ match sco with
+ SCOPE_block id -> id
+ | SCOPE_mod_item i -> i.id
+ | SCOPE_obj_fn f -> f.id
+ | SCOPE_crate c -> c.id
+;;
+
+let scope_stack_managing_visitor
+ (scopes:(scope list) ref)
+ (inner:Walk.visitor)
+ : Walk.visitor =
+ let push s =
+ scopes := s :: (!scopes)
+ in
+ let pop _ =
+ scopes := List.tl (!scopes)
+ in
+ let visit_block_pre b =
+ push (SCOPE_block b.id);
+ inner.Walk.visit_block_pre b
+ in
+ let visit_block_post b =
+ inner.Walk.visit_block_post b;
+ pop();
+ in
+ let visit_mod_item_pre n p i =
+ push (SCOPE_mod_item i);
+ inner.Walk.visit_mod_item_pre n p i
+ in
+ let visit_mod_item_post n p i =
+ inner.Walk.visit_mod_item_post n p i;
+ pop();
+ in
+ let visit_obj_fn_pre obj ident fn =
+ push (SCOPE_obj_fn fn);
+ inner.Walk.visit_obj_fn_pre obj ident fn
+ in
+ let visit_obj_fn_post obj ident fn =
+ inner.Walk.visit_obj_fn_post obj ident fn;
+ pop();
+ in
+ let visit_crate_pre c =
+ push (SCOPE_crate c);
+ inner.Walk.visit_crate_pre c
+ in
+ let visit_crate_post c =
+ inner.Walk.visit_crate_post c;
+ pop()
+ in
+ { inner with
+ Walk.visit_block_pre = visit_block_pre;
+ Walk.visit_block_post = visit_block_post;
+ Walk.visit_mod_item_pre = visit_mod_item_pre;
+ Walk.visit_mod_item_post = visit_mod_item_post;
+ Walk.visit_obj_fn_pre = visit_obj_fn_pre;
+ Walk.visit_obj_fn_post = visit_obj_fn_post;
+ Walk.visit_crate_pre = visit_crate_pre;
+ Walk.visit_crate_post = visit_crate_post; }
+;;
+
+(* Generic lookup, used for slots, items, types, etc. *)
+
+type resolved = ((scope list * node_id) option) ;;
+
+let get_item (cx:ctxt) (node:node_id) : Ast.mod_item_decl =
+ match htab_search cx.ctxt_all_defns node with
+ Some (DEFN_item item) -> item
+ | Some _ -> err (Some node) "defn is not an item"
+ | None -> bug () "missing defn"
+;;
+
+let get_slot (cx:ctxt) (node:node_id) : Ast.slot =
+ match htab_search cx.ctxt_all_defns node with
+ Some (DEFN_slot slot) -> slot
+ | Some _ -> err (Some node) "defn is not a slot"
+ | None -> bug () "missing defn"
+;;
+
+let get_mod_item
+ (cx:ctxt)
+ (node:node_id)
+ : (Ast.mod_view * Ast.mod_items) =
+ match get_item cx node with
+ { Ast.decl_item = Ast.MOD_ITEM_mod md } -> md
+ | _ -> err (Some node) "defn is not a mod"
+;;
+
+let get_name_comp_ident
+ (comp:Ast.name_component)
+ : Ast.ident =
+ match comp with
+ Ast.COMP_ident i -> i
+ | Ast.COMP_app (i, _) -> i
+ | Ast.COMP_idx i -> string_of_int i
+;;
+
+let get_name_base_ident
+ (comp:Ast.name_base)
+ : Ast.ident =
+ match comp with
+ Ast.BASE_ident i -> i
+ | Ast.BASE_app (i, _) -> i
+ | Ast.BASE_temp _ ->
+ bug () "get_name_base_ident on BASE_temp"
+;;
+
+let rec project_ident_from_items
+ (cx:ctxt)
+ (scopes:scope list)
+ ((view:Ast.mod_view),(items:Ast.mod_items))
+ (ident:Ast.ident)
+ (inside:bool)
+ : resolved =
+ if not (inside || (exports_permit view ident))
+ then None
+ else
+ match htab_search items ident with
+ Some i -> Some (scopes, i.id)
+ | None ->
+ match htab_search view.Ast.view_imports ident with
+ None -> None
+ | Some name -> lookup_by_name cx scopes name
+
+and project_name_comp_from_resolved
+ (cx:ctxt)
+ (mod_res:resolved)
+ (ext:Ast.name_component)
+ : resolved =
+ match mod_res with
+ None -> None
+ | Some (scopes, id) ->
+ let scope = (SCOPE_mod_item {id=id; node=get_item cx id}) in
+ let scopes = scope :: scopes in
+ let ident = get_name_comp_ident ext in
+ let md = get_mod_item cx id in
+ project_ident_from_items cx scopes md ident false
+
+and lookup_by_name
+ (cx:ctxt)
+ (scopes:scope list)
+ (name:Ast.name)
+ : resolved =
+ assert (Ast.sane_name name);
+ match name with
+ Ast.NAME_base nb ->
+ let ident = get_name_base_ident nb in
+ lookup_by_ident cx scopes ident
+ | Ast.NAME_ext (name, ext) ->
+ let base_res = lookup_by_name cx scopes name in
+ project_name_comp_from_resolved cx base_res ext
+
+and lookup_by_ident
+ (cx:ctxt)
+ (scopes:scope list)
+ (ident:Ast.ident)
+ : resolved =
+ let check_slots scopes islots =
+ arr_search islots
+ (fun _ (sloti,ident') ->
+ if ident = ident'
+ then Some (scopes, sloti.id)
+ else None)
+ in
+ let check_params scopes params =
+ arr_search params
+ (fun _ {node=(i,_); id=id} ->
+ if i = ident then Some (scopes, id) else None)
+ in
+ let passed_capture_scope = ref false in
+ let would_capture r =
+ match r with
+ None -> None
+ | Some _ ->
+ if !passed_capture_scope
+ then err None "attempted dynamic environment-capture"
+ else r
+ in
+ let check_scope scopes scope =
+ match scope with
+ SCOPE_block block_id ->
+ let block_slots = Hashtbl.find cx.ctxt_block_slots block_id in
+ let block_items = Hashtbl.find cx.ctxt_block_items block_id in
+ begin
+ match htab_search block_slots (Ast.KEY_ident ident) with
+ Some id -> would_capture (Some (scopes, id))
+ | None ->
+ match htab_search block_items ident with
+ Some id -> Some (scopes, id)
+ | None -> None
+ end
+
+ | SCOPE_crate crate ->
+ project_ident_from_items
+ cx scopes crate.node.Ast.crate_items ident true
+
+ | SCOPE_obj_fn fn ->
+ would_capture (check_slots scopes fn.node.Ast.fn_input_slots)
+
+ | SCOPE_mod_item item ->
+ begin
+ let item_match =
+ match item.node.Ast.decl_item with
+ Ast.MOD_ITEM_fn f ->
+ check_slots scopes f.Ast.fn_input_slots
+
+ | Ast.MOD_ITEM_obj obj ->
+ begin
+ match htab_search obj.Ast.obj_fns ident with
+ Some fn -> Some (scopes, fn.id)
+ | None -> check_slots scopes obj.Ast.obj_state
+ end
+
+ | Ast.MOD_ITEM_mod md ->
+ project_ident_from_items cx scopes md ident true
+
+ | _ -> None
+ in
+ match item_match with
+ Some _ -> item_match
+ | None ->
+ would_capture
+ (check_params scopes item.node.Ast.decl_params)
+ end
+ in
+ let rec search scopes =
+ match scopes with
+ [] -> None
+ | scope::rest ->
+ match check_scope scopes scope with
+ None ->
+ begin
+ let is_ty_item i =
+ match i.node.Ast.decl_item with
+ Ast.MOD_ITEM_type _ -> true
+ | _ -> false
+ in
+ match scope with
+ SCOPE_block _
+ | SCOPE_obj_fn _ ->
+ search rest
+
+ | SCOPE_mod_item item when is_ty_item item ->
+ search rest
+
+ | _ ->
+ passed_capture_scope := true;
+ search rest
+ end
+ | x -> x
+ in
+ search scopes
+;;
+
+let lookup_by_temp
+ (cx:ctxt)
+ (scopes:scope list)
+ (temp:temp_id)
+ : ((scope list * node_id) option) =
+ let passed_item_scope = ref false in
+ let check_scope scope =
+ if !passed_item_scope
+ then None
+ else
+ match scope with
+ SCOPE_block block_id ->
+ let block_slots = Hashtbl.find cx.ctxt_block_slots block_id in
+ htab_search block_slots (Ast.KEY_temp temp)
+ | _ ->
+ passed_item_scope := true;
+ None
+ in
+ list_search_ctxt scopes check_scope
+;;
+
+let lookup
+ (cx:ctxt)
+ (scopes:scope list)
+ (key:Ast.slot_key)
+ : ((scope list * node_id) option) =
+ match key with
+ Ast.KEY_temp temp -> lookup_by_temp cx scopes temp
+ | Ast.KEY_ident ident -> lookup_by_ident cx scopes ident
+;;
+
+
+let run_passes
+ (cx:ctxt)
+ (name:string)
+ (path:Ast.name_component Stack.t)
+ (passes:Walk.visitor array)
+ (log:string->unit)
+ (crate:Ast.crate)
+ : unit =
+ let do_pass i pass =
+ let logger s = log (Printf.sprintf "pass %d: %s" i s) in
+ Walk.walk_crate
+ (Walk.path_managing_visitor path
+ (Walk.mod_item_logging_visitor logger path pass))
+ crate
+ in
+ let sess = cx.ctxt_sess in
+ if sess.Session.sess_failed
+ then ()
+ else
+ try
+ Session.time_inner name sess
+ (fun _ -> Array.iteri do_pass passes)
+ with
+ Semant_err (ido, str) -> report_err cx ido str
+;;
+
+(* Rust type -> IL type conversion. *)
+
+let word_sty (abi:Abi.abi) : Il.scalar_ty =
+ Il.ValTy abi.Abi.abi_word_bits
+;;
+
+let word_rty (abi:Abi.abi) : Il.referent_ty =
+ Il.ScalarTy (word_sty abi)
+;;
+
+let tydesc_rty (abi:Abi.abi) : 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 *)
+ 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 *)
+ Il.ScalarTy (Il.AddrTy Il.CodeTy); (* Abi.tydesc_field_mark_glue *)
+ Il.ScalarTy (Il.AddrTy Il.CodeTy); (* Abi.tydesc_field_obj_drop_glue *)
+ |]
+;;
+
+let obj_closure_rty (abi:Abi.abi) : Il.referent_ty =
+ Il.StructTy [| word_rty abi;
+ Il.ScalarTy (Il.AddrTy (tydesc_rty abi));
+ word_rty abi (* 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 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 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 (slot_referent_type abi) ttup) in
+ let tag ttag =
+ let union =
+ Il.UnionTy
+ (Array.map
+ (fun key -> tup (Hashtbl.find ttag key))
+ (sorted_htab_keys ttag))
+ in
+ let discriminant = word in
+ Il.StructTy [| discriminant; union |]
+ in
+
+ match t with
+ Ast.TY_any -> Il.StructTy [| word; ptr |]
+ | Ast.TY_nil -> Il.NilTy
+ | Ast.TY_int
+ | Ast.TY_uint -> word
+
+ | Ast.TY_bool -> sv Il.Bits8
+
+ | Ast.TY_mach (TY_u8)
+ | Ast.TY_mach (TY_i8) -> sv Il.Bits8
+
+ | Ast.TY_mach (TY_u16)
+ | Ast.TY_mach (TY_i16) -> sv Il.Bits16
+
+ | Ast.TY_mach (TY_u32)
+ | Ast.TY_mach (TY_i32)
+ | Ast.TY_mach (TY_f32)
+ | Ast.TY_char -> sv Il.Bits32
+
+ | Ast.TY_mach (TY_u64)
+ | Ast.TY_mach (TY_i64)
+ | Ast.TY_mach (TY_f64) -> sv Il.Bits64
+
+ | Ast.TY_str -> sp (Il.StructTy [| word; word; word; ptr |])
+ | Ast.TY_vec _ -> sp (Il.StructTy [| word; word; word; ptr |])
+ | Ast.TY_tup tt -> tup tt
+ | Ast.TY_rec tr -> tup (Array.map snd tr)
+
+ | Ast.TY_fn _ ->
+ let fn_closure_ptr = sp (Il.StructTy [| word; Il.OpaqueTy |]) in
+ Il.StructTy [| codeptr; fn_closure_ptr |]
+
+ | Ast.TY_obj _ ->
+ let obj_closure_ptr = sp (obj_closure_rty abi) in
+ Il.StructTy [| ptr; obj_closure_ptr |]
+
+ | Ast.TY_tag ttag -> tag ttag
+ | Ast.TY_iso tiso -> tag tiso.Ast.iso_group.(tiso.Ast.iso_index)
+
+ | Ast.TY_idx _ -> word (* A lie, but permits GEP'ing to it. *)
+
+ | Ast.TY_chan _
+ | Ast.TY_port _
+ | Ast.TY_task -> rc_ptr
+
+ | Ast.TY_type -> sp (tydesc_rty abi)
+
+ | Ast.TY_native _ -> ptr
+
+ | Ast.TY_param (i, _) -> Il.ParamTy i
+
+ | Ast.TY_named _ -> bug () "named type in referent_type"
+ | Ast.TY_constrained (t, _) -> referent_type abi t
+
+and slot_referent_type (abi:Abi.abi) (sl:Ast.slot) : 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 = sv abi.Abi.abi_word_bits in
+
+ let rty = referent_type abi (slot_ty sl) in
+ match sl.Ast.slot_mode with
+ Ast.MODE_exterior _ -> sp (Il.StructTy [| word; rty |])
+ | Ast.MODE_interior _ -> rty
+ | Ast.MODE_alias _ -> sp rty
+;;
+
+let task_rty (abi:Abi.abi) : Il.referent_ty =
+ Il.StructTy
+ begin
+ Array.init
+ Abi.n_visible_task_fields
+ (fun _ -> word_rty abi)
+ end
+;;
+
+let call_args_referent_type_full
+ (abi:Abi.abi)
+ (out_slot:Ast.slot)
+ (n_ty_params:int)
+ (in_slots:Ast.slot array)
+ (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_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
+ Il.StructTy (Array.init n_ty_params (fun _ -> td))
+ in
+ let arg_rtys = Il.StructTy (Array.map (slot_referent_type abi) in_slots) in
+ (*
+ * NB: must match corresponding calltup structure in trans and
+ * member indices in ABI exactly.
+ *)
+ Il.StructTy
+ [|
+ out_ptr_rty; (* Abi.calltup_elt_out_ptr *)
+ task_ptr_rty; (* Abi.calltup_elt_task_ptr *)
+ ty_param_rtys; (* Abi.calltup_elt_ty_params *)
+ arg_rtys; (* Abi.calltup_elt_args *)
+ Il.StructTy iterator_arg_rtys; (* Abi.calltup_elt_iterator_args *)
+ Il.StructTy indirect_arg_rtys (* Abi.calltup_elt_indirect_args *)
+ |]
+;;
+
+let call_args_referent_type
+ (cx:ctxt)
+ (n_ty_params:int)
+ (callee_ty:Ast.ty)
+ (closure:Il.referent_ty option)
+ : Il.referent_ty =
+ let indirect_arg_rtys =
+ match closure with
+ None -> [| |]
+ | Some c ->
+ [|
+ (* Abi.indirect_args_elt_closure *)
+ Il.ScalarTy (Il.AddrTy c)
+ |]
+ in
+ let iterator_arg_rtys _ =
+ [|
+ (* Abi.iterator_args_elt_loop_size *)
+ Il.ScalarTy (Il.ValTy cx.ctxt_abi.Abi.abi_word_bits);
+ (* Abi.iterator_args_elt_loop_info_ptr *)
+ Il.ScalarTy (Il.AddrTy Il.OpaqueTy)
+ |]
+ in
+ match callee_ty with
+ Ast.TY_fn (tsig, taux) ->
+ call_args_referent_type_full
+ cx.ctxt_abi
+ tsig.Ast.sig_output_slot
+ n_ty_params
+ tsig.Ast.sig_input_slots
+ (if taux.Ast.fn_is_iter then (iterator_arg_rtys()) else [||])
+ indirect_arg_rtys
+
+ | _ -> bug cx "Semant.call_args_referent_type on non-callable type"
+;;
+
+let indirect_call_args_referent_type
+ (cx:ctxt)
+ (n_ty_params:int)
+ (callee_ty:Ast.ty)
+ (closure:Il.referent_ty)
+ : Il.referent_ty =
+ call_args_referent_type cx n_ty_params callee_ty (Some closure)
+;;
+
+let direct_call_args_referent_type
+ (cx:ctxt)
+ (callee_node:node_id)
+ : Il.referent_ty =
+ let ity = Hashtbl.find cx.ctxt_all_item_types callee_node in
+ let n_ty_params =
+ if item_is_obj_fn cx callee_node
+ then 0
+ else n_item_ty_params cx callee_node
+ in
+ call_args_referent_type cx n_ty_params ity None
+;;
+
+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 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 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 word_slot (abi:Abi.abi) : Ast.slot =
+ interior_slot (Ast.TY_mach abi.Abi.abi_word_ty)
+;;
+
+let read_alias_slot (ty:Ast.ty) : Ast.slot =
+ { Ast.slot_mode = Ast.MODE_alias;
+ Ast.slot_mutable = false;
+ Ast.slot_ty = Some ty }
+;;
+
+let word_write_alias_slot (abi:Abi.abi) : Ast.slot =
+ { Ast.slot_mode = Ast.MODE_alias;
+ Ast.slot_mutable = true;
+ Ast.slot_ty = Some (Ast.TY_mach abi.Abi.abi_word_ty) }
+;;
+
+let mk_ty_fn_or_iter
+ (out_slot:Ast.slot)
+ (arg_slots:Ast.slot array)
+ (is_iter:bool)
+ : Ast.ty =
+ (* In some cases we don't care what aux or constrs are. *)
+ let taux = { Ast.fn_effect = Ast.PURE;
+ Ast.fn_is_iter = is_iter; }
+ in
+ let tsig = { Ast.sig_input_slots = arg_slots;
+ Ast.sig_input_constrs = [| |];
+ Ast.sig_output_slot = out_slot; }
+ in
+ Ast.TY_fn (tsig, taux)
+;;
+
+let mk_ty_fn
+ (out_slot:Ast.slot)
+ (arg_slots:Ast.slot array)
+ : Ast.ty =
+ mk_ty_fn_or_iter out_slot arg_slots false
+;;
+
+let mk_simple_ty_fn
+ (arg_slots:Ast.slot array)
+ : Ast.ty =
+ (* In some cases we don't care what the output slot is. *)
+ let out_slot = interior_slot Ast.TY_nil in
+ mk_ty_fn out_slot arg_slots
+;;
+
+let mk_simple_ty_iter
+ (arg_slots:Ast.slot array)
+ : Ast.ty =
+ (* In some cases we don't care what the output slot is. *)
+ let out_slot = interior_slot Ast.TY_nil in
+ mk_ty_fn_or_iter out_slot arg_slots true
+;;
+
+
+(* name mangling support. *)
+
+let item_name (cx:ctxt) (id:node_id) : Ast.name =
+ Hashtbl.find cx.ctxt_all_item_names id
+;;
+
+let item_str (cx:ctxt) (id:node_id) : string =
+ string_of_name (item_name cx id)
+;;
+
+let ty_str (ty:Ast.ty) : string =
+ let base = associative_binary_op_ty_fold "" (fun a b -> a ^ b) in
+ let fold_slot (mode,mut,ty) =
+ (if mut then "m" else "")
+ ^ (match mode with
+ Ast.MODE_exterior -> "e"
+ | Ast.MODE_alias -> "a"
+ | Ast.MODE_interior -> "")
+ ^ ty
+ in
+ let num n = (string_of_int n) ^ "$" in
+ let len a = num (Array.length a) in
+ let join az = Array.fold_left (fun a b -> a ^ b) "" az in
+ let fold_slots slots =
+ "t"
+ ^ (len slots)
+ ^ (join slots)
+ in
+ let fold_rec entries =
+ "r"
+ ^ (len entries)
+ ^ (Array.fold_left
+ (fun str (ident, s) -> str ^ "$" ^ ident ^ "$" ^ s)
+ "" entries)
+ in
+ let fold_tags tags =
+ "g"
+ ^ (num (Hashtbl.length tags))
+ ^ (Array.fold_left
+ (fun str key -> str ^ (string_of_name key) ^ (Hashtbl.find tags key))
+ "" (sorted_htab_keys tags))
+ in
+ let fold_iso (n, tags) =
+ "G"
+ ^ (num n)
+ ^ (len tags)
+ ^ (join tags)
+ in
+ let fold_mach m =
+ match m with
+ TY_u8 -> "U0"
+ | TY_u16 -> "U1"
+ | TY_u32 -> "U2"
+ | TY_u64 -> "U3"
+ | TY_i8 -> "I0"
+ | TY_i16 -> "I1"
+ | TY_i32 -> "I2"
+ | TY_i64 -> "I3"
+ | TY_f32 -> "F2"
+ | TY_f64 -> "F3"
+ in
+ let fold =
+ { base with
+ (* Structural types. *)
+ ty_fold_slot = fold_slot;
+ ty_fold_slots = fold_slots;
+ ty_fold_tags = fold_tags;
+ ty_fold_rec = fold_rec;
+ ty_fold_nil = (fun _ -> "n");
+ ty_fold_bool = (fun _ -> "b");
+ ty_fold_mach = fold_mach;
+ ty_fold_int = (fun _ -> "i");
+ ty_fold_uint = (fun _ -> "u");
+ ty_fold_char = (fun _ -> "c");
+ ty_fold_obj = (fun _ -> "o");
+ ty_fold_str = (fun _ -> "s");
+ ty_fold_vec = (fun s -> "v" ^ s);
+ ty_fold_iso = fold_iso;
+ ty_fold_idx = (fun i -> "x" ^ (string_of_int i));
+ (* FIXME: encode constrs, aux as well. *)
+ ty_fold_fn = (fun ((ins,_,out),_) -> "f" ^ ins ^ out);
+
+ (* Built-in special types. *)
+ ty_fold_any = (fun _ -> "A");
+ ty_fold_chan = (fun t -> "H" ^ t);
+ ty_fold_port = (fun t -> "R" ^ t);
+ ty_fold_task = (fun _ -> "T");
+ ty_fold_native = (fun _ -> "N");
+ ty_fold_param = (fun _ -> "P");
+ ty_fold_type = (fun _ -> "Y");
+
+ (* FIXME: encode obj types. *)
+ (* FIXME: encode opaque and param numbers. *)
+ ty_fold_named = (fun _ -> bug () "string-encoding named type");
+ (* FIXME: encode constrs as well. *)
+ ty_fold_constrained = (fun (t,_)-> t) }
+ in
+ fold_ty fold ty
+;;
+
+let glue_str (cx:ctxt) (g:glue) : string =
+ match g with
+ GLUE_activate -> "glue$activate"
+ | GLUE_yield -> "glue$yield"
+ | GLUE_exit_main_task -> "glue$exit_main_task"
+ | GLUE_exit_task -> "glue$exit_task"
+ | GLUE_mark ty -> "glue$mark$" ^ (ty_str ty)
+ | GLUE_drop ty -> "glue$drop$" ^ (ty_str ty)
+ | GLUE_free ty -> "glue$free$" ^ (ty_str ty)
+ | GLUE_copy ty -> "glue$copy$" ^ (ty_str ty)
+ | GLUE_clone ty -> "glue$clone$" ^ (ty_str ty)
+ | GLUE_compare ty -> "glue$compare$" ^ (ty_str ty)
+ | GLUE_hash ty -> "glue$hash$" ^ (ty_str ty)
+ | GLUE_write ty -> "glue$write$" ^ (ty_str ty)
+ | GLUE_read ty -> "glue$read$" ^ (ty_str ty)
+ | GLUE_unwind -> "glue$unwind"
+ | GLUE_get_next_pc -> "glue$get_next_pc"
+ | GLUE_mark_frame i -> "glue$mark_frame$" ^ (item_str cx i)
+ | GLUE_drop_frame i -> "glue$drop_frame$" ^ (item_str cx i)
+ | GLUE_reloc_frame i -> "glue$reloc_frame$" ^ (item_str cx i)
+ (*
+ * FIXME: the node_id here isn't an item, it's a statement;
+ * lookup bind target and encode bound arg tuple type.
+ *)
+ | GLUE_fn_binding i
+ -> "glue$fn_binding$" ^ (string_of_int (int_of_node i))
+ | GLUE_obj_drop oid
+ -> (item_str cx oid) ^ ".drop"
+ | GLUE_loop_body i
+ -> "glue$loop_body$" ^ (string_of_int (int_of_node i))
+ | GLUE_forward (id, oty1, oty2)
+ -> "glue$forward$"
+ ^ id
+ ^ "$" ^ (ty_str (Ast.TY_obj oty1))
+ ^ "$" ^ (ty_str (Ast.TY_obj oty2))
+;;
+
+
+(*
+ * Local Variables:
+ * fill-column: 78;
+ * indent-tabs-mode: nil
+ * buffer-file-coding-system: utf-8-unix
+ * compile-command: "make -C ../.. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
+ * End:
+ *)
diff --git a/src/boot/me/trans.ml b/src/boot/me/trans.ml
new file mode 100644
index 00000000..bca15136
--- /dev/null
+++ b/src/boot/me/trans.ml
@@ -0,0 +1,5031 @@
+(* Translation *)
+
+open Semant;;
+open Common;;
+open Transutil;;
+
+let log cx = Session.log "trans"
+ cx.ctxt_sess.Session.sess_log_trans
+ cx.ctxt_sess.Session.sess_log_out
+;;
+
+let arr_max a = (Array.length a) - 1;;
+
+type quad_idx = int
+;;
+
+type call =
+ {
+ call_ctrl: call_ctrl;
+ call_callee_ptr: Il.operand;
+ call_callee_ty: Ast.ty;
+ call_callee_ty_params: Ast.ty array;
+ call_output: Il.cell;
+ call_args: Ast.atom array;
+ call_iterator_args: Il.operand array;
+ call_indirect_args: Il.operand array;
+ }
+;;
+
+let trans_visitor
+ (cx:ctxt)
+ (path:Ast.name_component Stack.t)
+ (inner:Walk.visitor)
+ : Walk.visitor =
+
+ let iflog thunk =
+ if cx.ctxt_sess.Session.sess_log_trans
+ then thunk ()
+ else ()
+ in
+
+ let curr_file = Stack.create () in
+ let curr_stmt = Stack.create () in
+
+ let (abi:Abi.abi) = cx.ctxt_abi in
+ let (word_sz:int64) = word_sz abi in
+ let (word_slot:Ast.slot) = word_slot abi in
+
+ let oper_str = Il.string_of_operand abi.Abi.abi_str_of_hardreg in
+ let cell_str = Il.string_of_cell abi.Abi.abi_str_of_hardreg in
+
+ let (word_bits:Il.bits) = abi.Abi.abi_word_bits in
+ let (word_ty:Il.scalar_ty) = Il.ValTy word_bits in
+ let (word_rty:Il.referent_ty) = Il.ScalarTy word_ty in
+ let (word_ty_mach:ty_mach) =
+ match word_bits with
+ Il.Bits8 -> TY_u8
+ | Il.Bits16 -> TY_u16
+ | Il.Bits32 -> TY_u32
+ | Il.Bits64 -> TY_u64
+ in
+ let (word_ty_signed_mach:ty_mach) =
+ match word_bits with
+ Il.Bits8 -> TY_i8
+ | Il.Bits16 -> TY_i16
+ | Il.Bits32 -> TY_i32
+ | Il.Bits64 -> TY_i64
+ in
+ let word_n = word_n abi in
+ let imm_of_ty (i:int64) (tm:ty_mach) : Il.operand =
+ Il.Imm (Asm.IMM i, tm)
+ in
+
+ let imm (i:int64) : Il.operand = imm_of_ty i word_ty_mach in
+ let simm (i:int64) : Il.operand = imm_of_ty i word_ty_signed_mach in
+ let one = imm 1L in
+ 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 nil_ptr = Il.Mem ((Il.Abs (Asm.IMM 0L)), Il.NilTy) in
+
+ let crate_rel fix =
+ Asm.SUB (Asm.M_POS fix, Asm.M_POS cx.ctxt_crate_fixup)
+ in
+
+ let crate_rel_word fix =
+ Asm.WORD (word_ty_signed_mach, crate_rel fix)
+ in
+
+ let crate_rel_imm (fix:fixup) : Il.operand =
+ Il.Imm (crate_rel fix, word_ty_signed_mach)
+ in
+
+ let table_of_crate_rel_fixups (fixups:fixup array) : Asm.frag =
+ Asm.SEQ (Array.map crate_rel_word fixups)
+ in
+
+ let fixup_rel_word (base:fixup) (fix:fixup) =
+ Asm.WORD (word_ty_signed_mach,
+ Asm.SUB (Asm.M_POS fix, Asm.M_POS base))
+ in
+
+ let table_of_fixup_rel_fixups
+ (fixup:fixup)
+ (fixups:fixup array)
+ : Asm.frag =
+ Asm.SEQ (Array.map (fixup_rel_word fixup) fixups)
+ in
+
+ let table_of_table_rel_fixups (fixups:fixup array) : Asm.frag =
+ let table_fix = new_fixup "vtbl" in
+ Asm.DEF (table_fix, table_of_fixup_rel_fixups table_fix fixups)
+ in
+
+ let nabi_indirect =
+ match cx.ctxt_sess.Session.sess_targ with
+ Linux_x86_elf -> false
+ | _ -> true
+ in
+
+ let nabi_rust =
+ { nabi_indirect = nabi_indirect;
+ nabi_convention = CONV_rust }
+ in
+
+ let out_mem_disp = abi.Abi.abi_frame_base_sz in
+ let arg0_disp =
+ Int64.add abi.Abi.abi_frame_base_sz abi.Abi.abi_implicit_args_sz
+ in
+ let frame_crate_ptr = word_n (-1) in
+ let frame_fns_disp = word_n (-2) in
+
+ let fn_ty (id:node_id) : Ast.ty =
+ Hashtbl.find cx.ctxt_all_item_types id
+ in
+ let fn_args_rty
+ (id:node_id)
+ (closure:Il.referent_ty option)
+ : Il.referent_ty =
+ let n_params =
+ if item_is_obj_fn cx id
+ then 0
+ else n_item_ty_params cx id
+ in
+ call_args_referent_type cx n_params (fn_ty id) closure
+ in
+
+ let emitters = Stack.create () in
+ let push_new_emitter (vregs_ok:bool) (fnid:node_id option) =
+ let e = Il.new_emitter
+ abi.Abi.abi_prealloc_quad
+ abi.Abi.abi_is_2addr_machine
+ vregs_ok fnid
+ in
+ Stack.push (Hashtbl.create 0) e.Il.emit_size_cache;
+ Stack.push e emitters;
+ in
+
+ let push_new_emitter_with_vregs fnid = push_new_emitter true fnid in
+ let push_new_emitter_without_vregs fnid = push_new_emitter false fnid in
+
+ let pop_emitter _ = ignore (Stack.pop emitters) in
+ let emitter _ = Stack.top emitters in
+ let emitter_size_cache _ = Stack.top (emitter()).Il.emit_size_cache in
+ let push_emitter_size_cache _ =
+ Stack.push
+ (Hashtbl.copy (emitter_size_cache()))
+ (emitter()).Il.emit_size_cache
+ in
+ let pop_emitter_size_cache _ =
+ ignore (Stack.pop (emitter()).Il.emit_size_cache)
+ in
+ let emit q = Il.emit (emitter()) q in
+ let next_vreg _ = Il.next_vreg (emitter()) in
+ let next_vreg_cell t = Il.next_vreg_cell (emitter()) t in
+ let next_spill_cell t =
+ let s = Il.next_spill (emitter()) in
+ let spill_mem = Il.Spill s in
+ let spill_ta = (spill_mem, Il.ScalarTy t) in
+ Il.Mem spill_ta
+ in
+ let mark _ : quad_idx = (emitter()).Il.emit_pc in
+ let patch_existing (jmp:quad_idx) (targ:quad_idx) : unit =
+ Il.patch_jump (emitter()) jmp targ
+ in
+ let patch (i:quad_idx) : unit =
+ Il.patch_jump (emitter()) i (mark());
+ (* Insert a dead quad to ensure there's an otherwise-unused
+ * jump-target here.
+ *)
+ emit Il.Dead
+ in
+
+ let current_fn () =
+ match (emitter()).Il.emit_node with
+ None -> bug () "current_fn without associated node"
+ | Some id -> id
+ in
+ let current_fn_args_rty (closure:Il.referent_ty option) : Il.referent_ty =
+ fn_args_rty (current_fn()) closure
+ in
+ let current_fn_callsz () = get_callsz cx (current_fn()) in
+
+ let annotations _ =
+ (emitter()).Il.emit_annotations
+ in
+
+ let annotate (str:string) =
+ let e = emitter() in
+ Hashtbl.add e.Il.emit_annotations e.Il.emit_pc str
+ in
+
+ let epilogue_jumps = Stack.create() in
+
+ let path_name (_:unit) : string =
+ string_of_name (Walk.path_to_name path)
+ in
+
+ let based (reg:Il.reg) : Il.mem =
+ Il.RegIn (reg, None)
+ in
+
+ let based_off (reg:Il.reg) (off:Asm.expr64) : Il.mem =
+ Il.RegIn (reg, Some off)
+ in
+
+ let based_imm (reg:Il.reg) (imm:int64) : Il.mem =
+ based_off reg (Asm.IMM imm)
+ in
+
+ let fp_imm (imm:int64) : Il.mem =
+ based_imm abi.Abi.abi_fp_reg imm
+ in
+
+ let sp_imm (imm:int64) : Il.mem =
+ based_imm abi.Abi.abi_sp_reg imm
+ in
+
+ let word_at (mem:Il.mem) : Il.cell =
+ Il.Mem (mem, Il.ScalarTy (Il.ValTy word_bits))
+ in
+
+ let wordptr_at (mem:Il.mem) : Il.cell =
+ Il.Mem (mem, Il.ScalarTy (Il.AddrTy (Il.ScalarTy (Il.ValTy word_bits))))
+ in
+
+ let mov (dst:Il.cell) (src:Il.operand) : unit =
+ emit (Il.umov dst src)
+ in
+
+ let umul (dst:Il.cell) (a:Il.operand) (b:Il.operand) : unit =
+ emit (Il.binary Il.UMUL dst a b);
+ in
+
+ let add (dst:Il.cell) (a:Il.operand) (b:Il.operand) : unit =
+ emit (Il.binary Il.ADD dst a b);
+ in
+
+ let add_to (dst:Il.cell) (src:Il.operand) : unit =
+ add dst (Il.Cell dst) src;
+ in
+
+ let sub (dst:Il.cell) (a:Il.operand) (b:Il.operand) : unit =
+ emit (Il.binary Il.SUB dst a b);
+ in
+
+ let sub_from (dst:Il.cell) (src:Il.operand) : unit =
+ sub dst (Il.Cell dst) src;
+ in
+
+ let lea (dst:Il.cell) (src:Il.mem) : unit =
+ emit (Il.lea dst (Il.Cell (Il.Mem (src, Il.OpaqueTy))))
+ in
+
+ let rty_ptr_at (mem:Il.mem) (pointee_rty:Il.referent_ty) : Il.cell =
+ Il.Mem (mem, Il.ScalarTy (Il.AddrTy pointee_rty))
+ in
+
+ let ptr_at (mem:Il.mem) (pointee_ty:Ast.ty) : Il.cell =
+ rty_ptr_at mem (referent_type abi pointee_ty)
+ in
+
+ let need_scalar_ty (rty:Il.referent_ty) : Il.scalar_ty =
+ match rty with
+ Il.ScalarTy s -> s
+ | _ -> bug () "expected ScalarTy"
+ in
+
+ let need_mem_cell (cell:Il.cell) : Il.typed_mem =
+ match cell with
+ Il.Mem a -> a
+ | Il.Reg _ -> bug ()
+ "expected address cell, got non-address register cell"
+ in
+
+ let need_cell (operand:Il.operand) : Il.cell =
+ match operand with
+ Il.Cell c -> c
+ | _ -> bug () "expected cell, got operand %s"
+ (Il.string_of_operand abi.Abi.abi_str_of_hardreg operand)
+ in
+
+ let get_element_ptr =
+ Il.get_element_ptr word_bits abi.Abi.abi_str_of_hardreg
+ in
+
+ let get_variant_ptr (mem_cell:Il.cell) (i:int) : Il.cell =
+ match mem_cell with
+ Il.Mem (mem, Il.UnionTy elts)
+ when i >= 0 && i < (Array.length elts) ->
+ assert ((Array.length elts) != 0);
+ Il.Mem (mem, elts.(i))
+
+ | _ -> bug () "get_variant_ptr %d on cell %s" i
+ (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"
+
+ and curr_crate_ptr _ : Il.cell =
+ word_at (fp_imm frame_crate_ptr)
+
+ and crate_rel_to_ptr (rel:Il.operand) (rty:Il.referent_ty) : Il.cell =
+ let cell = next_vreg_cell (Il.AddrTy rty) in
+ mov cell (Il.Cell (curr_crate_ptr()));
+ add_to cell rel;
+ cell
+
+ (*
+ * Note: alias *requires* its cell to be in memory already, and should
+ * only be used on slots you know to be memory-resident. Use 'aliasing' or
+ * 'via_memory' if you have a cell or operand you want in memory for a very
+ * short period of time (the time spent by the code generated by the thunk).
+ *)
+
+ and alias (cell:Il.cell) : Il.cell =
+ let mem, ty = need_mem_cell cell in
+ let vreg_cell = next_vreg_cell (Il.AddrTy ty) in
+ begin
+ match ty with
+ Il.NilTy -> ()
+ | _ -> lea vreg_cell mem
+ end;
+ vreg_cell
+
+ and force_to_mem (src:Il.operand) : Il.typed_mem =
+ let do_spill op (t:Il.scalar_ty) =
+ let spill = next_spill_cell t in
+ mov spill op;
+ need_mem_cell spill
+ in
+ match src with
+ Il.Cell (Il.Mem ta) -> ta
+ | Il.Cell (Il.Reg (_, t)) -> do_spill src t
+ | Il.Imm _ -> do_spill src (Il.ValTy word_bits)
+ | Il.ImmPtr (f, rty) ->
+ do_spill
+ (Il.Cell (crate_rel_to_ptr (crate_rel_imm f) rty))
+ (Il.AddrTy rty)
+
+ and force_to_reg (op:Il.operand) : Il.typed_reg =
+ let do_mov op st =
+ let tmp = next_vreg () in
+ let regty = (tmp, st) in
+ mov (Il.Reg regty) op;
+ regty
+ in
+ match op with
+ Il.Imm (_, tm) -> do_mov op (Il.ValTy (Il.bits_of_ty_mach tm))
+ | Il.ImmPtr (f, rty) ->
+ do_mov
+ (Il.Cell (crate_rel_to_ptr (crate_rel_imm f) rty))
+ (Il.AddrTy rty)
+ | Il.Cell (Il.Reg rt) -> rt
+ | Il.Cell (Il.Mem (_, Il.ScalarTy st)) -> do_mov op st
+ | Il.Cell (Il.Mem (_, rt)) ->
+ bug () "forcing non-scalar referent of type %s to register"
+ (Il.string_of_referent_ty rt)
+
+ and via_memory (writeback:bool) (c:Il.cell) (thunk:Il.cell -> unit) : unit =
+ match c with
+ Il.Mem _ -> thunk c
+ | Il.Reg _ ->
+ let mem_c = Il.Mem (force_to_mem (Il.Cell c)) in
+ thunk mem_c;
+ if writeback
+ then
+ mov c (Il.Cell mem_c)
+
+ and aliasing (writeback:bool) (c:Il.cell) (thunk:Il.cell -> unit) : unit =
+ via_memory writeback c (fun c -> thunk (alias c))
+
+ and pointee_type (ptr:Il.cell) : Il.referent_ty =
+ match ptr with
+ Il.Reg (_, (Il.AddrTy rt)) -> rt
+ | Il.Mem (_, Il.ScalarTy (Il.AddrTy rt)) -> rt
+ | _ ->
+ bug () "taking pointee-type of non-address cell %s "
+ (cell_str ptr)
+
+ and deref (ptr:Il.cell) : Il.cell =
+ let (r, st) = force_to_reg (Il.Cell ptr) in
+ match st with
+ Il.AddrTy rt -> Il.Mem (based r, rt)
+ | _ -> bug () "dereferencing non-address cell of type %s "
+ (Il.string_of_scalar_ty st)
+
+ and deref_off (ptr:Il.cell) (off:Asm.expr64) : Il.cell =
+ let (r, st) = force_to_reg (Il.Cell ptr) in
+ match st with
+ Il.AddrTy rt -> Il.Mem (based_off r off, rt)
+ | _ -> bug () "offset-dereferencing non-address cell of type %s "
+ (Il.string_of_scalar_ty st)
+
+ and deref_imm (ptr:Il.cell) (imm:int64) : Il.cell =
+ deref_off ptr (Asm.IMM imm)
+
+ and tp_imm (imm:int64) : Il.cell =
+ deref_imm abi.Abi.abi_tp_cell imm
+ in
+
+
+ let make_tydesc_slots n =
+ Array.init n (fun _ -> interior_slot Ast.TY_type)
+ in
+
+ let cell_vreg_num (vr:(int option) ref) : int =
+ match !vr with
+ None ->
+ let v = (Il.next_vreg_num (emitter())) in
+ vr := Some v;
+ v
+ | Some v -> v
+ in
+
+ let slot_id_referent_type (slot_id:node_id) : Il.referent_ty =
+ slot_referent_type abi (referent_to_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_ty_param (ty_params:Il.cell) (param_idx:int) : Il.cell =
+ 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
+ in
+
+ let get_args_for_current_frame _ =
+ let curr_args_rty =
+ current_fn_args_rty (Some Il.OpaqueTy)
+ in
+ caller_args_cell curr_args_rty
+ in
+
+ let get_indirect_args_for_current_frame _ =
+ get_element_ptr (get_args_for_current_frame ())
+ Abi.calltup_elt_indirect_args
+ in
+
+ let get_iterator_args_for_current_frame _ =
+ get_element_ptr (get_args_for_current_frame ())
+ Abi.calltup_elt_iterator_args
+ in
+
+ let get_closure_for_current_frame _ =
+ let self_indirect_args =
+ get_indirect_args_for_current_frame ()
+ in
+ get_element_ptr self_indirect_args
+ Abi.indirect_args_elt_closure
+ in
+
+ let get_iter_block_fn_for_current_frame _ =
+ let self_iterator_args =
+ get_iterator_args_for_current_frame ()
+ in
+ let blk_fn = get_element_ptr self_iterator_args
+ Abi.iterator_args_elt_block_fn
+ in
+ ptr_cast blk_fn
+ (Il.ScalarTy (Il.AddrTy Il.CodeTy))
+ in
+
+ let get_iter_outer_frame_ptr_for_current_frame _ =
+ let self_iterator_args =
+ get_iterator_args_for_current_frame ()
+ in
+ get_element_ptr self_iterator_args
+ 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 item_is_obj_fn cx id
+ then
+ begin
+ let obj = get_obj_for_current_frame() in
+ let tydesc = get_element_ptr obj 1 in
+ let ty_params_ty = Ast.TY_tup (make_tydesc_slots 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
+ in
+
+ let get_ty_param_in_current_frame (param_idx:int) : Il.cell =
+ get_ty_param (get_ty_params_of_current_frame()) param_idx
+ in
+
+ let linearize_ty_params (ty:Ast.ty) : (Ast.ty * Il.operand array) =
+ let htab = Hashtbl.create 0 in
+ let q = Queue.create () in
+ let base = ty_fold_rebuild (fun t -> t) in
+ let ty_fold_param (i, mut) =
+ let param = Ast.TY_param (i, mut) in
+ match htab_search htab param with
+ Some p -> p
+ | None ->
+ let p = Ast.TY_param (Hashtbl.length htab, mut) in
+ htab_put htab param p;
+ Queue.add (Il.Cell (get_ty_param_in_current_frame i)) q;
+ p
+ in
+ let fold =
+ { base with
+ ty_fold_param = ty_fold_param; }
+ in
+ let ty = fold_ty fold ty in
+ (ty, queue_to_arr q)
+ in
+
+ let has_parametric_types (t:Ast.ty) : bool =
+ let base = ty_fold_bool_or false in
+ let ty_fold_param _ =
+ true
+ in
+ let fold = { base with ty_fold_param = ty_fold_param } in
+ fold_ty fold t
+ in
+
+ let rec calculate_sz (ty_params:Il.cell) (size:size) : Il.operand =
+ iflog (fun _ -> annotate
+ (Printf.sprintf "calculating size %s"
+ (string_of_size size)));
+ let sub_sz = calculate_sz ty_params in
+ match htab_search (emitter_size_cache()) size with
+ Some op -> op
+ | _ ->
+ let res =
+ match size with
+ SIZE_fixed i -> imm i
+ | SIZE_fixup_mem_pos f -> Il.Imm (Asm.M_POS f, word_ty_mach)
+ | SIZE_fixup_mem_sz f -> Il.Imm (Asm.M_SZ f, word_ty_mach)
+
+ | SIZE_param_size i ->
+ let tydesc = deref (get_ty_param ty_params i) in
+ Il.Cell (get_element_ptr tydesc Abi.tydesc_field_size)
+
+ | SIZE_param_align i ->
+ let tydesc = deref (get_ty_param ty_params i) in
+ Il.Cell (get_element_ptr tydesc Abi.tydesc_field_align)
+
+ | SIZE_rt_neg a ->
+ let op_a = sub_sz a in
+ let tmp = next_vreg_cell word_ty in
+ emit (Il.unary Il.NEG tmp op_a);
+ Il.Cell tmp
+
+ | SIZE_rt_add (a, b) ->
+ let op_a = sub_sz a in
+ let op_b = sub_sz b in
+ let tmp = next_vreg_cell word_ty in
+ add tmp op_a op_b;
+ Il.Cell tmp
+
+ | SIZE_rt_mul (a, b) ->
+ let op_a = sub_sz a in
+ let op_b = sub_sz b in
+ let tmp = next_vreg_cell word_ty in
+ emit (Il.binary Il.UMUL tmp op_a op_b);
+ Il.Cell tmp
+
+ | SIZE_rt_max (a, b) ->
+ let op_a = sub_sz a in
+ let op_b = sub_sz b in
+ let tmp = next_vreg_cell word_ty in
+ mov tmp op_a;
+ emit (Il.cmp op_a op_b);
+ let jmp = mark () in
+ emit (Il.jmp Il.JAE Il.CodeNone);
+ mov tmp op_b;
+ patch jmp;
+ Il.Cell tmp
+
+ | SIZE_rt_align (align, off) ->
+ (*
+ * calculate off + pad where:
+ *
+ * pad = (align - (off mod align)) mod align
+ *
+ * In our case it's always a power of two,
+ * so we can just do:
+ *
+ * mask = align-1
+ * off += mask
+ * off &= ~mask
+ *
+ *)
+ annotate "fetch alignment";
+ let op_align = sub_sz align in
+ annotate "fetch offset";
+ let op_off = sub_sz off in
+ let mask = next_vreg_cell word_ty in
+ let off = next_vreg_cell word_ty in
+ mov mask op_align;
+ sub_from mask one;
+ mov off op_off;
+ add_to off (Il.Cell mask);
+ emit (Il.unary Il.NOT mask (Il.Cell mask));
+ emit (Il.binary Il.AND
+ off (Il.Cell off) (Il.Cell mask));
+ Il.Cell off
+ in
+ iflog (fun _ -> annotate
+ (Printf.sprintf "calculated size %s is %s"
+ (string_of_size size)
+ (oper_str res)));
+ htab_put (emitter_size_cache()) size res;
+ res
+
+
+ and calculate_sz_in_current_frame (size:size) : Il.operand =
+ calculate_sz (get_ty_params_of_current_frame()) size
+
+ and callee_args_cell (tail_area:bool) (args_rty:Il.referent_ty) : Il.cell =
+ if tail_area
+ then
+ Il.Mem (sp_off_sz (current_fn_callsz ()), args_rty)
+ else
+ Il.Mem (sp_imm 0L, args_rty)
+
+ and based_sz (ty_params:Il.cell) (reg:Il.reg) (size:size) : Il.mem =
+ match Il.size_to_expr64 size with
+ Some e -> based_off reg e
+ | None ->
+ let runtime_size = calculate_sz ty_params size in
+ let v = next_vreg () in
+ let c = (Il.Reg (v, word_ty)) in
+ mov c (Il.Cell (Il.Reg (reg, word_ty)));
+ add_to c runtime_size;
+ based v
+
+ and fp_off_sz (size:size) : Il.mem =
+ based_sz (get_ty_params_of_current_frame()) abi.Abi.abi_fp_reg size
+
+ and sp_off_sz (size:size) : Il.mem =
+ based_sz (get_ty_params_of_current_frame()) abi.Abi.abi_sp_reg size
+ in
+
+ let slot_sz_in_current_frame (slot:Ast.slot) : Il.operand =
+ let rty = slot_referent_type abi slot in
+ let sz = Il.referent_ty_size word_bits rty in
+ calculate_sz_in_current_frame sz
+ in
+
+ let slot_sz_with_ty_params
+ (ty_params:Il.cell)
+ (slot:Ast.slot)
+ : Il.operand =
+ let rty = slot_referent_type abi slot in
+ let sz = Il.referent_ty_size word_bits rty in
+ calculate_sz ty_params sz
+ in
+
+ let get_element_ptr_dyn
+ (ty_params:Il.cell)
+ (mem_cell:Il.cell)
+ (i:int)
+ : Il.cell =
+ match mem_cell with
+ Il.Mem (mem, Il.StructTy elts)
+ when i >= 0 && i < (Array.length elts) ->
+ assert ((Array.length elts) != 0);
+ begin
+ let elt_rty = elts.(i) in
+ let elt_off = Il.get_element_offset word_bits elts i in
+ match elt_off with
+ SIZE_fixed fixed_off ->
+ Il.Mem (Il.mem_off_imm mem fixed_off, elt_rty)
+ | sz ->
+ let sz = calculate_sz ty_params sz in
+ let v = next_vreg word_ty in
+ let vc = Il.Reg (v, word_ty) in
+ lea vc mem;
+ add_to vc sz;
+ Il.Mem (based v, elt_rty)
+ end
+ | _ -> bug () "get_element_ptr_dyn %d on cell %s" i
+ (cell_str mem_cell)
+ in
+
+ let get_element_ptr_dyn_in_current_frame
+ (mem_cell:Il.cell)
+ (i:int)
+ : Il.cell =
+ get_element_ptr_dyn (get_ty_params_of_current_frame()) mem_cell i
+ in
+
+ let get_explicit_args_for_current_frame _ =
+ get_element_ptr_dyn_in_current_frame (get_args_for_current_frame ())
+ Abi.calltup_elt_args
+ in
+
+
+ let deref_off_sz
+ (ty_params:Il.cell)
+ (ptr:Il.cell)
+ (size:size)
+ : Il.cell =
+ match Il.size_to_expr64 size with
+ Some e -> deref_off ptr e
+ | None ->
+ let (r,_) = force_to_reg (Il.Cell ptr) in
+ let mem = based_sz ty_params r size in
+ Il.Mem (mem, (pointee_type ptr))
+ in
+
+ let cell_of_block_slot
+ (slot_id:node_id)
+ : Il.cell =
+ let referent_type = slot_id_referent_type slot_id in
+ match htab_search cx.ctxt_slot_vregs slot_id with
+ Some vr ->
+ begin
+ match referent_type with
+ Il.ScalarTy st -> Il.Reg (Il.Vreg (cell_vreg_num vr), st)
+ | Il.NilTy -> nil_ptr
+ | Il.StructTy _ -> bugi cx slot_id
+ "cannot treat structured referent as single operand"
+ | Il.UnionTy _ -> bugi cx slot_id
+ "cannot treat union referent as single operand"
+ | Il.ParamTy _ -> bugi cx slot_id
+ "cannot treat parametric referent as single operand"
+ | Il.OpaqueTy -> bugi cx slot_id
+ "cannot treat opaque referent as single operand"
+ | Il.CodeTy -> bugi cx slot_id
+ "cannot treat code referent as single operand"
+ end
+ | None ->
+ begin
+ match htab_search cx.ctxt_slot_offsets slot_id with
+ None -> bugi cx slot_id
+ "slot assigned to neither vreg nor offset"
+ | Some off ->
+ if slot_is_obj_state cx slot_id
+ then
+ begin
+ let state_arg = get_closure_for_current_frame () in
+ let (slot_mem, _) =
+ need_mem_cell (deref_off_sz
+ (get_ty_params_of_current_frame())
+ state_arg off)
+ in
+ Il.Mem (slot_mem, referent_type)
+ end
+ else
+ if (Stack.is_empty curr_stmt)
+ then
+ Il.Mem (fp_off_sz off, referent_type)
+ else
+ let slot_depth = get_slot_depth cx slot_id in
+ let stmt_depth =
+ get_stmt_depth cx (Stack.top curr_stmt)
+ in
+ if slot_depth <> stmt_depth
+ then
+ let _ = assert (slot_depth < stmt_depth) in
+ let _ =
+ iflog
+ begin
+ fun _ ->
+ let k =
+ Hashtbl.find cx.ctxt_slot_keys slot_id
+ in
+ annotate
+ (Printf.sprintf
+ "access outer frame slot #%d = %s"
+ (int_of_node slot_id)
+ (Ast.fmt_to_str
+ Ast.fmt_slot_key k))
+ end
+ in
+ let diff = stmt_depth - slot_depth in
+ let _ = annotate "get outer frame pointer" in
+ let fp =
+ get_iter_outer_frame_ptr_for_current_frame ()
+ in
+ if diff > 1
+ then
+ bug () "unsupported nested for each loop";
+ for i = 2 to diff do
+ (* FIXME: access outer caller-block fps,
+ * given nearest caller-block fp.
+ *)
+ let _ =
+ annotate "step to outer-outer frame"
+ in
+ mov fp (Il.Cell fp)
+ done;
+ let _ = annotate "calculate size" in
+ let p =
+ based_sz (get_ty_params_of_current_frame())
+ (fst (force_to_reg (Il.Cell fp))) off
+ in
+ Il.Mem (p, referent_type)
+ else
+ Il.Mem (fp_off_sz off, referent_type)
+ end
+ in
+
+ let binop_to_jmpop (binop:Ast.binop) : Il.jmpop =
+ match binop with
+ Ast.BINOP_eq -> Il.JE
+ | Ast.BINOP_ne -> Il.JNE
+ | Ast.BINOP_lt -> Il.JL
+ | Ast.BINOP_le -> Il.JLE
+ | Ast.BINOP_ge -> Il.JGE
+ | Ast.BINOP_gt -> Il.JG
+ | _ -> bug () "Unhandled binop in binop_to_jmpop"
+ in
+
+ let get_vtbl_entry_idx (table_ptr:Il.cell) (i:int) : Il.cell =
+ (* Vtbls are encoded as tables of table-relative displacements. *)
+ let (table_mem, _) = need_mem_cell (deref table_ptr) in
+ let disp = Il.Cell (word_at (Il.mem_off_imm table_mem (word_n i))) in
+ let ptr_cell = next_vreg_cell (Il.AddrTy Il.CodeTy) in
+ mov ptr_cell (Il.Cell table_ptr);
+ add_to ptr_cell disp;
+ ptr_cell
+ in
+
+ let get_vtbl_entry
+ (obj_cell:Il.cell)
+ (obj_ty:Ast.ty_obj)
+ (id:Ast.ident)
+ : (Il.cell * Ast.ty_fn) =
+ let (_, fns) = obj_ty in
+ let sorted_idents = sorted_htab_keys fns in
+ let i = arr_idx sorted_idents id in
+ let fn_ty = Hashtbl.find fns id in
+ let table_ptr = get_element_ptr obj_cell Abi.binding_field_item in
+ (get_vtbl_entry_idx table_ptr i, fn_ty)
+ in
+
+ let rec trans_slot_lval_ext
+ (base_ty:Ast.ty)
+ (cell:Il.cell)
+ (comp:Ast.lval_component)
+ : (Il.cell * Ast.slot) =
+
+ let bounds_checked_access at slot =
+ let atop = trans_atom at in
+ let unit_sz = slot_sz_in_current_frame slot in
+ let idx = next_vreg_cell word_ty in
+ emit (Il.binary Il.UMUL idx atop unit_sz);
+ let elt_mem = trans_bounds_check (deref cell) (Il.Cell idx) in
+ (Il.Mem (elt_mem, slot_referent_type abi slot), slot)
+ in
+
+ match (base_ty, comp) with
+ (Ast.TY_rec entries,
+ Ast.COMP_named (Ast.COMP_ident id)) ->
+ let i = arr_idx (Array.map fst entries) id in
+ (get_element_ptr_dyn_in_current_frame cell i, snd entries.(i))
+
+ | (Ast.TY_tup entries,
+ Ast.COMP_named (Ast.COMP_idx i)) ->
+ (get_element_ptr_dyn_in_current_frame cell i, entries.(i))
+
+ | (Ast.TY_vec slot,
+ Ast.COMP_atom at) ->
+ bounds_checked_access at slot
+
+ | (Ast.TY_str,
+ Ast.COMP_atom at) ->
+ bounds_checked_access at (interior_slot (Ast.TY_mach TY_u8))
+
+ | (Ast.TY_obj obj_ty,
+ Ast.COMP_named (Ast.COMP_ident id)) ->
+ let (cell, fn_ty) = get_vtbl_entry cell obj_ty id in
+ (cell, (interior_slot (Ast.TY_fn fn_ty)))
+
+
+ | _ -> bug () "unhandled form of lval_ext in trans_slot_lval_ext"
+
+ (*
+ * vec: operand holding ptr to vec.
+ * mul_idx: index value * unit size.
+ * return: ptr to element.
+ *)
+ and trans_bounds_check (vec:Il.cell) (mul_idx:Il.operand) : Il.mem =
+ let (len:Il.cell) = get_element_ptr vec Abi.vec_elt_fill in
+ let (data:Il.cell) = get_element_ptr vec Abi.vec_elt_data in
+ let (base:Il.cell) = next_vreg_cell Il.voidptr_t in
+ let (elt_reg:Il.reg) = next_vreg () in
+ let (elt:Il.cell) = Il.Reg (elt_reg, Il.voidptr_t) in
+ let (diff:Il.cell) = next_vreg_cell word_ty in
+ annotate "bounds check";
+ lea base (fst (need_mem_cell data));
+ add elt (Il.Cell base) mul_idx;
+ emit (Il.binary Il.SUB diff (Il.Cell elt) (Il.Cell base));
+ let jmp = trans_compare Il.JB (Il.Cell diff) (Il.Cell len) in
+ trans_cond_fail "bounds check" jmp;
+ based elt_reg
+
+ and trans_lval_full
+ (initializing:bool)
+ (lv:Ast.lval)
+ : (Il.cell * Ast.slot) =
+
+ let rec trans_slot_lval_full (initializing:bool) lv =
+ let (cell, slot) =
+ match lv with
+ Ast.LVAL_ext (base, comp) ->
+ let (base_cell, base_slot) =
+ trans_slot_lval_full initializing base
+ in
+ let base_cell' = deref_slot initializing base_cell base_slot in
+ trans_slot_lval_ext (slot_ty base_slot) base_cell' comp
+
+ | Ast.LVAL_base nb ->
+ let slot = lval_to_slot cx nb.id in
+ let referent = lval_to_referent cx nb.id in
+ let cell = cell_of_block_slot referent in
+ (cell, slot)
+ in
+ iflog
+ begin
+ fun _ ->
+ annotate
+ (Printf.sprintf "lval %a = %s"
+ Ast.sprintf_lval lv
+ (cell_str cell))
+ end;
+ (cell, slot)
+
+ in
+ if lval_is_slot cx lv
+ then trans_slot_lval_full initializing lv
+ else
+ if initializing
+ then err None "init item"
+ else
+ begin
+ assert (lval_is_item cx lv);
+ bug ()
+ "trans_lval_full called on item lval '%a'" Ast.sprintf_lval lv
+ end
+
+ and trans_lval_maybe_init
+ (initializing:bool)
+ (lv:Ast.lval)
+ : (Il.cell * Ast.slot) =
+ trans_lval_full initializing lv
+
+ and trans_lval_init (lv:Ast.lval) : (Il.cell * Ast.slot) =
+ trans_lval_maybe_init true lv
+
+ and trans_lval (lv:Ast.lval) : (Il.cell * Ast.slot) =
+ trans_lval_maybe_init false lv
+
+ and trans_callee
+ (flv:Ast.lval)
+ : (Il.operand * Ast.ty) =
+ (* direct call to item *)
+ let fty = Hashtbl.find cx.ctxt_all_lval_types (lval_base_id flv) in
+ if lval_is_item cx flv then
+ let fn_item = lval_item cx flv in
+ let fn_ptr = code_fixup_to_ptr_operand (get_fn_fixup cx fn_item.id) in
+ (fn_ptr, fty)
+ else
+ (* indirect call to computed slot *)
+ let (cell, _) = trans_lval flv in
+ (Il.Cell cell, fty)
+
+ and trans_crate_rel_data_operand
+ (d:data)
+ (thunk:unit -> Asm.frag)
+ : Il.operand =
+ let (fix, _) =
+ htab_search_or_add cx.ctxt_data d
+ begin
+ fun _ ->
+ let fix = new_fixup "data item" in
+ let frag = Asm.DEF (fix, thunk()) in
+ (fix, frag)
+ end
+ in
+ crate_rel_imm fix
+
+ and trans_crate_rel_data_frag (d:data) (thunk:unit -> Asm.frag) : Asm.frag =
+ let (fix, _) =
+ htab_search_or_add cx.ctxt_data d
+ begin
+ fun _ ->
+ let fix = new_fixup "data item" in
+ let frag = Asm.DEF (fix, thunk()) in
+ (fix, frag)
+ end
+ in
+ crate_rel_word fix
+
+ and trans_crate_rel_static_string_operand (s:string) : Il.operand =
+ trans_crate_rel_data_operand (DATA_str s) (fun _ -> Asm.ZSTRING s)
+
+ and trans_crate_rel_static_string_frag (s:string) : Asm.frag =
+ trans_crate_rel_data_frag (DATA_str s) (fun _ -> Asm.ZSTRING s)
+
+ 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))
+
+ and get_static_tydesc
+ (idopt:node_id option)
+ (t:Ast.ty)
+ (sz:int64)
+ (align:int64)
+ : Il.operand =
+ trans_crate_rel_data_operand
+ (DATA_tydesc t)
+ begin
+ fun _ ->
+ let tydesc_fixup = new_fixup "tydesc" in
+ log cx "tydesc for %a has sz=%Ld, align=%Ld"
+ Ast.sprintf_ty t sz align;
+ Asm.DEF
+ (tydesc_fixup,
+ Asm.SEQ
+ [|
+ Asm.WORD (word_ty_mach, Asm.IMM 0L);
+ Asm.WORD (word_ty_mach, Asm.IMM sz);
+ Asm.WORD (word_ty_mach, Asm.IMM align);
+ table_of_fixup_rel_fixups tydesc_fixup
+ [|
+ get_copy_glue t None;
+ get_drop_glue t None;
+ get_free_glue t (slot_mem_ctrl (interior_slot t)) None;
+ get_mark_glue t None;
+ |];
+ (* Include any obj-dtor, if this is an obj and has one. *)
+ begin
+ match idopt with
+ None -> Asm.WORD (word_ty_mach, Asm.IMM 0L);
+ | Some oid ->
+ begin
+ let g = GLUE_obj_drop oid in
+ match htab_search cx.ctxt_glue_code g with
+ Some code ->
+ fixup_rel_word
+ tydesc_fixup
+ code.code_fixup;
+ | None ->
+ Asm.WORD (word_ty_mach, Asm.IMM 0L);
+ end
+ end;
+ |])
+ end
+
+ and get_obj_vtbl (id:node_id) : Il.operand =
+ let obj =
+ match Hashtbl.find cx.ctxt_all_defns id with
+ DEFN_item { Ast.decl_item=Ast.MOD_ITEM_obj obj} -> obj
+ | _ -> bug () "Trans.get_obj_vtbl on non-obj referent"
+ in
+ trans_crate_rel_data_operand (DATA_obj_vtbl id)
+ begin
+ fun _ ->
+ iflog (fun _ -> log cx "emitting %d-entry obj vtbl for %s"
+ (Hashtbl.length obj.Ast.obj_fns) (path_name()));
+ table_of_table_rel_fixups
+ (Array.map
+ begin
+ fun k ->
+ let fn = Hashtbl.find obj.Ast.obj_fns k in
+ get_fn_fixup cx fn.id
+ end
+ (sorted_htab_keys obj.Ast.obj_fns))
+ end
+
+
+ and trans_copy_forward_args (args_rty:Il.referent_ty) : unit =
+ let caller_args_cell = caller_args_cell args_rty in
+ let callee_args_cell = callee_args_cell false args_rty in
+ let (dst_reg, _) = force_to_reg (Il.Cell (alias callee_args_cell)) in
+ let (src_reg, _) = force_to_reg (Il.Cell (alias caller_args_cell)) in
+ let tmp_reg = next_vreg () in
+ let nbytes = force_sz (Il.referent_ty_size word_bits args_rty) in
+ abi.Abi.abi_emit_inline_memcpy (emitter())
+ nbytes dst_reg src_reg tmp_reg false;
+
+
+ and get_forwarding_obj_fn
+ (ident:Ast.ident)
+ (caller:Ast.ty_obj)
+ (callee:Ast.ty_obj)
+ : fixup =
+ (* Forwarding "glue" is not glue in the normal sense of being called with
+ * only Abi.worst_case_glue_call_args args; the functions are full-fleged
+ * obj fns like any other, and they perform a full call to the target
+ * obj. We just use the glue facility here to store the forwarding
+ * operators somewhere.
+ *)
+ let g = GLUE_forward (ident, caller, callee) in
+ let fix = new_fixup (glue_str cx g) in
+ 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))
+ in
+ let callsz = Il.referent_ty_size word_bits self_args_rty in
+ let spill = new_fixup "forwarding fn spill" in
+ trans_glue_frame_entry callsz spill;
+ let all_self_args_cell = caller_args_cell self_args_rty in
+ let self_indirect_args_cell =
+ get_element_ptr all_self_args_cell Abi.calltup_elt_indirect_args
+ in
+ (*
+ * Note: this is wrong. This assumes our closure is a vtbl,
+ * when in fact it is a pointer to a refcounted malloc slab
+ * containing an obj.
+ *)
+ let closure_cell =
+ deref (get_element_ptr self_indirect_args_cell
+ Abi.indirect_args_elt_closure)
+ in
+
+ let (callee_fn_cell, _) =
+ get_vtbl_entry closure_cell callee ident
+ in
+ iflog (fun _ -> annotate "copy args forward to callee");
+ trans_copy_forward_args self_args_rty;
+
+ iflog (fun _ -> annotate "call through to callee");
+ (* FIXME: use a tail-call here. *)
+ call_code (code_of_cell callee_fn_cell);
+ trans_glue_frame_exit fix spill g;
+ fix
+
+
+ and get_forwarding_vtbl
+ (caller:Ast.ty_obj)
+ (callee:Ast.ty_obj)
+ : Il.operand =
+ trans_crate_rel_data_operand (DATA_forwarding_vtbl (caller,callee))
+ begin
+ fun _ ->
+ let (_,fns) = caller in
+ iflog (fun _ -> log cx "emitting %d-entry obj forwarding vtbl"
+ (Hashtbl.length fns));
+ table_of_table_rel_fixups
+ (Array.map
+ begin
+ fun k ->
+ get_forwarding_obj_fn k caller callee
+ end
+ (sorted_htab_keys fns))
+ end
+
+ and trans_init_str (dst:Ast.lval) (s:string) : unit =
+ (* Include null byte. *)
+ let init_sz = Int64.of_int ((String.length s) + 1) in
+ let static = trans_static_string s in
+ let (dst, _) = trans_lval_init dst in
+ trans_upcall "upcall_new_str" dst [| static; imm init_sz |]
+
+ and trans_lit (lit:Ast.lit) : Il.operand =
+ match lit with
+ Ast.LIT_nil -> Il.Cell (nil_ptr)
+ | Ast.LIT_bool false -> imm_false
+ | Ast.LIT_bool true -> imm_true
+ | Ast.LIT_char c -> imm_of_ty (Int64.of_int c) TY_u32
+ | Ast.LIT_int (i, _) -> simm i
+ | Ast.LIT_uint (i, _) -> imm i
+ | Ast.LIT_mach (m, n, _) -> imm_of_ty n m
+
+ and trans_atom (atom:Ast.atom) : Il.operand =
+ iflog
+ begin
+ fun _ ->
+ annotate (Ast.fmt_to_str Ast.fmt_atom atom)
+ end;
+
+ match atom with
+ Ast.ATOM_lval lv ->
+ let (cell, slot) = trans_lval lv in
+ Il.Cell (deref_slot false cell slot)
+
+ | Ast.ATOM_literal lit -> trans_lit lit.node
+
+ and fixup_to_ptr_operand
+ (imm_ok:bool)
+ (fix:fixup)
+ (referent_ty:Il.referent_ty)
+ : Il.operand =
+ if imm_ok
+ then Il.ImmPtr (fix, referent_ty)
+ else Il.Cell (crate_rel_to_ptr (crate_rel_imm fix) referent_ty)
+
+ and code_fixup_to_ptr_operand (fix:fixup) : Il.operand =
+ fixup_to_ptr_operand abi.Abi.abi_has_pcrel_code fix Il.CodeTy
+
+ (* A pointer-valued op may be of the form ImmPtr, which carries its
+ * target fixup, "constant-propagated" through trans so that
+ * pc-relative addressing can make use of it whenever
+ * appropriate. Reify_ptr exists for cases when you are about to
+ * store an ImmPtr into a memory cell or other place beyond which the
+ * compiler will cease to know about its identity; at this point you
+ * should decay it to a crate-relative displacement and
+ * (computationally) add it to the crate base value, before working
+ * with it.
+ *
+ * This helps you obey the IL type-system prohibition against
+ * 'mov'-ing an ImmPtr to a cell. If you forget to call this
+ * in the right places, you will get code-generation failures.
+ *)
+ and reify_ptr (op:Il.operand) : Il.operand =
+ match op with
+ Il.ImmPtr (fix, rty) ->
+ Il.Cell (crate_rel_to_ptr (crate_rel_imm fix) rty)
+ | _ -> op
+
+ and annotate_quads (name:string) : unit =
+ let e = emitter() in
+ let quads = emitted_quads e in
+ let annotations = annotations() in
+ log cx "emitted quads for %s:" name;
+ for i = 0 to arr_max quads
+ do
+ if Hashtbl.mem annotations i
+ then
+ List.iter
+ (fun a -> log cx "// %s" a)
+ (List.rev (Hashtbl.find_all annotations i));
+ log cx "[%6d]\t%s" i
+ (Il.string_of_quad
+ abi.Abi.abi_str_of_hardreg quads.(i));
+ done
+
+
+ and write_frame_info_ptrs (fnid:node_id option) =
+ let frame_fns =
+ match fnid with
+ None -> zero
+ | Some fnid -> get_frame_glue_fns fnid
+ in
+ let crate_ptr_reg = next_vreg () in
+ let crate_ptr_cell = Il.Reg (crate_ptr_reg, (Il.AddrTy Il.OpaqueTy)) in
+ iflog (fun _ -> annotate "write frame-info pointers");
+ Abi.load_fixup_addr (emitter())
+ crate_ptr_reg cx.ctxt_crate_fixup Il.OpaqueTy;
+ mov (word_at (fp_imm frame_crate_ptr)) (Il.Cell (crate_ptr_cell));
+ mov (word_at (fp_imm frame_fns_disp)) frame_fns
+
+ and check_interrupt_flag _ =
+ let wordptr_ty = Il.AddrTy (Il.ScalarTy word_ty) in
+ let dom = next_vreg_cell wordptr_ty in
+ let flag = next_vreg_cell word_ty in
+ mov dom (Il.Cell (tp_imm (word_n Abi.task_field_dom)));
+ mov flag (Il.Cell (deref_imm dom
+ (word_n Abi.dom_field_interrupt_flag)));
+ let null_jmp = null_check flag in
+ trans_yield ();
+ patch null_jmp
+
+ and trans_glue_frame_entry
+ (callsz:size)
+ (spill:fixup)
+ : unit =
+ let framesz = SIZE_fixup_mem_sz spill in
+ 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");
+ write_frame_info_ptrs None;
+ check_interrupt_flag ();
+ iflog (fun _ -> annotate "finished prologue");
+
+ and emitted_quads e =
+ Array.sub e.Il.emit_quads 0 e.Il.emit_pc
+
+ and capture_emitted_glue (fix:fixup) (spill:fixup) (g:glue) : unit =
+ let e = emitter() in
+ iflog (fun _ -> annotate_quads (glue_str cx g));
+ let code = { code_fixup = fix;
+ code_quads = emitted_quads e;
+ code_vregs_and_spill = Some (Il.num_vregs e, spill); }
+ in
+ htab_put cx.ctxt_glue_code g code
+
+ and trans_glue_frame_exit (fix:fixup) (spill:fixup) (g:glue) : unit =
+ iflog (fun _ -> annotate "epilogue");
+ abi.Abi.abi_emit_fn_epilogue (emitter());
+ capture_emitted_glue fix spill g;
+ pop_emitter ()
+
+ and emit_exit_task_glue (fix:fixup) (g:glue) : unit =
+ let name = glue_str cx g in
+ let spill = new_fixup (name ^ " spill") in
+ push_new_emitter_with_vregs None;
+ (*
+ * We return-to-here in a synthetic frame we did not build; our job is
+ * merely to call upcall_exit.
+ *)
+ iflog (fun _ -> annotate "assume 'exited' state");
+ trans_void_upcall "upcall_exit" [| |];
+ capture_emitted_glue fix spill g;
+ pop_emitter ()
+
+ and get_exit_task_glue _ : fixup =
+ let g = GLUE_exit_task in
+ match htab_search cx.ctxt_glue_code g with
+ Some code -> code.code_fixup
+ | None ->
+ let fix = cx.ctxt_exit_task_fixup in
+ emit_exit_task_glue fix g;
+ fix
+
+ (*
+ * Closure representation has 3 GEP-parts:
+ *
+ * ......
+ * . gc . gc control word, if mutable
+ * +----+
+ * | rc | refcount
+ * +----+
+ *
+ * +----+
+ * | tf | ----> pair of fn+binding that closure
+ * +----+ / targets
+ * | tb | --
+ * +----+
+ *
+ * +----+
+ * | b1 | bound arg1
+ * +----+
+ * . .
+ * . .
+ * . .
+ * +----+
+ * | bN | bound argN
+ * +----+
+ *)
+
+ and closure_referent_type
+ (bs:Ast.slot array)
+ (* FIXME (issue #5): mutability flag *)
+ : Il.referent_ty =
+ let rc = Il.ScalarTy word_ty in
+ let targ = referent_type abi (mk_simple_ty_fn [||]) in
+ let bindings = Array.map (slot_referent_type abi) bs in
+ Il.StructTy [| rc; targ; Il.StructTy bindings |]
+
+ (* FIXME (issue #2): this should eventually use tail calling logic *)
+
+ and emit_fn_binding_glue
+ (arg_slots:Ast.slot array)
+ (arg_bound_flags:bool array)
+ (fix:fixup)
+ (g:glue)
+ : unit =
+ let extract_slots want_bound =
+ arr_filter_some
+ (arr_map2
+ (fun slot bound ->
+ if bound = want_bound then Some slot else None)
+ arg_slots
+ arg_bound_flags)
+ in
+ let bound_slots = extract_slots true in
+ let unbound_slots = extract_slots false in
+ let (self_ty:Ast.ty) = mk_simple_ty_fn unbound_slots in
+ let (callee_ty:Ast.ty) = mk_simple_ty_fn arg_slots in
+
+ let self_closure_rty = closure_referent_type bound_slots in
+ (* FIXME: binding type parameters doesn't work. *)
+ let self_args_rty =
+ call_args_referent_type cx 0 self_ty (Some self_closure_rty)
+ in
+ let callee_args_rty =
+ call_args_referent_type cx 0 callee_ty (Some Il.OpaqueTy)
+ in
+
+ let callsz = Il.referent_ty_size word_bits callee_args_rty in
+ let spill = new_fixup "bind glue spill" in
+ trans_glue_frame_entry callsz spill;
+
+ let all_self_args_cell = caller_args_cell self_args_rty in
+ let self_indirect_args_cell =
+ get_element_ptr all_self_args_cell Abi.calltup_elt_indirect_args
+ in
+ let closure_cell =
+ deref (get_element_ptr self_indirect_args_cell
+ Abi.indirect_args_elt_closure)
+ in
+ let closure_target_cell =
+ get_element_ptr closure_cell Abi.binding_field_binding
+ in
+ let closure_target_fn_cell =
+ get_element_ptr closure_target_cell Abi.binding_field_item
+ in
+
+ merge_bound_args
+ self_args_rty callee_args_rty
+ arg_slots arg_bound_flags;
+ iflog (fun _ -> annotate "call through to closure target fn");
+
+ (*
+ * Closures, unlike first-class [disp,*binding] pairs, contain
+ * a fully-resolved target pointer, not a displacement. So we
+ * don't want to use callee_fn_ptr or the like to access the
+ * contents. We just call through the cell directly.
+ *)
+
+ call_code (code_of_cell closure_target_fn_cell);
+ trans_glue_frame_exit fix spill g
+
+
+ and get_fn_binding_glue
+ (bind_id:node_id)
+ (arg_slots:Ast.slot array)
+ (arg_bound_flags:bool array)
+ : fixup =
+ let g = GLUE_fn_binding bind_id in
+ match htab_search cx.ctxt_glue_code g with
+ Some code -> code.code_fixup
+ | None ->
+ let fix = new_fixup (glue_str cx g) in
+ emit_fn_binding_glue arg_slots arg_bound_flags fix g;
+ fix
+
+
+ (*
+ * Mem-glue functions are either 'mark', 'drop' or 'free', they take
+ * one pointer arg and return nothing.
+ *)
+
+ and trans_mem_glue_frame_entry (n_outgoing_args:int) (spill:fixup) : unit =
+ let isz = cx.ctxt_abi.Abi.abi_implicit_args_sz in
+ let callsz = SIZE_fixed (Int64.add isz (word_n n_outgoing_args)) in
+ trans_glue_frame_entry callsz spill
+
+ and get_mem_glue (g:glue) (inner:Il.mem -> unit) : fixup =
+ match htab_search cx.ctxt_glue_code g with
+ Some code -> code.code_fixup
+ | None ->
+ begin
+ let name = glue_str cx g in
+ let fix = new_fixup name in
+ (*
+ * Put a temporary code entry in the table to handle
+ * recursive emit calls during the generation of the glue
+ * function.
+ *)
+ let tmp_code = { code_fixup = fix;
+ code_quads = [| |];
+ code_vregs_and_spill = None; } in
+ let spill = new_fixup (name ^ " spill") in
+ htab_put cx.ctxt_glue_code g tmp_code;
+ log cx "emitting glue: %s" name;
+ trans_mem_glue_frame_entry Abi.worst_case_glue_call_args spill;
+ let (arg:Il.mem) = fp_imm arg0_disp in
+ inner arg;
+ Hashtbl.remove cx.ctxt_glue_code g;
+ trans_glue_frame_exit fix spill g;
+ fix
+ end
+
+ and get_typed_mem_glue
+ (g:glue)
+ (fty:Ast.ty)
+ (inner:Il.cell -> Il.cell -> unit)
+ : fixup =
+ get_mem_glue g
+ begin
+ fun _ ->
+ let n_ty_params = 0 in
+ let calltup_rty =
+ call_args_referent_type cx n_ty_params fty None
+ in
+ let calltup_cell = caller_args_cell calltup_rty in
+ let out_cell =
+ get_element_ptr calltup_cell Abi.calltup_elt_out_ptr
+ in
+ let args_cell =
+ get_element_ptr calltup_cell Abi.calltup_elt_args
+ in
+ begin
+ match Il.cell_referent_ty args_cell with
+ Il.StructTy az ->
+ assert ((Array.length az)
+ <= Abi.worst_case_glue_call_args);
+ | _ -> bug () "unexpected cell referent ty in glue args"
+ end;
+ inner out_cell args_cell
+ end
+
+ and trace_str b s =
+ if b
+ then
+ begin
+ let static = trans_static_string s in
+ trans_void_upcall "upcall_trace_str" [| static |]
+ end
+
+ and trace_word b w =
+ if b
+ then
+ trans_void_upcall "upcall_trace_word" [| Il.Cell w |]
+
+ and ty_params_covering (t:Ast.ty) : Ast.slot =
+ let n_ty_params = n_used_type_params t in
+ let params = make_tydesc_slots n_ty_params in
+ read_alias_slot (Ast.TY_tup params)
+
+ and get_drop_glue
+ (ty:Ast.ty)
+ (curr_iso:Ast.ty_iso option)
+ : fixup =
+ let g = GLUE_drop ty in
+ let inner _ (args:Il.cell) =
+ let ty_params = deref (get_element_ptr args 0) in
+ let cell = get_element_ptr args 1 in
+ note_drop_step ty "in drop-glue, dropping";
+ trace_word cx.ctxt_sess.Session.sess_trace_drop cell;
+ drop_ty ty_params ty (deref cell) curr_iso;
+ note_drop_step ty "drop-glue complete";
+ in
+ let ty_params_ptr = ty_params_covering ty in
+ let fty = mk_simple_ty_fn [| ty_params_ptr; read_alias_slot ty |] in
+ get_typed_mem_glue g fty inner
+
+
+ and get_free_glue
+ (ty:Ast.ty)
+ (mctrl:mem_ctrl)
+ (curr_iso:Ast.ty_iso option)
+ : fixup =
+ let g = GLUE_free ty in
+ let inner _ (args:Il.cell) =
+ (*
+ * Free-glue assumes it's called with a pointer to an
+ * exterior allocation with normal exterior layout. It's
+ * just a way to move drop+free out of leaf code.
+ *)
+ let ty_params = deref (get_element_ptr args 0) in
+ let cell = get_element_ptr args 1 in
+ let (body_mem, _) =
+ need_mem_cell
+ (get_element_ptr_dyn ty_params (deref cell)
+ Abi.exterior_rc_slot_field_body)
+ in
+ let vr = next_vreg_cell Il.voidptr_t in
+ lea vr body_mem;
+ note_drop_step ty "in free-glue, calling drop-glue on body";
+ trace_word cx.ctxt_sess.Session.sess_trace_drop vr;
+ trans_call_simple_static_glue
+ (get_drop_glue ty curr_iso) ty_params vr;
+ note_drop_step ty "back in free-glue, calling free";
+ if type_has_state ty
+ then
+ note_drop_step ty "type has state"
+ else
+ note_drop_step ty "type has no state";
+ if mctrl = MEM_gc
+ then
+ begin
+ note_drop_step ty "MEM_gc, adjusting pointer";
+ lea vr (fst (need_mem_cell (deref cell)));
+ emit (Il.binary Il.SUB vr (Il.Cell vr)
+ (imm
+ (word_n Abi.exterior_gc_malloc_return_adjustment)));
+ trans_free vr
+ end
+ else
+ begin
+ note_drop_step ty "not MEM_gc";
+ trans_free cell;
+ end;
+ trace_str cx.ctxt_sess.Session.sess_trace_drop
+ "free-glue complete";
+ in
+ let ty_params_ptr = ty_params_covering ty in
+ let fty = mk_simple_ty_fn [| ty_params_ptr; exterior_slot ty |] in
+ get_typed_mem_glue g fty inner
+
+
+ and get_mark_glue
+ (ty:Ast.ty)
+ (curr_iso:Ast.ty_iso option)
+ : fixup =
+ let g = GLUE_mark ty in
+ let inner _ (args:Il.cell) =
+ let ty_params = deref (get_element_ptr args 0) in
+ let cell = get_element_ptr args 1 in
+ mark_ty ty_params ty (deref cell) curr_iso
+ in
+ let ty_params_ptr = ty_params_covering ty in
+ let fty = mk_simple_ty_fn [| ty_params_ptr; read_alias_slot ty |] in
+ get_typed_mem_glue g fty inner
+
+
+ and get_clone_glue
+ (ty:Ast.ty)
+ (curr_iso:Ast.ty_iso option)
+ : fixup =
+ let g = GLUE_clone ty in
+ let inner (out_ptr:Il.cell) (args:Il.cell) =
+ let dst = deref out_ptr in
+ let ty_params = deref (get_element_ptr args 0) in
+ let src = deref (get_element_ptr args 1) in
+ let clone_task = get_element_ptr args 2 in
+ clone_ty ty_params clone_task ty dst src curr_iso
+ in
+ let ty_params_ptr = ty_params_covering ty in
+ let fty =
+ mk_ty_fn
+ (interior_slot ty) (* dst *)
+ [|
+ ty_params_ptr;
+ read_alias_slot ty; (* src *)
+ word_slot (* clone-task *)
+ |]
+ in
+ get_typed_mem_glue g fty inner
+
+
+ and get_copy_glue
+ (ty:Ast.ty)
+ (curr_iso:Ast.ty_iso option)
+ : fixup =
+ let g = GLUE_copy ty in
+ let inner (out_ptr:Il.cell) (args:Il.cell) =
+ let dst = deref out_ptr in
+ let ty_params = deref (get_element_ptr args 0) in
+ let src = deref (get_element_ptr args 1) in
+ copy_ty ty_params ty dst src curr_iso
+ in
+ let ty_params_ptr = ty_params_covering ty in
+ let fty =
+ mk_ty_fn
+ (interior_slot ty)
+ [| ty_params_ptr; read_alias_slot ty |]
+ in
+ get_typed_mem_glue g fty inner
+
+
+ (* Glue functions use mostly the same calling convention as ordinary
+ * functions.
+ *
+ * Each glue function expects its own particular arguments, which are
+ * usually aliases-- ie, caller doesn't transfer ownership to the
+ * glue. And nothing is represented in terms of AST nodes. So we
+ * don't do lvals-and-atoms here.
+ *)
+
+ and trans_call_glue
+ (code:Il.code)
+ (dst:Il.cell option)
+ (args:Il.cell array)
+ : unit =
+ let inner dst =
+ let scratch = next_vreg_cell Il.voidptr_t in
+ let pop _ = emit (Il.Pop scratch) in
+ for i = ((Array.length args) - 1) downto 0
+ do
+ emit (Il.Push (Il.Cell args.(i)))
+ done;
+ emit (Il.Push (Il.Cell abi.Abi.abi_tp_cell));
+ emit (Il.Push dst);
+ call_code code;
+ pop ();
+ pop ();
+ Array.iter (fun _ -> pop()) args;
+ in
+ match dst with
+ None -> inner zero
+ | Some dst -> aliasing true dst (fun dst -> inner (Il.Cell dst))
+
+ and trans_call_static_glue
+ (callee:Il.operand)
+ (dst:Il.cell option)
+ (args:Il.cell array)
+ : unit =
+ trans_call_glue (code_of_operand callee) dst args
+
+ and trans_call_dynamic_glue
+ (tydesc:Il.cell)
+ (idx:int)
+ (dst:Il.cell option)
+ (args:Il.cell array)
+ : unit =
+ let fptr = get_vtbl_entry_idx tydesc idx in
+ trans_call_glue (code_of_operand (Il.Cell fptr)) dst args
+
+ and trans_call_simple_static_glue
+ (fix:fixup)
+ (ty_params:Il.cell)
+ (arg:Il.cell)
+ : unit =
+ trans_call_static_glue
+ (code_fixup_to_ptr_operand fix)
+ None [| alias ty_params; arg |]
+
+ and get_tydesc_params
+ (outer_ty_params:Il.cell)
+ (td:Il.cell)
+ : Il.cell =
+ let first_param =
+ get_element_ptr (deref td) Abi.tydesc_field_first_param
+ in
+ let res = next_vreg_cell Il.voidptr_t in
+ mov res (Il.Cell (alias outer_ty_params));
+ emit (Il.cmp (Il.Cell first_param) zero);
+ let no_param_jmp = mark() in
+ emit (Il.jmp Il.JE Il.CodeNone);
+ mov res (Il.Cell first_param);
+ patch no_param_jmp;
+ res
+
+ and trans_call_simple_dynamic_glue
+ (ty_param:int)
+ (vtbl_idx:int)
+ (ty_params:Il.cell)
+ (arg:Il.cell)
+ : unit =
+ iflog (fun _ ->
+ annotate (Printf.sprintf "calling tydesc[%d].glue[%d]"
+ ty_param vtbl_idx));
+ let td = get_ty_param ty_params ty_param in
+ let ty_params_ptr = get_tydesc_params ty_params td in
+ trans_call_dynamic_glue
+ td vtbl_idx
+ None [| ty_params_ptr; arg; |]
+
+ (* trans_compare returns a quad number of the cjmp, which the caller
+ patches to the cjmp destination. *)
+ and trans_compare
+ (cjmp:Il.jmpop)
+ (lhs:Il.operand)
+ (rhs:Il.operand)
+ : quad_idx list =
+ (* FIXME: this is an x86-ism; abstract via ABI. *)
+ emit (Il.cmp (Il.Cell (Il.Reg (force_to_reg lhs))) rhs);
+ let jmp = mark() in
+ emit (Il.jmp cjmp Il.CodeNone);
+ [jmp]
+
+ and trans_cond (invert:bool) (expr:Ast.expr) : quad_idx list =
+
+ let anno _ =
+ iflog
+ begin
+ fun _ ->
+ annotate ((Ast.fmt_to_str Ast.fmt_expr expr) ^
+ ": cond, finale")
+ end
+ in
+
+ match expr with
+ Ast.EXPR_binary (binop, a, b) ->
+ let lhs = trans_atom a in
+ let rhs = trans_atom b in
+ let cjmp = binop_to_jmpop binop in
+ let cjmp' =
+ if invert then
+ match cjmp with
+ Il.JE -> Il.JNE
+ | Il.JNE -> Il.JE
+ | Il.JL -> Il.JGE
+ | Il.JLE -> Il.JG
+ | Il.JGE -> Il.JL
+ | Il.JG -> Il.JLE
+ | _ -> bug () "Unhandled inverse binop in trans_cond"
+ else
+ cjmp
+ in
+ anno ();
+ trans_compare cjmp' lhs rhs
+
+ | _ ->
+ let bool_operand = trans_expr expr in
+ anno ();
+ trans_compare Il.JNE bool_operand
+ (if invert then imm_true else imm_false)
+
+ and trans_binop (binop:Ast.binop) : Il.binop =
+ match binop with
+ Ast.BINOP_or -> Il.OR
+ | Ast.BINOP_and -> Il.AND
+ | Ast.BINOP_xor -> Il.XOR
+
+ | Ast.BINOP_lsl -> Il.LSL
+ | Ast.BINOP_lsr -> Il.LSR
+ | Ast.BINOP_asr -> Il.ASR
+
+ | Ast.BINOP_add -> Il.ADD
+ | Ast.BINOP_sub -> Il.SUB
+
+ (* FIXME (issue #57):
+ * switch on type of operands, IMUL/IDIV/IMOD etc.
+ *)
+ | Ast.BINOP_mul -> Il.UMUL
+ | Ast.BINOP_div -> Il.UDIV
+ | Ast.BINOP_mod -> Il.UMOD
+ | _ -> bug () "bad binop to Trans.trans_binop"
+
+ and trans_binary
+ (binop:Ast.binop)
+ (lhs:Il.operand)
+ (rhs:Il.operand) : Il.operand =
+ let arith op =
+ let bits = Il.operand_bits word_bits lhs in
+ let dst = Il.Reg (Il.next_vreg (emitter()), Il.ValTy bits) in
+ emit (Il.binary op dst lhs rhs);
+ Il.Cell dst
+ in
+ match binop with
+ Ast.BINOP_or | Ast.BINOP_and | Ast.BINOP_xor
+ | Ast.BINOP_lsl | Ast.BINOP_lsr | Ast.BINOP_asr
+ | Ast.BINOP_add | Ast.BINOP_sub
+ (* FIXME (issue #57):
+ * switch on type of operands, IMUL/IDIV/IMOD etc.
+ *)
+ | Ast.BINOP_mul | Ast.BINOP_div | Ast.BINOP_mod ->
+ arith (trans_binop binop)
+
+ | _ -> let dst = Il.Reg (Il.next_vreg (emitter()), Il.ValTy Il.Bits8) in
+ mov dst imm_true;
+ let jmps = trans_compare (binop_to_jmpop binop) lhs rhs in
+ mov dst imm_false;
+ List.iter patch jmps;
+ Il.Cell dst
+
+
+ and trans_expr (expr:Ast.expr) : Il.operand =
+
+ let anno _ =
+ iflog
+ begin
+ fun _ ->
+ annotate ((Ast.fmt_to_str Ast.fmt_expr expr) ^
+ ": plain exit, finale")
+ end
+ in
+ match expr with
+ Ast.EXPR_binary (binop, a, b) ->
+ assert (is_prim_type (atom_type cx a));
+ assert (is_prim_type (atom_type cx b));
+ trans_binary binop (trans_atom a) (trans_atom b)
+
+ | Ast.EXPR_unary (unop, a) ->
+ assert (is_prim_type (atom_type cx a));
+ let src = trans_atom a in
+ let bits = Il.operand_bits word_bits src in
+ let dst = Il.Reg (Il.next_vreg (emitter()), Il.ValTy bits) in
+ let op = match unop with
+ Ast.UNOP_not
+ | Ast.UNOP_bitnot -> Il.NOT
+ | Ast.UNOP_neg -> Il.NEG
+ | Ast.UNOP_cast t ->
+ let t = Hashtbl.find cx.ctxt_all_cast_types t.id in
+ let at = atom_type cx a in
+ if (type_is_2s_complement at) &&
+ (type_is_2s_complement t)
+ then
+ if type_is_unsigned_2s_complement t
+ then Il.UMOV
+ else Il.IMOV
+ else
+ err None "unsupported cast operator"
+ in
+ anno ();
+ emit (Il.unary op dst src);
+ Il.Cell dst
+
+ | Ast.EXPR_atom a ->
+ trans_atom a
+
+ and trans_block (block:Ast.block) : unit =
+ trace_str cx.ctxt_sess.Session.sess_trace_block
+ "entering block";
+ push_emitter_size_cache ();
+ emit (Il.Enter (Hashtbl.find cx.ctxt_block_fixups block.id));
+ Array.iter trans_stmt block.node;
+ trace_str cx.ctxt_sess.Session.sess_trace_block
+ "exiting block";
+ emit Il.Leave;
+ pop_emitter_size_cache ();
+ trace_str cx.ctxt_sess.Session.sess_trace_block
+ "exited block";
+
+ and upcall_fixup (name:string) : fixup =
+ Semant.require_native cx REQUIRED_LIB_rustrt name;
+
+ and trans_upcall
+ (name:string)
+ (ret:Il.cell)
+ (args:Il.operand array)
+ : unit =
+ abi.Abi.abi_emit_native_call (emitter())
+ ret nabi_rust (upcall_fixup name) args;
+
+ and trans_void_upcall
+ (name:string)
+ (args:Il.operand array)
+ : unit =
+ abi.Abi.abi_emit_native_void_call (emitter())
+ nabi_rust (upcall_fixup name) args;
+
+ and trans_log_int (a:Ast.atom) : unit =
+ trans_void_upcall "upcall_log_int" [| (trans_atom a) |]
+
+ and trans_log_str (a:Ast.atom) : unit =
+ trans_void_upcall "upcall_log_str" [| (trans_atom a) |]
+
+ and trans_spawn
+ ((*initializing*)_:bool)
+ (dst:Ast.lval)
+ (domain:Ast.domain)
+ (fn_lval:Ast.lval)
+ (args:Ast.atom array)
+ : unit =
+ let (task_cell, _) = trans_lval_init dst in
+ let (fptr_operand, fn_ty) = trans_callee fn_lval in
+ (*let fn_ty_params = [| |] in*)
+ let _ =
+ (* FIXME: handle indirect-spawns (clone closure). *)
+ if not (lval_is_direct_fn cx fn_lval)
+ then bug () "unhandled indirect-spawn"
+ in
+ let args_rty = call_args_referent_type cx 0 fn_ty None in
+ let fptr_operand = reify_ptr fptr_operand in
+ let exit_task_glue_fixup = get_exit_task_glue () in
+ let callsz =
+ calculate_sz_in_current_frame (Il.referent_ty_size word_bits args_rty)
+ in
+ let exit_task_glue_fptr =
+ code_fixup_to_ptr_operand exit_task_glue_fixup
+ in
+ let exit_task_glue_fptr = reify_ptr exit_task_glue_fptr in
+
+ iflog (fun _ -> annotate "spawn task: copy args");
+
+ let new_task = next_vreg_cell Il.voidptr_t in
+ let call = { call_ctrl = CALL_indirect;
+ call_callee_ptr = fptr_operand;
+ call_callee_ty = fn_ty;
+ call_callee_ty_params = [| |];
+ call_output = task_cell;
+ call_args = args;
+ call_iterator_args = [| |];
+ call_indirect_args = [| |] }
+ in
+ match domain with
+ Ast.DOMAIN_thread ->
+ begin
+ trans_upcall "upcall_new_thread" new_task [| |];
+ copy_fn_args false (CLONE_all new_task) call;
+ trans_upcall "upcall_start_thread" task_cell
+ [|
+ Il.Cell new_task;
+ exit_task_glue_fptr;
+ fptr_operand;
+ callsz
+ |];
+ end
+ | _ ->
+ begin
+ trans_upcall "upcall_new_task" new_task [| |];
+ copy_fn_args false (CLONE_chan new_task) call;
+ trans_upcall "upcall_start_task" task_cell
+ [|
+ Il.Cell new_task;
+ exit_task_glue_fptr;
+ fptr_operand;
+ callsz
+ |];
+ end;
+ ()
+
+ and get_curr_span _ =
+ if Stack.is_empty curr_stmt
+ then ("<none>", 0, 0)
+ else
+ let stmt_id = Stack.top curr_stmt in
+ match (Session.get_span cx.ctxt_sess stmt_id) with
+ None -> ("<none>", 0, 0)
+ | Some sp -> sp.lo
+
+ and trans_cond_fail (str:string) (fwd_jmps:quad_idx list) : unit =
+ let (filename, line, _) = get_curr_span () in
+ iflog (fun _ -> annotate ("condition-fail: " ^ str));
+ trans_void_upcall "upcall_fail"
+ [|
+ trans_static_string str;
+ trans_static_string filename;
+ imm (Int64.of_int line)
+ |];
+ List.iter patch fwd_jmps
+
+ and trans_check_expr (e:Ast.expr) : unit =
+ let fwd_jmps = trans_cond false e in
+ trans_cond_fail (Ast.fmt_to_str Ast.fmt_expr e) fwd_jmps
+
+ and trans_malloc (dst:Il.cell) (nbytes:Il.operand) : unit =
+ trans_upcall "upcall_malloc" dst [| nbytes |]
+
+ and trans_free (src:Il.cell) : unit =
+ trans_void_upcall "upcall_free" [| Il.Cell src |]
+
+ and trans_yield () : unit =
+ trans_void_upcall "upcall_yield" [| |];
+
+ and trans_fail () : unit =
+ let (filename, line, _) = get_curr_span () in
+ trans_void_upcall "upcall_fail"
+ [|
+ trans_static_string "explicit failure";
+ trans_static_string filename;
+ imm (Int64.of_int line)
+ |];
+
+ and trans_join (task:Ast.lval) : unit =
+ 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
+ begin
+ fun src_alias ->
+ trans_void_upcall "upcall_send"
+ [| trans_atom (Ast.ATOM_lval chan);
+ Il.Cell src_alias |];
+ end
+
+ and trans_recv (initializing:bool) (dst:Ast.lval) (chan:Ast.lval) : unit =
+ let (dstcell, _) = trans_lval_maybe_init initializing dst in
+ aliasing true dstcell
+ begin
+ fun dst_alias ->
+ trans_void_upcall "upcall_recv"
+ [| Il.Cell dst_alias;
+ trans_atom (Ast.ATOM_lval chan) |];
+ end
+
+ and trans_init_port (dst:Ast.lval) : unit =
+ let (dstcell, dst_slot) = trans_lval_init dst in
+ let unit_ty = match slot_ty dst_slot with
+ Ast.TY_port t -> t
+ | _ -> bug () "init dst of port-init has non-port type"
+ in
+ let unit_sz = ty_sz abi unit_ty in
+ trans_upcall "upcall_new_port" dstcell [| imm unit_sz |]
+
+ and trans_del_port (port:Il.cell) : unit =
+ trans_void_upcall "upcall_del_port" [| Il.Cell port |]
+
+ and trans_init_chan (dst:Ast.lval) (port:Ast.lval) : unit =
+ let (dstcell, _) = trans_lval_init dst
+ in
+ trans_upcall "upcall_new_chan" dstcell
+ [| trans_atom (Ast.ATOM_lval port) |]
+
+ and trans_del_chan (chan:Il.cell) : unit =
+ trans_void_upcall "upcall_del_chan" [| Il.Cell chan |]
+
+ and trans_kill_task (task:Il.cell) : unit =
+ trans_void_upcall "upcall_kill" [| Il.Cell task |]
+
+ (*
+ * A vec is implicitly exterior: every slot vec[T] is 1 word and
+ * points to a refcounted structure. That structure has 3 words with
+ * defined meaning at the beginning; data follows the header.
+ *
+ * word 0: refcount or gc control word
+ * word 1: allocated size of data
+ * word 2: initialised size of data
+ * word 3...N: data
+ *
+ * This 3-word prefix is shared with strings, we factor the common
+ * part out for reuse in string code.
+ *)
+
+ and trans_init_vec (dst:Ast.lval) (atoms:Ast.atom array) : unit =
+ let (dst_cell, dst_slot) = trans_lval_init dst in
+ let unit_slot = match slot_ty dst_slot with
+ Ast.TY_vec s -> s
+ | _ -> bug () "init dst of vec-init has non-vec type"
+ in
+ let fill = next_vreg_cell word_ty in
+ let unit_sz = slot_sz_in_current_frame unit_slot in
+ umul fill unit_sz (imm (Int64.of_int (Array.length atoms)));
+ trans_upcall "upcall_new_vec" dst_cell [| Il.Cell fill |];
+ let vec = deref dst_cell in
+ let body_mem =
+ fst (need_mem_cell
+ (get_element_ptr_dyn_in_current_frame
+ vec Abi.vec_elt_data))
+ in
+ let unit_rty = slot_referent_type abi unit_slot in
+ let body_rty = Il.StructTy (Array.map (fun _ -> unit_rty) atoms) in
+ let body = Il.Mem (body_mem, body_rty) in
+ Array.iteri
+ begin
+ fun i atom ->
+ let cell = get_element_ptr_dyn_in_current_frame body i in
+ trans_init_slot_from_atom CLONE_none cell unit_slot atom
+ end
+ atoms;
+ mov (get_element_ptr vec Abi.vec_elt_fill) (Il.Cell fill);
+
+ and get_dynamic_tydesc (idopt:node_id option) (t:Ast.ty) : Il.cell =
+ let td = next_vreg_cell Il.voidptr_t in
+ let root_desc =
+ Il.Cell (crate_rel_to_ptr
+ (get_static_tydesc idopt t 0L 0L)
+ (tydesc_rty abi))
+ 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 (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
+ let descs_ptr = next_vreg_cell Il.voidptr_t in
+ if (Array.length descs) > 0
+ then
+ (* FIXME: this relies on knowledge that spills are contiguous. *)
+ let spills =
+ Array.map (fun _ -> next_spill_cell Il.voidptr_t) descs
+ in
+ Array.iteri (fun i t -> mov spills.(n-(i+1)) t) descs;
+ lea descs_ptr (fst (need_mem_cell spills.(n-1)))
+ else
+ mov descs_ptr zero;
+ trans_upcall "upcall_get_type_desc" td
+ [| Il.Cell (curr_crate_ptr());
+ size; align; imm (Int64.of_int n);
+ Il.Cell descs_ptr |];
+ td
+
+ and get_tydesc (idopt:node_id option) (ty:Ast.ty) : Il.cell =
+ log cx "getting tydesc for %a" Ast.sprintf_ty ty;
+ match ty with
+ Ast.TY_param (idx, _) ->
+ (get_ty_param_in_current_frame idx)
+ | t when has_parametric_types t ->
+ (get_dynamic_tydesc idopt t)
+ | _ ->
+ (crate_rel_to_ptr (get_static_tydesc idopt ty
+ (ty_sz abi ty)
+ (ty_align abi ty))
+ (tydesc_rty abi))
+
+ and exterior_ctrl_cell (cell:Il.cell) (off:int) : Il.cell =
+ let (rc_mem, _) = need_mem_cell (deref_imm cell (word_n off)) in
+ word_at rc_mem
+
+ and exterior_rc_cell (cell:Il.cell) : Il.cell =
+ exterior_ctrl_cell cell Abi.exterior_rc_slot_field_refcnt
+
+ and exterior_gc_ctrl_cell (cell:Il.cell) : Il.cell =
+ exterior_ctrl_cell cell Abi.exterior_gc_slot_field_ctrl
+
+ and exterior_gc_next_cell (cell:Il.cell) : Il.cell =
+ exterior_ctrl_cell cell Abi.exterior_gc_slot_field_next
+
+ and exterior_allocation_size
+ (slot:Ast.slot)
+ : Il.operand =
+ let header_sz =
+ match slot_mem_ctrl slot with
+ MEM_gc -> word_n Abi.exterior_gc_header_size
+ | MEM_rc_opaque
+ | MEM_rc_struct -> word_n Abi.exterior_rc_header_size
+ | MEM_interior -> bug () "exterior_allocation_size of MEM_interior"
+ in
+ let t = slot_ty slot in
+ let refty_sz =
+ Il.referent_ty_size abi.Abi.abi_word_bits (referent_type abi t)
+ in
+ match refty_sz with
+ SIZE_fixed _ -> imm (Int64.add (ty_sz abi t) header_sz)
+ | _ ->
+ let ty_params = get_ty_params_of_current_frame() in
+ let refty_sz = calculate_sz ty_params refty_sz in
+ let v = next_vreg word_ty in
+ let vc = Il.Reg (v, word_ty) in
+ mov vc refty_sz;
+ add_to vc (imm header_sz);
+ Il.Cell vc;
+
+ and iter_tag_slots
+ (ty_params:Il.cell)
+ (dst_cell:Il.cell)
+ (src_cell:Il.cell)
+ (ttag:Ast.ty_tag)
+ (f:Il.cell -> Il.cell -> Ast.slot -> (Ast.ty_iso option) -> unit)
+ (curr_iso:Ast.ty_iso option)
+ : unit =
+ let tag_keys = sorted_htab_keys ttag in
+ let src_tag = get_element_ptr src_cell 0 in
+ let dst_tag = get_element_ptr dst_cell 0 in
+ let src_union = get_element_ptr_dyn ty_params src_cell 1 in
+ let dst_union = get_element_ptr_dyn ty_params dst_cell 1 in
+ let tmp = next_vreg_cell word_ty in
+ f dst_tag src_tag word_slot curr_iso;
+ mov tmp (Il.Cell src_tag);
+ Array.iteri
+ begin
+ fun i key ->
+ (iflog (fun _ ->
+ annotate (Printf.sprintf "tag case #%i == %a" i
+ Ast.sprintf_name key)));
+ let jmps =
+ trans_compare Il.JNE (Il.Cell tmp) (imm (Int64.of_int i))
+ in
+ let ttup = Hashtbl.find ttag key in
+ iter_tup_slots
+ (get_element_ptr_dyn ty_params)
+ (get_variant_ptr dst_union i)
+ (get_variant_ptr src_union i)
+ ttup f curr_iso;
+ List.iter patch jmps
+ end
+ tag_keys
+
+ and get_iso_tag tiso =
+ tiso.Ast.iso_group.(tiso.Ast.iso_index)
+
+
+ and seq_unit_slot (seq:Ast.ty) : Ast.slot =
+ match seq with
+ Ast.TY_vec s -> s
+ | Ast.TY_str -> (interior_slot (Ast.TY_mach TY_u8))
+ | _ -> bug () "seq_unit_slot of non-vec, non-str type"
+
+
+ and iter_seq_slots
+ (ty_params:Il.cell)
+ (dst_cell:Il.cell)
+ (src_cell:Il.cell)
+ (unit_slot:Ast.slot)
+ (f:Il.cell -> Il.cell -> Ast.slot -> (Ast.ty_iso option) -> unit)
+ (curr_iso:Ast.ty_iso option)
+ : unit =
+ let unit_sz = slot_sz_with_ty_params ty_params unit_slot in
+ (*
+ * Unlike most of the iter_ty_slots helpers; this one allocates a
+ * vreg and so has to be aware of when it's iterating over 2
+ * sequences of cells or just 1.
+ *)
+ check_exterior_rty src_cell;
+ check_exterior_rty dst_cell;
+ if dst_cell = src_cell
+ then
+ begin
+ let src_cell = deref src_cell in
+ let data =
+ get_element_ptr_dyn ty_params src_cell Abi.vec_elt_data
+ in
+ let len = get_element_ptr src_cell Abi.vec_elt_fill in
+ let ptr = next_vreg_cell Il.voidptr_t in
+ let lim = next_vreg_cell Il.voidptr_t in
+ lea lim (fst (need_mem_cell data));
+ mov ptr (Il.Cell lim);
+ add_to lim (Il.Cell len);
+ let back_jmp_target = mark () in
+ let fwd_jmps = trans_compare Il.JAE (Il.Cell ptr) (Il.Cell lim) in
+ let unit_cell =
+ deref (ptr_cast ptr (slot_referent_type abi unit_slot))
+ in
+ f unit_cell unit_cell unit_slot curr_iso;
+ add_to ptr unit_sz;
+ check_interrupt_flag ();
+ emit (Il.jmp Il.JMP (Il.CodeLabel back_jmp_target));
+ List.iter patch fwd_jmps;
+ end
+ else
+ begin
+ bug () "Unsupported form of seq iter: src != dst."
+ end
+
+
+ and iter_ty_slots_full
+ (ty_params:Il.cell)
+ (ty:Ast.ty)
+ (dst_cell:Il.cell)
+ (src_cell:Il.cell)
+ (f:Il.cell -> Il.cell -> Ast.slot -> (Ast.ty_iso option) -> unit)
+ (curr_iso:Ast.ty_iso option)
+ : unit =
+ (*
+ * FIXME: this will require some reworking if we support
+ * rec, tag or tup slots that fit in a vreg. It requires
+ * addrs presently.
+ *)
+ match ty with
+ Ast.TY_rec entries ->
+ iter_rec_slots
+ (get_element_ptr_dyn ty_params) dst_cell src_cell
+ entries f curr_iso
+
+ | Ast.TY_tup slots ->
+ iter_tup_slots
+ (get_element_ptr_dyn ty_params) dst_cell src_cell
+ slots f curr_iso
+
+ | Ast.TY_tag tag ->
+ iter_tag_slots ty_params dst_cell src_cell tag f curr_iso
+
+ | Ast.TY_iso tiso ->
+ let ttag = get_iso_tag tiso in
+ iter_tag_slots ty_params dst_cell src_cell ttag f (Some tiso)
+
+ | Ast.TY_fn _
+ | Ast.TY_obj _ -> bug () "Attempting to iterate over fn/pred/obj slots"
+
+ | Ast.TY_vec _
+ | Ast.TY_str ->
+ let unit_slot = seq_unit_slot ty in
+ iter_seq_slots ty_params dst_cell src_cell unit_slot f curr_iso
+
+ | _ -> ()
+
+ (*
+ * This just calls iter_ty_slots_full with your cell as both src and
+ * dst, with an adaptor function that discards the dst slots of the
+ * parallel traversal and and calls your provided function on the
+ * passed-in src slots.
+ *)
+ and iter_ty_slots
+ (ty_params:Il.cell)
+ (ty:Ast.ty)
+ (cell:Il.cell)
+ (f:Il.cell -> Ast.slot -> (Ast.ty_iso option) -> unit)
+ (curr_iso:Ast.ty_iso option)
+ : unit =
+ iter_ty_slots_full ty_params ty cell cell
+ (fun _ src_cell slot curr_iso -> f src_cell slot curr_iso)
+ curr_iso
+
+ and drop_ty
+ (ty_params:Il.cell)
+ (ty:Ast.ty)
+ (cell:Il.cell)
+ (curr_iso:Ast.ty_iso option)
+ : unit =
+ match ty with
+ Ast.TY_param (i, _) ->
+ iflog (fun _ -> annotate
+ (Printf.sprintf "drop_ty: parametric drop %#d" i));
+ aliasing false cell
+ begin
+ fun cell ->
+ trans_call_simple_dynamic_glue
+ i Abi.tydesc_field_drop_glue ty_params cell
+ end
+
+ | Ast.TY_fn _ ->
+ begin
+ let binding = get_element_ptr cell Abi.binding_field_binding in
+ let null_jmp = null_check binding in
+ (* Drop non-null bindings. *)
+ (* FIXME (issue #58): this is completely wrong,
+ * need a second thunk that generates code to make
+ * use of a runtime type descriptor extracted from
+ * a binding tuple. For now this only works by
+ * accident.
+ *)
+ drop_slot ty_params binding
+ (exterior_slot Ast.TY_int) curr_iso;
+ patch null_jmp
+ end
+
+ | Ast.TY_obj _ ->
+ begin
+ let binding = get_element_ptr cell Abi.binding_field_binding in
+ let null_jmp = null_check binding in
+ let obj = deref binding in
+ let rc = get_element_ptr obj 0 in
+ let rc_jmp = drop_refcount_and_cmp rc in
+ let tydesc = get_element_ptr obj 1 in
+ let body = get_element_ptr obj 2 in
+ let ty_params =
+ get_element_ptr (deref tydesc) Abi.tydesc_field_first_param
+ in
+ let dtor =
+ get_element_ptr (deref tydesc) Abi.tydesc_field_obj_drop_glue
+ in
+ let null_dtor_jmp = null_check dtor in
+ (* Call any dtor, if present. *)
+ trans_call_dynamic_glue tydesc
+ Abi.tydesc_field_obj_drop_glue None [| binding |];
+ patch null_dtor_jmp;
+ (* Drop the body. *)
+ trans_call_dynamic_glue tydesc
+ Abi.tydesc_field_drop_glue None [| ty_params; alias body |];
+ trans_free binding;
+ mov binding zero;
+ patch rc_jmp;
+ patch null_jmp
+ end
+
+
+ | _ ->
+ iter_ty_slots ty_params ty cell (drop_slot ty_params) curr_iso
+
+ and mark_ty
+ (ty_params:Il.cell)
+ (ty:Ast.ty)
+ (cell:Il.cell)
+ (curr_iso:Ast.ty_iso option)
+ : unit =
+ match ty with
+ | Ast.TY_fn _
+ | Ast.TY_obj _ -> ()
+ | _ ->
+ iter_ty_slots ty_params ty cell (mark_slot ty_params) curr_iso
+
+ and clone_ty
+ (ty_params:Il.cell)
+ (clone_task:Il.cell)
+ (ty:Ast.ty)
+ (dst:Il.cell)
+ (src:Il.cell)
+ (curr_iso:Ast.ty_iso option)
+ : unit =
+ match ty with
+ Ast.TY_chan _ ->
+ trans_upcall "upcall_clone_chan" dst
+ [| (Il.Cell clone_task); (Il.Cell src) |]
+ | Ast.TY_task
+ | Ast.TY_port _
+ | _ when type_has_state ty
+ -> bug () "cloning mutable type"
+ | _ when i64_le (ty_sz abi ty) word_sz
+ -> mov dst (Il.Cell src)
+ | Ast.TY_fn _
+ | Ast.TY_obj _ -> ()
+ | _ ->
+ iter_ty_slots_full ty_params ty dst src
+ (clone_slot ty_params clone_task) curr_iso
+
+ and copy_ty
+ (ty_params:Il.cell)
+ (ty:Ast.ty)
+ (dst:Il.cell)
+ (src:Il.cell)
+ (curr_iso:Ast.ty_iso option)
+ : unit =
+ iflog (fun _ ->
+ annotate ("copy_ty: referent data of type " ^
+ (Ast.fmt_to_str Ast.fmt_ty ty)));
+ match ty with
+ Ast.TY_nil
+ | Ast.TY_bool
+ | Ast.TY_mach _
+ | Ast.TY_int
+ | Ast.TY_uint
+ | Ast.TY_native _
+ | Ast.TY_type
+ | Ast.TY_char ->
+ iflog
+ (fun _ -> annotate
+ (Printf.sprintf "copy_ty: simple mov (%Ld byte scalar)"
+ (ty_sz abi ty)));
+ mov dst (Il.Cell src)
+
+ | Ast.TY_param (i, _) ->
+ iflog
+ (fun _ -> annotate
+ (Printf.sprintf "copy_ty: parametric copy %#d" i));
+ aliasing false src
+ begin
+ fun src ->
+ let td = get_ty_param ty_params i in
+ let ty_params_ptr = get_tydesc_params ty_params td in
+ trans_call_dynamic_glue
+ td Abi.tydesc_field_copy_glue
+ (Some dst) [| ty_params_ptr; src; |]
+ end
+
+ | Ast.TY_fn _
+ | Ast.TY_obj _ ->
+ begin
+ let src_item = get_element_ptr src Abi.binding_field_item in
+ let dst_item = get_element_ptr dst Abi.binding_field_item in
+ let src_binding = get_element_ptr src Abi.binding_field_binding in
+ let dst_binding = get_element_ptr dst Abi.binding_field_binding in
+ mov dst_item (Il.Cell src_item);
+ let null_jmp = null_check src_binding in
+ (* Copy if we have a src binding. *)
+ (* FIXME (issue #58): this is completely wrong, call
+ * through to the binding's self-copy fptr. For now
+ * this only works by accident.
+ *)
+ trans_copy_slot ty_params true
+ dst_binding (exterior_slot Ast.TY_int)
+ src_binding (exterior_slot Ast.TY_int)
+ curr_iso;
+ patch null_jmp
+ end
+
+ | _ ->
+ iter_ty_slots_full ty_params ty dst src
+ (fun dst src slot curr_iso ->
+ trans_copy_slot ty_params true
+ dst slot src slot curr_iso)
+ curr_iso
+
+ and free_ty
+ (ty_params:Il.cell)
+ (ty:Ast.ty)
+ (cell:Il.cell)
+ (curr_iso:Ast.ty_iso option)
+ : unit =
+ match ty with
+ Ast.TY_port _ -> trans_del_port cell
+ | Ast.TY_chan _ -> trans_del_chan cell
+ | Ast.TY_task -> trans_kill_task cell
+ | Ast.TY_vec s ->
+ iter_seq_slots ty_params cell cell s
+ (fun _ src slot iso -> drop_slot ty_params src slot iso) curr_iso;
+ trans_free cell
+
+ | _ -> trans_free cell
+
+ and maybe_iso
+ (curr_iso:Ast.ty_iso option)
+ (t:Ast.ty)
+ : Ast.ty =
+ match (curr_iso, t) with
+ (Some iso, Ast.TY_idx n) ->
+ Ast.TY_iso { iso with Ast.iso_index = n }
+ | (None, Ast.TY_idx _) ->
+ bug () "TY_idx outside TY_iso"
+ | _ -> t
+
+ and maybe_enter_iso
+ (t:Ast.ty)
+ (curr_iso:Ast.ty_iso option)
+ : Ast.ty_iso option =
+ match t with
+ Ast.TY_iso tiso -> Some tiso
+ | _ -> curr_iso
+
+ and mark_slot
+ (ty_params:Il.cell)
+ (cell:Il.cell)
+ (slot:Ast.slot)
+ (curr_iso:Ast.ty_iso option)
+ : unit =
+ let ty = slot_ty slot in
+ match slot_mem_ctrl slot with
+ MEM_gc ->
+ note_gc_step slot "mark GC slot: check for null:";
+ emit (Il.cmp (Il.Cell cell) zero);
+ let null_cell_jump = mark () in
+ emit (Il.jmp Il.JE Il.CodeNone);
+ let gc_word = exterior_gc_ctrl_cell cell in
+ let tmp = next_vreg_cell Il.voidptr_t in
+ (* if this has been marked already, jump to exit.*)
+ note_gc_step slot "mark GC slot: check for mark:";
+ emit (Il.binary Il.AND tmp (Il.Cell gc_word) one);
+ let already_marked_jump = mark () in
+ emit (Il.jmp Il.JZ Il.CodeNone);
+ (* Set mark bit in allocation header. *)
+ note_gc_step slot "mark GC slot: mark:";
+ emit (Il.binary Il.OR gc_word (Il.Cell gc_word) one);
+ (* Iterate over exterior slots marking outgoing links. *)
+ log cx "slot rty: %s" (cell_str cell);
+ let (body_mem, _) =
+ need_mem_cell
+ (get_element_ptr (deref cell)
+ Abi.exterior_gc_slot_field_body)
+ in
+ let ty = maybe_iso curr_iso ty in
+ let curr_iso = maybe_enter_iso ty curr_iso in
+ lea tmp body_mem;
+ trans_call_simple_static_glue
+ (get_mark_glue ty curr_iso)
+ ty_params tmp;
+ patch null_cell_jump;
+ patch already_marked_jump;
+ note_gc_step slot "mark GC slot: done marking:";
+
+ | MEM_interior when type_is_structured ty ->
+ (iflog (fun _ ->
+ annotate ("mark interior slot " ^
+ (Ast.fmt_to_str Ast.fmt_slot slot))));
+ let (mem, _) = need_mem_cell cell in
+ let tmp = next_vreg_cell Il.voidptr_t in
+ let ty = maybe_iso curr_iso ty in
+ let curr_iso = maybe_enter_iso ty curr_iso in
+ lea tmp mem;
+ trans_call_simple_static_glue
+ (get_mark_glue ty curr_iso)
+ ty_params tmp
+
+ | _ -> ()
+
+ and check_exterior_rty cell =
+ match cell with
+ Il.Reg (_, Il.AddrTy (Il.StructTy fields))
+ | Il.Mem (_, Il.ScalarTy (Il.AddrTy (Il.StructTy fields)))
+ when (((Array.length fields) > 0) && (fields.(0) = word_rty)) -> ()
+ | _ -> bug ()
+ "expected plausibly-exterior cell, got %s"
+ (Il.string_of_referent_ty (Il.cell_referent_ty cell))
+
+ and clone_slot
+ (ty_params:Il.cell)
+ (clone_task:Il.cell)
+ (dst:Il.cell)
+ (src:Il.cell)
+ (dst_slot:Ast.slot)
+ (curr_iso:Ast.ty_iso option)
+ : unit =
+ let ty = slot_ty dst_slot in
+ match dst_slot.Ast.slot_mode with
+ Ast.MODE_exterior _ ->
+ let ty = maybe_iso curr_iso ty in
+ let curr_iso = maybe_enter_iso ty curr_iso in
+ let dst = deref_slot true dst dst_slot in
+ let glue_fix = get_clone_glue (slot_ty dst_slot) curr_iso in
+ trans_call_static_glue
+ (code_fixup_to_ptr_operand glue_fix)
+ (Some dst)
+ [| alias ty_params; src; clone_task |]
+
+ | Ast.MODE_alias _ -> bug () "cloning into alias slot"
+ | Ast.MODE_interior _ ->
+ clone_ty ty_params clone_task ty dst src curr_iso
+
+ and drop_slot_in_current_frame
+ (cell:Il.cell)
+ (slot:Ast.slot)
+ (curr_iso:Ast.ty_iso option)
+ : unit =
+ drop_slot (get_ty_params_of_current_frame()) cell slot curr_iso
+
+ and null_check (cell:Il.cell) : quad_idx =
+ emit (Il.cmp (Il.Cell cell) zero);
+ let j = mark() in
+ emit (Il.jmp Il.JE Il.CodeNone);
+ j
+
+ and drop_refcount_and_cmp (rc:Il.cell) : quad_idx =
+ iflog (fun _ -> annotate "drop refcount and maybe free");
+ emit (Il.binary Il.SUB rc (Il.Cell rc) one);
+ emit (Il.cmp (Il.Cell rc) zero);
+ let j = mark () in
+ emit (Il.jmp Il.JNE Il.CodeNone);
+ j
+
+ and drop_slot
+ (ty_params:Il.cell)
+ (cell:Il.cell)
+ (slot:Ast.slot)
+ (curr_iso:Ast.ty_iso option)
+ : unit =
+ let ty = slot_ty slot in
+ let ty = maybe_iso curr_iso ty in
+ let curr_iso = maybe_enter_iso ty curr_iso in
+ let slot = {slot with Ast.slot_ty = Some ty} in
+ let mctrl = slot_mem_ctrl slot in
+ match mctrl with
+ MEM_rc_opaque ->
+ (* Refcounted opaque objects we handle without glue functions. *)
+ let _ = check_exterior_rty cell in
+ let null_jmp = null_check cell in
+ let j = drop_refcount_and_cmp (exterior_rc_cell cell) in
+ free_ty ty_params ty cell curr_iso;
+ (* Null the slot out to prevent double-free if the frame
+ * unwinds.
+ *)
+ mov cell zero;
+ patch j;
+ patch null_jmp
+
+ | MEM_gc
+ | MEM_rc_struct ->
+ (* Refcounted "structured exterior" objects we handle via
+ * glue functions.
+ *)
+
+ (*
+ * 'GC memory' is treated similarly, just happens to have
+ * an extra couple cells on the front.
+ *)
+
+ (* FIXME (issue #25): check to see that the exterior has
+ * further exterior members; if it doesn't we can elide the
+ * call to the glue function. *)
+ let _ = check_exterior_rty cell in
+ let null_jmp = null_check cell in
+ let rc = exterior_rc_cell cell in
+ let _ = note_gc_step slot "dropping refcount on " in
+ let _ = trace_word cx.ctxt_sess.Session.sess_trace_gc rc in
+ let j = drop_refcount_and_cmp rc in
+ trans_call_simple_static_glue
+ (get_free_glue ty mctrl curr_iso)
+ ty_params cell;
+ (* Null the slot out to prevent double-free if the frame
+ * unwinds.
+ *)
+ mov cell zero;
+ patch j;
+ patch null_jmp
+
+ | MEM_interior when type_is_structured ty ->
+ (iflog (fun _ ->
+ annotate ("drop interior slot " ^
+ (Ast.fmt_to_str Ast.fmt_slot slot))));
+ let (mem, _) = need_mem_cell cell in
+ let vr = next_vreg_cell Il.voidptr_t in
+ lea vr mem;
+ trans_call_simple_static_glue
+ (get_drop_glue ty curr_iso)
+ ty_params vr
+
+ | MEM_interior ->
+ (* Interior allocation of all-interior value: free directly. *)
+ let ty = maybe_iso curr_iso ty in
+ drop_ty ty_params ty cell curr_iso
+
+ and note_drop_step ty step =
+ if cx.ctxt_sess.Session.sess_trace_drop ||
+ cx.ctxt_sess.Session.sess_log_trans
+ then
+ let slotstr = Ast.fmt_to_str Ast.fmt_ty ty in
+ let str = step ^ " " ^ slotstr in
+ begin
+ annotate str;
+ trace_str cx.ctxt_sess.Session.sess_trace_drop str
+ end
+
+ and note_gc_step slot step =
+ if cx.ctxt_sess.Session.sess_trace_gc ||
+ cx.ctxt_sess.Session.sess_log_trans
+ then
+ let mctrl_str =
+ match slot_mem_ctrl slot with
+ MEM_gc -> "MEM_gc"
+ | MEM_rc_struct -> "MEM_rc_struct"
+ | MEM_rc_opaque -> "MEM_rc_struct"
+ | MEM_interior -> "MEM_rc_struct"
+ in
+ let slotstr = Ast.fmt_to_str Ast.fmt_slot slot in
+ let str = step ^ " " ^ mctrl_str ^ " " ^ slotstr in
+ begin
+ annotate str;
+ trace_str cx.ctxt_sess.Session.sess_trace_gc str
+ end
+
+ (* Returns the offset of the slot-body in the initialized allocation. *)
+ and init_exterior_slot (cell:Il.cell) (slot:Ast.slot) : unit =
+ match slot_mem_ctrl slot with
+ MEM_gc ->
+ iflog (fun _ -> annotate "init GC exterior: malloc");
+ let sz = exterior_allocation_size slot in
+ (*
+ * Malloc and then immediately shift down to point to
+ * the pseudo-rc cell.
+ *)
+ note_gc_step slot "init GC exterior: malloc slot:";
+ trans_malloc cell sz;
+ add_to cell
+ (imm (word_n Abi.exterior_gc_malloc_return_adjustment));
+ note_gc_step slot "init GC exterior: load control word";
+ let ctrl = exterior_gc_ctrl_cell cell in
+ let tydesc = get_tydesc None (slot_ty slot) in
+ let rc = exterior_rc_cell cell in
+ note_gc_step slot "init GC exterior: set refcount";
+ mov rc one;
+ trace_word cx.ctxt_sess.Session.sess_trace_gc rc;
+ mov ctrl (Il.Cell tydesc);
+ note_gc_step slot "init GC exterior: load chain next-ptr";
+ let next = exterior_gc_next_cell cell in
+ let chain = tp_imm (word_n Abi.task_field_gc_alloc_chain) in
+ mov next (Il.Cell chain);
+ note_gc_step slot "init GC exterior: link GC mem to chain";
+ mov chain (Il.Cell cell);
+ note_gc_step slot "init GC exterior: done initializing"
+
+ | MEM_rc_opaque
+ | MEM_rc_struct ->
+ iflog (fun _ -> annotate "init RC exterior: malloc");
+ let sz = exterior_allocation_size slot in
+ trans_malloc cell sz;
+ iflog (fun _ -> annotate "init RC exterior: load refcount");
+ let rc = exterior_rc_cell cell in
+ mov rc one
+
+ | MEM_interior -> bug () "init_exterior_slot of MEM_interior"
+
+ and deref_slot
+ (initializing:bool)
+ (cell:Il.cell)
+ (slot:Ast.slot)
+ : Il.cell =
+ match slot.Ast.slot_mode with
+ Ast.MODE_interior _ ->
+ cell
+
+ | Ast.MODE_exterior _ ->
+ check_exterior_rty cell;
+ if initializing
+ then init_exterior_slot cell slot;
+ get_element_ptr_dyn_in_current_frame
+ (deref cell)
+ Abi.exterior_rc_slot_field_body
+
+ | Ast.MODE_alias _ ->
+ if initializing
+ then cell
+ else deref cell
+
+ and trans_copy_tup
+ (ty_params:Il.cell)
+ (initializing:bool)
+ (dst:Il.cell)
+ (src:Il.cell)
+ (slots:Ast.ty_tup)
+ : unit =
+ Array.iteri
+ begin
+ fun i slot ->
+ let sub_dst_cell = get_element_ptr_dyn ty_params dst i in
+ let sub_src_cell = get_element_ptr_dyn ty_params src i in
+ trans_copy_slot
+ ty_params initializing
+ sub_dst_cell slot sub_src_cell slot None
+ end
+ slots
+
+ and trans_copy_slot
+ (ty_params:Il.cell)
+ (initializing:bool)
+ (dst:Il.cell) (dst_slot:Ast.slot)
+ (src:Il.cell) (src_slot:Ast.slot)
+ (curr_iso:Ast.ty_iso option)
+ : unit =
+ let anno (weight:string) : unit =
+ iflog
+ begin
+ fun _ ->
+ annotate
+ (Printf.sprintf "%sweight copy: %a <- %a"
+ weight
+ Ast.sprintf_slot dst_slot
+ Ast.sprintf_slot src_slot)
+ end;
+ in
+ assert (slot_ty src_slot = slot_ty dst_slot);
+ match (slot_mem_ctrl src_slot,
+ slot_mem_ctrl dst_slot) with
+
+ | (MEM_rc_opaque, MEM_rc_opaque)
+ | (MEM_gc, MEM_gc)
+ | (MEM_rc_struct, MEM_rc_struct) ->
+ (* Lightweight copy: twiddle refcounts, move pointer. *)
+ anno "refcounted light";
+ add_to (exterior_rc_cell src) one;
+ if not initializing
+ then
+ drop_slot ty_params dst dst_slot None;
+ mov dst (Il.Cell src)
+
+ | _ ->
+ (* Heavyweight copy: duplicate 1 level of the referent. *)
+ anno "heavy";
+ trans_copy_slot_heavy ty_params initializing
+ dst dst_slot src src_slot curr_iso
+
+ (* NB: heavyweight copying here does not mean "producing a deep
+ * clone of the entire data tree rooted at the src operand". It means
+ * "replicating a single level of the tree".
+ *
+ * There is no general-recursion entailed in performing a heavy
+ * copy. There is only "one level" to each heavy copy call.
+ *
+ * In other words, this is a lightweight copy:
+ *
+ * [dstptr] <-copy- [srcptr]
+ * \ |
+ * \ |
+ * [some record.rc++]
+ * |
+ * [some other record]
+ *
+ * Whereas this is a heavyweight copy:
+ *
+ * [dstptr] <-copy- [srcptr]
+ * | |
+ * | |
+ * [some record] [some record]
+ * | |
+ * [some other record]
+ *
+ *)
+
+ and trans_copy_slot_heavy
+ (ty_params:Il.cell)
+ (initializing:bool)
+ (dst:Il.cell) (dst_slot:Ast.slot)
+ (src:Il.cell) (src_slot:Ast.slot)
+ (curr_iso:Ast.ty_iso option)
+ : unit =
+ assert (slot_ty src_slot = slot_ty dst_slot);
+ iflog (fun _ ->
+ annotate ("heavy copy: slot preparation"));
+
+ let ty = slot_ty src_slot in
+ let ty = maybe_iso curr_iso ty in
+ let curr_iso = maybe_enter_iso ty curr_iso in
+ let dst_slot = { dst_slot with Ast.slot_ty = Some ty } in
+ let src_slot = { src_slot with Ast.slot_ty = Some ty } in
+ let dst = deref_slot initializing dst dst_slot in
+ let src = deref_slot false src src_slot in
+ copy_ty ty_params ty dst src curr_iso
+
+ and trans_copy
+ (initializing:bool)
+ (dst:Ast.lval)
+ (src:Ast.expr)
+ : unit =
+ let (dst_cell, dst_slot) = trans_lval_maybe_init initializing dst in
+ match (slot_ty dst_slot, src) with
+ (Ast.TY_vec _,
+ Ast.EXPR_binary (Ast.BINOP_add,
+ Ast.ATOM_lval a, Ast.ATOM_lval b))
+ | (Ast.TY_str,
+ Ast.EXPR_binary (Ast.BINOP_add,
+ Ast.ATOM_lval a, Ast.ATOM_lval b)) ->
+ (*
+ * Translate str or vec
+ *
+ * s = a + b
+ *
+ * as
+ *
+ * s = a;
+ * s += b;
+ *)
+ let (a_cell, a_slot) = trans_lval a in
+ let (b_cell, b_slot) = trans_lval b in
+ trans_copy_slot
+ (get_ty_params_of_current_frame())
+ initializing dst_cell dst_slot
+ a_cell a_slot None;
+ trans_vec_append dst_cell dst_slot
+ (Il.Cell b_cell) (slot_ty b_slot)
+
+
+ | (Ast.TY_obj caller_obj_ty,
+ Ast.EXPR_unary (Ast.UNOP_cast t, a)) ->
+ let src_ty = atom_type cx a in
+ let _ = assert (not (is_prim_type (src_ty))) in
+ begin
+ let t = Hashtbl.find cx.ctxt_all_cast_types t.id in
+ let _ = assert (t = (Ast.TY_obj caller_obj_ty)) in
+ let callee_obj_ty =
+ match atom_type cx a with
+ Ast.TY_obj t -> t
+ | _ -> bug () "obj cast from non-obj type"
+ in
+ let src_cell = need_cell (trans_atom a) in
+ let src_slot = interior_slot src_ty in
+
+ (* FIXME: this is wrong. It treats the underlying obj-state
+ * as the same as the callee and simply substitutes the
+ * forwarding vtbl, which would be great if it had any way
+ * convey the callee vtbl to the forwarding functions. But it
+ * doesn't. Instead, we have to malloc a fresh 3-word
+ * refcounted obj to hold the callee's vtbl+state pair, copy
+ * that in as the state here.
+ *)
+ let _ =
+ trans_copy_slot (get_ty_params_of_current_frame())
+ initializing
+ dst_cell dst_slot
+ src_cell src_slot
+ in
+ let caller_vtbl_oper =
+ get_forwarding_vtbl caller_obj_ty callee_obj_ty
+ in
+ let caller_obj =
+ deref_slot initializing dst_cell dst_slot
+ in
+ let caller_vtbl =
+ get_element_ptr caller_obj Abi.binding_field_item
+ in
+ mov caller_vtbl caller_vtbl_oper
+ end
+
+ | (_, Ast.EXPR_binary _)
+ | (_, Ast.EXPR_unary _)
+ | (_, Ast.EXPR_atom (Ast.ATOM_literal _)) ->
+ (*
+ * Translations of these expr types yield vregs,
+ * so copy is just MOV into the lval.
+ *)
+ let src_operand = trans_expr src in
+ mov (deref_slot false dst_cell dst_slot) src_operand
+
+ | (_, Ast.EXPR_atom (Ast.ATOM_lval src_lval)) ->
+ if lval_is_direct_fn cx src_lval then
+ trans_copy_direct_fn dst_cell src_lval
+ else
+ (* Possibly-large structure copying *)
+ let (src_cell, src_slot) = trans_lval src_lval in
+ trans_copy_slot
+ (get_ty_params_of_current_frame())
+ initializing
+ dst_cell dst_slot
+ src_cell src_slot
+ None
+
+ and trans_copy_direct_fn
+ (dst_cell:Il.cell)
+ (flv:Ast.lval)
+ : unit =
+ let item = lval_item cx flv in
+ let fix = Hashtbl.find cx.ctxt_fn_fixups item.id in
+
+ let dst_pair_item_cell =
+ get_element_ptr dst_cell Abi.binding_field_item
+ in
+ let dst_pair_binding_cell =
+ get_element_ptr dst_cell Abi.binding_field_binding
+ in
+
+ mov dst_pair_item_cell (crate_rel_imm fix);
+ mov dst_pair_binding_cell zero
+
+
+ and trans_init_structural_from_atoms
+ (dst:Il.cell)
+ (dst_slots:Ast.slot array)
+ (atoms:Ast.atom array)
+ : unit =
+ Array.iteri
+ begin
+ fun i atom ->
+ trans_init_slot_from_atom
+ CLONE_none
+ (get_element_ptr_dyn_in_current_frame dst i)
+ dst_slots.(i)
+ atom
+ end
+ atoms
+
+ and trans_init_rec_update
+ (dst:Il.cell)
+ (dst_slots:Ast.slot array)
+ (trec:Ast.ty_rec)
+ (atab:(Ast.ident * Ast.mode * bool * Ast.atom) array)
+ (base:Ast.lval)
+ : unit =
+ Array.iteri
+ begin
+ fun i (fml_ident, _) ->
+ let fml_entry _ (act_ident, _, _, atom) =
+ if act_ident = fml_ident then Some atom else None
+ in
+ let slot = dst_slots.(i) in
+ match arr_search atab fml_entry with
+ Some atom ->
+ trans_init_slot_from_atom
+ CLONE_none
+ (get_element_ptr_dyn_in_current_frame dst i)
+ slot
+ atom
+ | None ->
+ let (src, _) = trans_lval base in
+ trans_copy_slot
+ (get_ty_params_of_current_frame()) true
+ (get_element_ptr_dyn_in_current_frame dst i) slot
+ (get_element_ptr_dyn_in_current_frame src i) slot
+ None
+ end
+ trec
+
+ and trans_init_slot_from_atom
+ (clone:clone_ctrl)
+ (dst:Il.cell) (dst_slot:Ast.slot)
+ (atom:Ast.atom)
+ : unit =
+ let is_alias_cell =
+ match dst_slot.Ast.slot_mode with
+ Ast.MODE_alias _ -> true
+ | _ -> false
+ in
+ match atom with
+ | Ast.ATOM_literal _ ->
+ let src = trans_atom atom in
+ if is_alias_cell
+ then
+ match clone with
+ CLONE_none ->
+ (*
+ * FIXME: this won't work on mutable aliases, it
+ * doesn't know to reload. Try something
+ * else.
+ *)
+ mov dst (Il.Cell (alias (Il.Mem (force_to_mem src))))
+ | _ ->
+ bug () "attempting to clone alias cell"
+ else
+ mov (deref_slot true dst dst_slot) src
+ | Ast.ATOM_lval src_lval ->
+ let (src, src_slot) = trans_lval src_lval in
+ trans_init_slot_from_cell clone dst dst_slot src src_slot
+
+ and trans_init_slot_from_cell
+ (clone:clone_ctrl)
+ (dst:Il.cell) (dst_slot:Ast.slot)
+ (src:Il.cell) (src_slot:Ast.slot)
+ : unit =
+ assert (slot_ty src_slot = slot_ty dst_slot);
+ let is_alias_cell =
+ match dst_slot.Ast.slot_mode with
+ Ast.MODE_alias _ -> true
+ | _ -> false
+ in
+ match clone with
+ CLONE_chan clone_task ->
+ let clone =
+ if (type_contains_chan (slot_ty src_slot))
+ then CLONE_all clone_task
+ else CLONE_none
+ in
+ trans_init_slot_from_cell clone dst dst_slot src src_slot
+ | CLONE_none ->
+ if is_alias_cell
+ then mov dst (Il.Cell (alias src))
+ else
+ trans_copy_slot
+ (get_ty_params_of_current_frame())
+ true dst dst_slot src src_slot None
+ | CLONE_all clone_task ->
+ if is_alias_cell
+ then bug () "attempting to clone alias cell"
+ else
+ clone_slot
+ (get_ty_params_of_current_frame())
+ clone_task dst src dst_slot None
+
+ and trans_be_fn
+ (cx:ctxt)
+ (dst_cell:Il.cell)
+ (flv:Ast.lval)
+ (ty_params:Ast.ty array)
+ (args:Ast.atom array)
+ : unit =
+ let (ptr, fn_ty) = trans_callee flv in
+ let cc = call_ctrl flv in
+ let call = { call_ctrl = cc;
+ call_callee_ptr = ptr;
+ call_callee_ty = fn_ty;
+ call_callee_ty_params = ty_params;
+ call_output = dst_cell;
+ call_args = args;
+ call_iterator_args = call_iterator_args None;
+ call_indirect_args = call_indirect_args flv cc }
+ in
+ (* FIXME: true if caller is object fn *)
+ let caller_is_closure = false in
+ log cx "trans_be_fn: %s call to lval %a"
+ (call_ctrl_string cc) Ast.sprintf_lval flv;
+ trans_be (fun () -> Ast.sprintf_lval () flv) caller_is_closure call
+
+ and trans_prepare_fn_call
+ (initializing:bool)
+ (cx:ctxt)
+ (dst_cell:Il.cell)
+ (flv:Ast.lval)
+ (ty_params:Ast.ty array)
+ (fco:for_each_ctrl option)
+ (args:Ast.atom array)
+ : Il.operand =
+ let (ptr, fn_ty) = trans_callee flv in
+ let cc = call_ctrl flv in
+ let call = { call_ctrl = cc;
+ call_callee_ptr = ptr;
+ call_callee_ty = fn_ty;
+ call_callee_ty_params = ty_params;
+ call_output = dst_cell;
+ call_args = args;
+ call_iterator_args = call_iterator_args fco;
+ call_indirect_args = call_indirect_args flv cc }
+ in
+ iflog
+ begin
+ fun _ ->
+ log cx "trans_prepare_fn_call: %s call to lval %a"
+ (call_ctrl_string cc) Ast.sprintf_lval flv;
+ log cx "lval type: %a" Ast.sprintf_ty fn_ty;
+ Array.iteri (fun i t -> log cx "ty param %d = %a"
+ i Ast.sprintf_ty t)
+ ty_params;
+ end;
+ trans_prepare_call initializing (fun () -> Ast.sprintf_lval () flv) call
+
+ and trans_call_pred_and_check
+ (constr:Ast.constr)
+ (flv:Ast.lval)
+ (args:Ast.atom array)
+ : unit =
+ let (ptr, fn_ty) = trans_callee flv in
+ let dst_cell = Il.Mem (force_to_mem imm_false) in
+ let call = { call_ctrl = call_ctrl flv;
+ call_callee_ptr = ptr;
+ call_callee_ty = fn_ty;
+ call_callee_ty_params = [| |];
+ call_output = dst_cell;
+ call_args = args;
+ call_iterator_args = [| |];
+ call_indirect_args = [| |] }
+ in
+ iflog (fun _ -> annotate "predicate call");
+ let fn_ptr =
+ trans_prepare_call true (fun _ -> Ast.sprintf_lval () flv) call
+ in
+ call_code (code_of_operand fn_ptr);
+ iflog (fun _ -> annotate "predicate check/fail");
+ let jmp = trans_compare Il.JE (Il.Cell dst_cell) imm_true in
+ let errstr = Printf.sprintf "predicate check: %a"
+ Ast.sprintf_constr constr
+ in
+ trans_cond_fail errstr jmp
+
+ and trans_init_closure
+ (closure_cell:Il.cell)
+ (target_fn_ptr:Il.operand)
+ (target_binding_ptr:Il.operand)
+ (bound_arg_slots:Ast.slot array)
+ (bound_args:Ast.atom array)
+ : unit =
+
+ let rc_cell = get_element_ptr closure_cell 0 in
+ let targ_cell = get_element_ptr closure_cell 1 in
+ let args_cell = get_element_ptr closure_cell 2 in
+
+ iflog (fun _ -> annotate "init closure refcount");
+ mov rc_cell one;
+ iflog (fun _ -> annotate "set closure target code ptr");
+ mov (get_element_ptr targ_cell 0) (reify_ptr target_fn_ptr);
+ iflog (fun _ -> annotate "set closure target binding ptr");
+ mov (get_element_ptr targ_cell 1) (reify_ptr target_binding_ptr);
+
+ iflog (fun _ -> annotate "set closure bound args");
+ copy_bound_args args_cell bound_arg_slots bound_args
+
+ and trans_bind_fn
+ (initializing:bool)
+ (cc:call_ctrl)
+ (bind_id:node_id)
+ (dst:Ast.lval)
+ (flv:Ast.lval)
+ (fn_sig:Ast.ty_sig)
+ (args:Ast.atom option array)
+ : unit =
+ let (dst_cell, _) = trans_lval_maybe_init initializing dst in
+ let (target_ptr, _) = trans_callee flv in
+ let arg_bound_flags = Array.map bool_of_option args in
+ let arg_slots =
+ arr_map2
+ (fun arg_slot bound_flag ->
+ if bound_flag then Some arg_slot else None)
+ fn_sig.Ast.sig_input_slots
+ arg_bound_flags
+ in
+ let bound_arg_slots = arr_filter_some arg_slots in
+ let bound_args = arr_filter_some args in
+ let glue_fixup =
+ get_fn_binding_glue bind_id fn_sig.Ast.sig_input_slots arg_bound_flags
+ in
+ let target_fn_ptr = callee_fn_ptr target_ptr cc in
+ let target_binding_ptr = callee_binding_ptr flv cc in
+ let closure_rty = closure_referent_type bound_arg_slots in
+ let closure_sz = force_sz (Il.referent_ty_size word_bits closure_rty) in
+ let fn_cell = get_element_ptr dst_cell Abi.binding_field_item in
+ let closure_cell =
+ ptr_cast
+ (get_element_ptr dst_cell Abi.binding_field_binding)
+ (Il.ScalarTy (Il.AddrTy (closure_rty)))
+ in
+ iflog (fun _ -> annotate "assign glue-code to fn slot of pair");
+ mov fn_cell (crate_rel_imm glue_fixup);
+ iflog (fun _ ->
+ annotate "heap-allocate closure to binding slot of pair");
+ trans_malloc closure_cell (imm closure_sz);
+ trans_init_closure
+ (deref closure_cell)
+ target_fn_ptr target_binding_ptr
+ bound_arg_slots bound_args
+
+
+ and trans_arg0 (arg_cell:Il.cell) (output_cell:Il.cell) : unit =
+ (* Emit arg0 of any call: the output slot. *)
+ iflog (fun _ -> annotate "fn-call arg 0: output slot");
+ trans_init_slot_from_cell
+ CLONE_none
+ arg_cell (word_write_alias_slot abi)
+ output_cell word_slot
+
+ and trans_arg1 (arg_cell:Il.cell) : unit =
+ (* Emit arg1 of any call: the task pointer. *)
+ iflog (fun _ -> annotate "fn-call arg 1: task pointer");
+ trans_init_slot_from_cell
+ CLONE_none
+ arg_cell word_slot
+ abi.Abi.abi_tp_cell word_slot
+
+ and trans_argN
+ (clone:clone_ctrl)
+ (arg_cell:Il.cell)
+ (arg_slot:Ast.slot)
+ (arg:Ast.atom)
+ : unit =
+ trans_init_slot_from_atom clone arg_cell arg_slot arg
+
+ and code_of_cell (cell:Il.cell) : Il.code =
+ match cell with
+ Il.Mem (_, Il.ScalarTy (Il.AddrTy Il.CodeTy))
+ | Il.Reg (_, Il.AddrTy Il.CodeTy) -> Il.CodePtr (Il.Cell cell)
+ | _ ->
+ bug () "expected code-pointer cell, found %s"
+ (cell_str cell)
+
+ and code_of_operand (operand:Il.operand) : Il.code =
+ match operand with
+ Il.Cell c -> code_of_cell c
+ | Il.ImmPtr (_, Il.CodeTy) -> Il.CodePtr operand
+ | _ ->
+ bug () "expected code-pointer operand, got %s"
+ (oper_str operand)
+
+ and ty_arg_slots (ty:Ast.ty) : Ast.slot array =
+ match ty with
+ Ast.TY_fn (tsig, _) -> tsig.Ast.sig_input_slots
+ | _ -> bug () "Trans.ty_arg_slots on non-callable type: %a"
+ Ast.sprintf_ty ty
+
+ and copy_fn_args
+ (tail_area:bool)
+ (clone:clone_ctrl)
+ (call:call)
+ : unit =
+
+ let n_ty_params = Array.length call.call_callee_ty_params in
+ let all_callee_args_rty =
+ let clo =
+ if call.call_ctrl = CALL_direct
+ then None
+ else (Some Il.OpaqueTy)
+ in
+ call_args_referent_type cx n_ty_params call.call_callee_ty clo
+ in
+ let all_callee_args_cell =
+ callee_args_cell tail_area all_callee_args_rty
+ in
+
+ let _ = iflog (fun _ -> annotate
+ (Printf.sprintf
+ "copying fn args to %d-ty-param call with rty: %s\n"
+ n_ty_params (Il.string_of_referent_ty
+ all_callee_args_rty)))
+ in
+ let callee_arg_slots = ty_arg_slots call.call_callee_ty in
+ let callee_output_cell =
+ get_element_ptr all_callee_args_cell Abi.calltup_elt_out_ptr
+ in
+ let callee_task_cell =
+ get_element_ptr all_callee_args_cell Abi.calltup_elt_task_ptr
+ in
+ let callee_ty_params =
+ get_element_ptr all_callee_args_cell Abi.calltup_elt_ty_params
+ in
+ let callee_args =
+ get_element_ptr_dyn_in_current_frame
+ all_callee_args_cell Abi.calltup_elt_args
+ in
+ let callee_iterator_args =
+ get_element_ptr_dyn_in_current_frame
+ all_callee_args_cell Abi.calltup_elt_iterator_args
+ in
+ let callee_indirect_args =
+ get_element_ptr_dyn_in_current_frame
+ all_callee_args_cell Abi.calltup_elt_indirect_args
+ in
+
+ let n_args = Array.length call.call_args in
+ let n_iterators = Array.length call.call_iterator_args in
+ let n_indirects = Array.length call.call_indirect_args in
+
+ Array.iteri
+ begin
+ fun i arg_atom ->
+ iflog (fun _ ->
+ annotate
+ (Printf.sprintf "fn-call arg %d of %d (+ %d indirect)"
+ i n_args n_indirects));
+ trans_argN
+ clone
+ (get_element_ptr_dyn_in_current_frame callee_args i)
+ callee_arg_slots.(i)
+ arg_atom
+ end
+ call.call_args;
+
+ Array.iteri
+ begin
+ fun i iterator_arg_operand ->
+ iflog (fun _ ->
+ annotate (Printf.sprintf "fn-call iterator-arg %d of %d"
+ i n_iterators));
+ mov
+ (get_element_ptr_dyn_in_current_frame callee_iterator_args i)
+ iterator_arg_operand
+ end
+ call.call_iterator_args;
+
+ Array.iteri
+ begin
+ fun i indirect_arg_operand ->
+ iflog (fun _ ->
+ annotate (Printf.sprintf "fn-call indirect-arg %d of %d"
+ i n_indirects));
+ mov
+ (get_element_ptr_dyn_in_current_frame callee_indirect_args i)
+ indirect_arg_operand
+ end
+ call.call_indirect_args;
+
+ Array.iteri
+ begin
+ fun i ty_param ->
+ iflog (fun _ ->
+ annotate
+ (Printf.sprintf "fn-call ty param %d of %d"
+ i n_ty_params));
+ trans_init_slot_from_cell CLONE_none
+ (get_element_ptr callee_ty_params i) word_slot
+ (get_tydesc None ty_param) word_slot
+ end
+ call.call_callee_ty_params;
+
+ trans_arg1 callee_task_cell;
+
+ trans_arg0 callee_output_cell call.call_output
+
+
+
+ and call_code (code:Il.code) : unit =
+ let vr = next_vreg_cell Il.voidptr_t in
+ emit (Il.call vr code);
+
+
+ and copy_bound_args
+ (dst_cell:Il.cell)
+ (bound_arg_slots:Ast.slot array)
+ (bound_args:Ast.atom array)
+ : unit =
+ let n_slots = Array.length bound_arg_slots in
+ Array.iteri
+ begin
+ fun i slot ->
+ iflog (fun _ ->
+ annotate (Printf.sprintf
+ "copy bound arg %d of %d" i n_slots));
+ trans_argN CLONE_none
+ (get_element_ptr dst_cell i)
+ slot bound_args.(i)
+ end
+ bound_arg_slots
+
+ and merge_bound_args
+ (all_self_args_rty:Il.referent_ty)
+ (all_callee_args_rty:Il.referent_ty)
+ (arg_slots:Ast.slot array)
+ (arg_bound_flags:bool array)
+ : unit =
+ begin
+ (*
+ * NB: 'all_*_args', both self and callee, are always 4-tuples:
+ *
+ * [out_ptr, task_ptr, [args], [indirect_args]]
+ *
+ * The first few bindings here just destructure those via GEP.
+ *
+ *)
+ let all_self_args_cell = caller_args_cell all_self_args_rty in
+ let all_callee_args_cell = callee_args_cell false all_callee_args_rty in
+
+ let self_args_cell =
+ get_element_ptr all_self_args_cell Abi.calltup_elt_args
+ in
+ let self_ty_params_cell =
+ get_element_ptr all_self_args_cell Abi.calltup_elt_ty_params
+ in
+ let callee_args_cell =
+ get_element_ptr all_callee_args_cell Abi.calltup_elt_args
+ in
+ let self_indirect_args_cell =
+ get_element_ptr all_self_args_cell Abi.calltup_elt_indirect_args
+ in
+
+ let n_args = Array.length arg_bound_flags in
+ let bound_i = ref 0 in
+ let unbound_i = ref 0 in
+
+ iflog (fun _ -> annotate "copy out-ptr");
+ mov
+ (get_element_ptr all_callee_args_cell Abi.calltup_elt_out_ptr)
+ (Il.Cell (get_element_ptr all_self_args_cell
+ Abi.calltup_elt_out_ptr));
+
+ iflog (fun _ -> annotate "copy task-ptr");
+ mov
+ (get_element_ptr all_callee_args_cell Abi.calltup_elt_task_ptr)
+ (Il.Cell (get_element_ptr all_self_args_cell
+ Abi.calltup_elt_task_ptr));
+
+ iflog (fun _ -> annotate "extract closure indirect-arg");
+ let closure_cell =
+ deref (get_element_ptr self_indirect_args_cell
+ Abi.indirect_args_elt_closure)
+ in
+ let closure_args_cell = get_element_ptr closure_cell 2 in
+
+ for arg_i = 0 to (n_args - 1) do
+ let dst_cell = get_element_ptr callee_args_cell arg_i in
+ let slot = arg_slots.(arg_i) in
+ let is_bound = arg_bound_flags.(arg_i) in
+ let src_cell =
+ if is_bound then
+ begin
+ iflog (fun _ -> annotate
+ (Printf.sprintf
+ "extract bound arg %d as actual arg %d"
+ !bound_i arg_i));
+ get_element_ptr closure_args_cell (!bound_i);
+ end
+ else
+ begin
+ iflog (fun _ -> annotate
+ (Printf.sprintf
+ "extract unbound arg %d as actual arg %d"
+ !unbound_i arg_i));
+ get_element_ptr self_args_cell (!unbound_i);
+ end
+ in
+ iflog (fun _ -> annotate
+ (Printf.sprintf
+ "copy into actual-arg %d" arg_i));
+ trans_copy_slot
+ self_ty_params_cell
+ true dst_cell slot src_cell slot None;
+ incr (if is_bound then bound_i else unbound_i);
+ done;
+ assert ((!bound_i + !unbound_i) == n_args)
+ end
+
+
+ and callee_fn_ptr
+ (fptr:Il.operand)
+ (cc:call_ctrl)
+ : Il.operand =
+ match cc with
+ CALL_direct
+ | CALL_vtbl -> fptr
+ | CALL_indirect ->
+ (* fptr is a pair [disp, binding*] *)
+ let pair_cell = need_cell (reify_ptr fptr) in
+ let disp_cell = get_element_ptr pair_cell Abi.binding_field_item in
+ Il.Cell (crate_rel_to_ptr (Il.Cell disp_cell) Il.CodeTy)
+
+ and callee_binding_ptr
+ (pair_lval:Ast.lval)
+ (cc:call_ctrl)
+ : Il.operand =
+ if cc = CALL_direct
+ then zero
+ else
+ let (pair_cell, _) = trans_lval pair_lval in
+ Il.Cell (get_element_ptr pair_cell Abi.binding_field_binding)
+
+ and call_ctrl flv : call_ctrl =
+ if lval_is_static cx flv
+ then CALL_direct
+ else
+ if lval_is_obj_vtbl cx flv
+ then CALL_vtbl
+ else CALL_indirect
+
+ and call_ctrl_string cc =
+ match cc with
+ CALL_direct -> "direct"
+ | CALL_indirect -> "indirect"
+ | CALL_vtbl -> "vtbl"
+
+ and call_iterator_args
+ (fco:for_each_ctrl option)
+ : Il.operand array =
+ match fco with
+ None -> [| |]
+ | Some fco ->
+ begin
+ iflog (fun _ -> annotate "calculate iterator args");
+ [| reify_ptr (code_fixup_to_ptr_operand fco.for_each_fixup);
+ Il.Cell (Il.Reg (abi.Abi.abi_fp_reg, Il.voidptr_t)); |]
+ end
+
+ and call_indirect_args
+ (flv:Ast.lval)
+ (cc:call_ctrl)
+ : Il.operand array =
+ begin
+ match cc with
+ CALL_direct -> [| |]
+ | CALL_indirect -> [| callee_binding_ptr flv cc |]
+ | CALL_vtbl ->
+ begin
+ match flv with
+ (*
+ * FIXME: will need to pass both words of obj if we add
+ * a 'self' value for self-dispatch within objs.
+ *)
+ Ast.LVAL_ext (base, _) -> [| callee_binding_ptr base cc |]
+ | _ ->
+ bug (lval_base_id flv)
+ "call_indirect_args on obj-fn without base obj"
+ end
+ end
+
+ and trans_be
+ (logname:(unit -> string))
+ (caller_is_closure:bool)
+ (call:call)
+ : unit =
+ let callee_fptr = callee_fn_ptr call.call_callee_ptr call.call_ctrl in
+ let callee_code = code_of_operand callee_fptr in
+ let callee_args_rty =
+ call_args_referent_type cx 0 call.call_callee_ty
+ (if call.call_ctrl = CALL_direct then None else (Some Il.OpaqueTy))
+ in
+ let callee_argsz =
+ force_sz (Il.referent_ty_size word_bits callee_args_rty)
+ in
+ let closure_rty =
+ if caller_is_closure
+ then Some Il.OpaqueTy
+ else None
+ in
+ let caller_args_rty = current_fn_args_rty closure_rty in
+ let caller_argsz =
+ force_sz (Il.referent_ty_size word_bits caller_args_rty)
+ in
+ iflog (fun _ -> annotate
+ (Printf.sprintf "copy args for tail call to %s" (logname ())));
+ copy_fn_args true CLONE_none call;
+ drop_slots_at_curr_stmt();
+ abi.Abi.abi_emit_fn_tail_call (emitter())
+ (force_sz (current_fn_callsz()))
+ caller_argsz callee_code callee_argsz;
+
+
+ and trans_prepare_call
+ ((*initializing*)_:bool)
+ (logname:(unit -> string))
+ (call:call)
+ : Il.operand =
+
+ let callee_fptr = callee_fn_ptr call.call_callee_ptr call.call_ctrl in
+ iflog (fun _ -> annotate
+ (Printf.sprintf "copy args for call to %s" (logname ())));
+ copy_fn_args false CLONE_none call;
+ iflog (fun _ -> annotate (Printf.sprintf "call %s" (logname ())));
+ (* FIXME (issue #24): we need to actually handle writing to an
+ * already-initialised slot. Currently we blindly assume we're
+ * initializing, overwrite the slot; this is ok if we're writing
+ * to an interior output slot, but we'll leak any exteriors as we
+ * do that. *)
+ callee_fptr
+
+ and callee_drop_slot
+ (k:Ast.slot_key)
+ (slot_id:node_id)
+ (slot:Ast.slot)
+ : unit =
+ iflog (fun _ ->
+ annotate (Printf.sprintf "callee_drop_slot %d = %s "
+ (int_of_node slot_id)
+ (Ast.fmt_to_str Ast.fmt_slot_key k)));
+ drop_slot_in_current_frame (cell_of_block_slot slot_id) slot None
+
+
+ and trans_alt_tag { Ast.alt_tag_lval = lval; Ast.alt_tag_arms = arms } =
+ let ((lval_cell:Il.cell), { Ast.slot_ty = ty_opt }) = trans_lval lval in
+ let lval_ty =
+ match ty_opt with
+ Some ty -> ty
+ | None -> bug cx "expected lval type"
+ in
+
+ let trans_arm { node = (pat, block) } : quad_idx =
+ (* Translates the pattern and returns the addresses of the branch
+ * instructions, which are taken if the match fails. *)
+ let rec trans_pat pat cell (ty:Ast.ty) =
+ match pat with
+ Ast.PAT_lit lit ->
+ let operand = trans_lit lit in
+ emit (Il.cmp (Il.Cell cell) operand);
+ let next_jump = mark() in
+ emit (Il.jmp Il.JNE Il.CodeNone);
+ [ next_jump ]
+
+ | Ast.PAT_tag (ident, pats) ->
+ let ty_tag =
+ match ty with
+ Ast.TY_tag tag_ty -> tag_ty
+ | Ast.TY_iso ti -> (ti.Ast.iso_group).(ti.Ast.iso_index)
+ | _ -> bug cx "expected tag type"
+ in
+ let tag_keys = sorted_htab_keys ty_tag in
+ let tag_name = Ast.NAME_base (Ast.BASE_ident ident) in
+ let tag_number = arr_idx tag_keys tag_name in
+ let ty_tup = Hashtbl.find ty_tag tag_name in
+
+ let tag_cell:Il.cell = get_element_ptr cell 0 in
+ let union_cell = get_element_ptr_dyn_in_current_frame cell 1 in
+
+ emit (Il.cmp
+ (Il.Cell tag_cell)
+ (imm (Int64.of_int tag_number)));
+ let next_jump = mark() in
+ emit (Il.jmp Il.JNE Il.CodeNone);
+
+ let tup_cell:Il.cell = get_variant_ptr union_cell tag_number in
+
+ let trans_elem_pat i elem_pat : quad_idx list =
+ let elem_cell =
+ get_element_ptr_dyn_in_current_frame tup_cell i
+ in
+ let elem_ty =
+ match ty_tup.(i).Ast.slot_ty with
+ Some ty -> ty
+ | None -> bug cx "expected element type"
+ in
+ trans_pat elem_pat elem_cell elem_ty
+ in
+
+ let elem_jumps = Array.mapi trans_elem_pat pats in
+ next_jump::(List.concat (Array.to_list elem_jumps))
+
+ | Ast.PAT_slot ({ node = dst_slot; id = dst_id }, _) ->
+ let dst_cell = cell_of_block_slot dst_id in
+ let src_cell = Il.Cell cell in
+ mov (deref_slot true dst_cell dst_slot) src_cell;
+ [] (* irrefutable *)
+
+ | Ast.PAT_wild -> [] (* irrefutable *)
+ in
+
+ let next_jumps = trans_pat pat lval_cell lval_ty in
+ trans_block block;
+ let last_jump = mark() in
+ emit (Il.jmp Il.JMP Il.CodeNone);
+ List.iter patch next_jumps;
+ last_jump
+ in
+ let last_jumps = Array.map trans_arm arms in
+ Array.iter patch last_jumps
+
+ 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
+ None -> ()
+ | Some slots ->
+ List.iter
+ begin
+ fun slot_id ->
+ let slot = get_slot cx slot_id in
+ let k = Hashtbl.find cx.ctxt_slot_keys slot_id in
+ iflog (fun _ ->
+ annotate
+ (Printf.sprintf
+ "post-stmt, drop_slot %d = %s "
+ (int_of_node slot_id)
+ (Ast.fmt_to_str Ast.fmt_slot_key k)));
+ drop_slot_in_current_frame
+ (cell_of_block_slot slot_id) slot None
+ end
+ slots
+
+ and trans_stmt (stmt:Ast.stmt) : unit =
+ (* Helper to localize errors by stmt, at minimum. *)
+ try
+ iflog
+ begin
+ fun _ ->
+ let s = Ast.fmt_to_str Ast.fmt_stmt_body stmt in
+ log cx "translating stmt: %s" s;
+ annotate s;
+ end;
+ Stack.push stmt.id curr_stmt;
+ trans_stmt_full stmt;
+ begin
+ match stmt.node with
+ Ast.STMT_be _
+ | Ast.STMT_ret _ -> ()
+ | _ -> drop_slots_at_curr_stmt();
+ end;
+ ignore (Stack.pop curr_stmt);
+ with
+ Semant_err (None, msg) -> raise (Semant_err ((Some stmt.id), msg))
+
+
+ and maybe_init (id:node_id) (action:string) (dst:Ast.lval) : bool =
+ let b = Hashtbl.mem cx.ctxt_copy_stmt_is_init id in
+ let act = if b then ("initializing-" ^ action) else action in
+ iflog
+ (fun _ ->
+ annotate (Printf.sprintf "%s on dst lval %a"
+ act Ast.sprintf_lval dst));
+ b
+
+
+ and trans_set_outptr (at:Ast.atom) : unit =
+ let (dst_mem, _) =
+ need_mem_cell
+ (deref (wordptr_at (fp_imm out_mem_disp)))
+ in
+ let atom_ty = atom_type cx at in
+ let dst_slot = interior_slot atom_ty in
+ let dst_ty = referent_type abi atom_ty in
+ let dst_cell = Il.Mem (dst_mem, dst_ty) in
+ trans_init_slot_from_atom
+ CLONE_none dst_cell dst_slot at
+
+
+ and trans_for_loop (fo:Ast.stmt_for) : unit =
+ let ty_params = get_ty_params_of_current_frame () in
+ let (dst_slot, _) = fo.Ast.for_slot in
+ let dst_cell = cell_of_block_slot dst_slot.id in
+ let (head_stmts, seq) = fo.Ast.for_seq in
+ let (seq_cell, seq_slot) = trans_lval_full false seq in
+ let unit_slot = seq_unit_slot (slot_ty seq_slot) in
+ Array.iter trans_stmt head_stmts;
+ iter_seq_slots ty_params seq_cell seq_cell unit_slot
+ begin
+ fun _ src_cell unit_slot curr_iso ->
+ trans_copy_slot
+ ty_params true
+ dst_cell dst_slot.node
+ src_cell unit_slot curr_iso;
+ trans_block fo.Ast.for_body;
+ end
+ None
+
+ and trans_for_each_loop (stmt_id:node_id) (fe:Ast.stmt_for_each) : unit =
+ let id = fe.Ast.for_each_body.id in
+ let g = GLUE_loop_body id in
+ let name = glue_str cx g in
+ let fix = new_fixup name in
+ let framesz = get_framesz cx id in
+ let callsz = get_callsz cx id in
+ let spill = Hashtbl.find cx.ctxt_spill_fixups id in
+ 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");
+ write_frame_info_ptrs None;
+ iflog (fun _ -> annotate "finished prologue");
+ trans_block fe.Ast.for_each_body;
+ trans_glue_frame_exit fix spill g;
+
+ (*
+ * We've now emitted the body helper-fn. Next, set up a loop that
+ * calls the iter and passes the helper-fn in.
+ *)
+ emit (Il.Enter
+ (Hashtbl.find
+ cx.ctxt_block_fixups
+ fe.Ast.for_each_head.id));
+ let (dst_slot, _) = fe.Ast.for_each_slot in
+ let dst_cell = cell_of_block_slot dst_slot.id in
+ let (flv, args) = fe.Ast.for_each_call in
+ let ty_params =
+ match htab_search cx.ctxt_call_lval_params (lval_base_id flv) with
+ Some params -> params
+ | None -> [| |]
+ in
+ let depth = Hashtbl.find cx.ctxt_stmt_loop_depths stmt_id in
+ let fc = { for_each_fixup = fix; for_each_depth = depth } in
+ iflog (fun _ ->
+ log cx "for-each at depth %d\n" depth);
+ let fn_ptr =
+ trans_prepare_fn_call true cx dst_cell flv ty_params (Some fc) args
+ in
+ call_code (code_of_operand fn_ptr);
+ emit Il.Leave;
+
+ and trans_put (atom_opt:Ast.atom option) : unit =
+ begin
+ match atom_opt with
+ None -> ()
+ | Some at -> trans_set_outptr at
+ end;
+ let block_fptr = Il.Cell (get_iter_block_fn_for_current_frame ()) in
+ let fp = get_iter_outer_frame_ptr_for_current_frame () in
+ let vr = next_vreg_cell Il.voidptr_t in
+ mov vr zero;
+ trans_call_glue (code_of_operand block_fptr) None [| vr; fp |]
+
+ and trans_vec_append dst_cell dst_slot src_oper src_ty =
+ let (dst_elt_slot, trim_trailing_null) =
+ match slot_ty dst_slot with
+ Ast.TY_str -> (interior_slot (Ast.TY_mach TY_u8), true)
+ | Ast.TY_vec e -> (e, false)
+ | _ -> bug () "unexpected dst type in trans_vec_append"
+ in
+ match src_ty with
+ Ast.TY_str
+ | Ast.TY_vec _ ->
+ 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 src_elt_slot =
+ match src_ty with
+ Ast.TY_str -> interior_slot (Ast.TY_mach TY_u8)
+ | Ast.TY_vec e -> e
+ | _ -> bug () "unexpected src type in trans_vec_append"
+ 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
+ then sub_from dst_fill (imm 1L);
+ trans_upcall "upcall_vec_grow"
+ dst_cell
+ [| Il.Cell dst_cell;
+ Il.Cell src_fill |];
+
+ (*
+ * 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
+
+ (* Copy loop: *)
+ let pty s = Il.AddrTy (slot_referent_type abi s) in
+ let dptr = next_vreg_cell (pty dst_elt_slot) in
+ let sptr = next_vreg_cell (pty src_elt_slot) in
+ let dlim = next_vreg_cell (pty dst_elt_slot) in
+ let dst_elt_sz = slot_sz_in_current_frame dst_elt_slot in
+ let src_elt_sz = slot_sz_in_current_frame src_elt_slot in
+ let dst_data =
+ get_element_ptr_dyn_in_current_frame
+ dst_vec Abi.vec_elt_data
+ in
+ let src_data =
+ get_element_ptr_dyn_in_current_frame
+ src_vec Abi.vec_elt_data
+ in
+ lea dptr (fst (need_mem_cell dst_data));
+ lea sptr (fst (need_mem_cell src_data));
+ add_to dptr (Il.Cell dst_fill);
+ mov dlim (Il.Cell dptr);
+ add_to dlim (Il.Cell src_fill);
+ let fwd_jmp = mark () in
+ emit (Il.jmp Il.JMP Il.CodeNone);
+ let back_jmp_targ = mark () in
+ (* copy slot *)
+ trans_copy_slot
+ (get_ty_params_of_current_frame()) true
+ (deref dptr) dst_elt_slot
+ (deref sptr) src_elt_slot
+ None;
+ add_to dptr dst_elt_sz;
+ add_to sptr src_elt_sz;
+ patch fwd_jmp;
+ check_interrupt_flag ();
+ let back_jmp =
+ trans_compare Il.JB (Il.Cell dptr) (Il.Cell dlim) in
+ List.iter
+ (fun j -> patch_existing j back_jmp_targ) back_jmp;
+ let v = next_vreg_cell word_ty in
+ mov v (Il.Cell src_fill);
+ add_to dst_fill (Il.Cell v);
+ | t ->
+ begin
+ bug () "unsupported vector-append type %a" Ast.sprintf_ty t
+ end
+
+
+ and trans_copy_binop dst binop a_src =
+ let (dst_cell, dst_slot) = trans_lval_maybe_init false dst in
+ let src_oper = trans_atom a_src in
+ match slot_ty dst_slot with
+ Ast.TY_str
+ | Ast.TY_vec _ when binop = Ast.BINOP_add ->
+ trans_vec_append dst_cell dst_slot src_oper (atom_type cx a_src)
+ | _ ->
+ let dst_cell = deref_slot false dst_cell dst_slot in
+ let op = trans_binop binop in
+ emit (Il.binary op dst_cell (Il.Cell dst_cell) src_oper);
+
+
+
+ and trans_stmt_full (stmt:Ast.stmt) : unit =
+ match stmt.node with
+
+ Ast.STMT_log a ->
+ begin
+ match atom_type cx a with
+ (* NB: If you extend this, be sure to update the
+ * typechecking code in type.ml as well. *)
+ Ast.TY_str -> trans_log_str a
+ | Ast.TY_int | Ast.TY_uint | Ast.TY_bool
+ | Ast.TY_char | Ast.TY_mach (TY_u8)
+ | Ast.TY_mach (TY_u16) | Ast.TY_mach (TY_u32)
+ | Ast.TY_mach (TY_i8) | Ast.TY_mach (TY_i16)
+ | Ast.TY_mach (TY_i32) ->
+ trans_log_int a
+ | _ -> bugi cx stmt.id "unimplemented logging type"
+ end
+
+ | Ast.STMT_check_expr e ->
+ begin
+ match expr_type cx e with
+ Ast.TY_bool -> trans_check_expr e
+ | _ -> bugi cx stmt.id "check expr on non-bool"
+ end
+
+ | Ast.STMT_yield ->
+ trans_yield ()
+
+ | Ast.STMT_fail ->
+ trans_fail ()
+
+ | Ast.STMT_join task ->
+ trans_join task
+
+ | 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_recv (dst, chan) ->
+ trans_recv (maybe_init stmt.id "recv" dst) dst chan
+
+ | Ast.STMT_copy (dst, e_src) ->
+ trans_copy (maybe_init stmt.id "copy" dst) dst e_src
+
+ | Ast.STMT_copy_binop (dst, binop, a_src) ->
+ trans_copy_binop dst binop a_src
+
+ | Ast.STMT_call (dst, flv, args) ->
+ begin
+ let init = maybe_init stmt.id "call" dst in
+ let ty = lval_ty cx flv in
+ let ty_params =
+ match
+ htab_search
+ cx.ctxt_call_lval_params (lval_base_id flv)
+ with
+ Some params -> params
+ | None -> [| |]
+ in
+ match ty with
+ Ast.TY_fn _ ->
+ let (dst_cell, _) = trans_lval_maybe_init init dst in
+ let fn_ptr =
+ trans_prepare_fn_call init cx dst_cell flv
+ ty_params None args
+ in
+ call_code (code_of_operand fn_ptr)
+ | _ -> bug () "Calling unexpected lval."
+ end
+
+ | Ast.STMT_bind (dst, flv, args) ->
+ begin
+ let init = maybe_init stmt.id "bind" dst in
+ match lval_ty cx flv with
+ Ast.TY_fn (tsig, _) ->
+ trans_bind_fn
+ init (call_ctrl flv) stmt.id dst flv tsig args
+ | _ -> bug () "Binding unexpected lval."
+ end
+
+ | Ast.STMT_init_rec (dst, atab, base) ->
+ let (slot_cell, slot) = trans_lval_init dst in
+ let (trec, dst_slots) =
+ match slot_ty slot with
+ Ast.TY_rec trec -> (trec, Array.map snd trec)
+ | _ ->
+ bugi cx stmt.id
+ "non-rec destination type in stmt_init_rec"
+ in
+ let dst_cell = deref_slot true slot_cell slot in
+ begin
+ match base with
+ None ->
+ let atoms =
+ Array.map (fun (_, _, _, atom) -> atom) atab
+ in
+ trans_init_structural_from_atoms
+ dst_cell dst_slots atoms
+ | Some base_lval ->
+ trans_init_rec_update
+ dst_cell dst_slots trec atab base_lval
+ end
+
+ | Ast.STMT_init_tup (dst, mode_atoms) ->
+ let (slot_cell, slot) = trans_lval_init dst in
+ let dst_slots =
+ match slot_ty slot with
+ Ast.TY_tup ttup -> ttup
+ | _ ->
+ bugi cx stmt.id
+ "non-tup destination type in stmt_init_tup"
+ in
+ let atoms = Array.map (fun (_, _, atom) -> atom) mode_atoms in
+ let dst_cell = deref_slot true slot_cell slot in
+ trans_init_structural_from_atoms dst_cell dst_slots atoms
+
+
+ | Ast.STMT_init_str (dst, s) ->
+ trans_init_str dst s
+
+ | Ast.STMT_init_vec (dst, _, atoms) ->
+ trans_init_vec dst atoms
+
+ | Ast.STMT_init_port dst ->
+ trans_init_port dst
+
+ | Ast.STMT_init_chan (dst, port) ->
+ begin
+ match port with
+ None ->
+ let (dst_cell, _) =
+ trans_lval_init dst
+ in
+ mov dst_cell imm_false
+ | Some p ->
+ trans_init_chan dst p
+ end
+
+ | Ast.STMT_block block ->
+ trans_block block
+
+ | Ast.STMT_while sw ->
+ let (head_stmts, head_expr) = sw.Ast.while_lval in
+ let fwd_jmp = mark () in
+ emit (Il.jmp Il.JMP Il.CodeNone);
+ let block_begin = mark () in
+ trans_block sw.Ast.while_body;
+ patch fwd_jmp;
+ Array.iter trans_stmt head_stmts;
+ check_interrupt_flag ();
+ let back_jmps = trans_cond false head_expr in
+ List.iter (fun j -> patch_existing j block_begin) back_jmps;
+
+ | Ast.STMT_if si ->
+ let skip_thn_jmps = trans_cond true si.Ast.if_test in
+ trans_block si.Ast.if_then;
+ begin
+ match si.Ast.if_else with
+ None -> List.iter patch skip_thn_jmps
+ | Some els ->
+ let skip_els_jmp = mark () in
+ begin
+ emit (Il.jmp Il.JMP Il.CodeNone);
+ List.iter patch skip_thn_jmps;
+ trans_block els;
+ patch skip_els_jmp
+ end
+ end
+
+ | Ast.STMT_check (preds, calls) ->
+ Array.iteri
+ (fun i (fn, args) -> trans_call_pred_and_check preds.(i) fn args)
+ calls
+
+ | Ast.STMT_ret atom_opt ->
+ begin
+ match atom_opt with
+ None -> ()
+ | Some at -> trans_set_outptr at
+ end;
+ drop_slots_at_curr_stmt();
+ Stack.push (mark()) (Stack.top epilogue_jumps);
+ emit (Il.jmp Il.JMP Il.CodeNone)
+
+ | Ast.STMT_be (flv, args) ->
+ let ty = lval_ty cx flv in
+ let ty_params =
+ match htab_search cx.ctxt_call_lval_params (lval_base_id flv) with
+ Some params -> params
+ | None -> [| |]
+ in
+ begin
+ match ty with
+ Ast.TY_fn (tsig, _) ->
+ let result_ty = slot_ty tsig.Ast.sig_output_slot in
+ let (dst_mem, _) =
+ need_mem_cell
+ (deref (wordptr_at (fp_imm out_mem_disp)))
+ in
+ let dst_rty = referent_type abi result_ty in
+ let dst_cell = Il.Mem (dst_mem, dst_rty) in
+ trans_be_fn cx dst_cell flv ty_params args
+
+ | _ -> bug () "Calling unexpected lval."
+ end
+
+ | Ast.STMT_put atom_opt ->
+ trans_put atom_opt
+
+ | Ast.STMT_alt_tag stmt_alt_tag -> trans_alt_tag stmt_alt_tag
+
+ | Ast.STMT_decl _ -> ()
+
+ | Ast.STMT_for fo ->
+ trans_for_loop fo
+
+ | Ast.STMT_for_each fe ->
+ trans_for_each_loop stmt.id fe
+
+ | _ -> bugi cx stmt.id "unhandled form of statement in trans_stmt %a"
+ Ast.sprintf_stmt stmt
+
+ and capture_emitted_quads (fix:fixup) (node:node_id) : unit =
+ let e = emitter() in
+ let n_vregs = Il.num_vregs e in
+ let quads = emitted_quads e in
+ let name = path_name () in
+ let f =
+ if Stack.is_empty curr_file
+ then bugi cx node "missing file scope when capturing quads."
+ else Stack.top curr_file
+ in
+ let item_code = Hashtbl.find cx.ctxt_file_code f in
+ begin
+ iflog (fun _ ->
+ log cx "capturing quads for item #%d" (int_of_node node);
+ annotate_quads name);
+ let vr_s =
+ match htab_search cx.ctxt_spill_fixups node with
+ None -> (assert (n_vregs = 0); None)
+ | Some spill -> Some (n_vregs, spill)
+ in
+ let code = { code_fixup = fix;
+ code_quads = quads;
+ code_vregs_and_spill = vr_s; }
+ in
+ htab_put item_code node code;
+ htab_put cx.ctxt_all_item_code node code
+ end
+
+ and get_frame_glue_fns (fnid:node_id) : Il.operand =
+ let n_ty_params = n_item_ty_params cx fnid in
+ let get_frame_glue glue inner =
+ get_mem_glue glue
+ begin
+ fun mem ->
+ iter_frame_and_arg_slots cx fnid
+ begin
+ fun key slot_id slot ->
+ 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
+ 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
+ in
+ let slot_cell =
+ deref_off_sz ty_params (Il.Reg (fp,st)) off
+ in
+ inner key slot_id ty_params slot slot_cell
+ | _ -> ()
+ end
+ end
+ in
+ trans_crate_rel_data_operand
+ (DATA_frame_glue_fns fnid)
+ begin
+ fun _ ->
+ let mark_frame_glue_fixup =
+ get_frame_glue (GLUE_mark_frame fnid)
+ begin
+ fun _ _ ty_params slot slot_cell ->
+ mark_slot ty_params slot_cell slot None
+ end
+ in
+ let drop_frame_glue_fixup =
+ get_frame_glue (GLUE_drop_frame fnid)
+ begin
+ fun _ _ ty_params slot slot_cell ->
+ drop_slot ty_params slot_cell slot None
+ end
+ in
+ let reloc_frame_glue_fixup =
+ get_frame_glue (GLUE_reloc_frame fnid)
+ begin
+ fun _ _ _ _ _ ->
+ ()
+ end
+ in
+ table_of_crate_rel_fixups
+ [|
+ (*
+ * NB: this must match the struct-offsets given in ABI
+ * & rust runtime library.
+ *)
+ mark_frame_glue_fixup;
+ drop_frame_glue_fixup;
+ reloc_frame_glue_fixup;
+ |]
+ end
+ in
+
+ let trans_frame_entry (fnid:node_id) : unit =
+ let framesz = get_framesz cx fnid in
+ let callsz = get_callsz cx fnid in
+ Stack.push (Stack.create()) epilogue_jumps;
+ push_new_emitter_with_vregs (Some fnid);
+ iflog (fun _ -> annotate "prologue");
+ iflog (fun _ -> annotate (Printf.sprintf
+ "framesz %s"
+ (string_of_size framesz)));
+ iflog (fun _ -> annotate (Printf.sprintf
+ "callsz %s"
+ (string_of_size callsz)));
+ abi.Abi.abi_emit_fn_prologue
+ (emitter()) framesz callsz nabi_rust
+ (upcall_fixup "upcall_grow_task");
+
+ write_frame_info_ptrs (Some fnid);
+ check_interrupt_flag ();
+ iflog (fun _ -> annotate "finished prologue");
+ in
+
+ let trans_frame_exit (fnid:node_id) (drop_args:bool) : unit =
+ Stack.iter patch (Stack.pop epilogue_jumps);
+ if drop_args
+ then
+ begin
+ iflog (fun _ -> annotate "drop args");
+ iter_arg_slots cx fnid callee_drop_slot;
+ end;
+ iflog (fun _ -> annotate "epilogue");
+ abi.Abi.abi_emit_fn_epilogue (emitter());
+ capture_emitted_quads (get_fn_fixup cx fnid) fnid;
+ pop_emitter ()
+ in
+
+ let trans_fn
+ (fnid:node_id)
+ (body:Ast.block)
+ : unit =
+ trans_frame_entry fnid;
+ trans_block body;
+ trans_frame_exit fnid true;
+ in
+
+ let trans_obj_ctor
+ (obj_id:node_id)
+ (state:Ast.header_slots)
+ : unit =
+ trans_frame_entry obj_id;
+
+ let all_args_rty = current_fn_args_rty None in
+ let all_args_cell = caller_args_cell all_args_rty in
+ let frame_args =
+ get_element_ptr_dyn_in_current_frame
+ all_args_cell Abi.calltup_elt_args
+ in
+ let frame_ty_params =
+ get_element_ptr_dyn_in_current_frame
+ all_args_cell Abi.calltup_elt_ty_params
+ in
+
+ let obj_args_tup = Array.map (fun (sloti,_) -> sloti.node) state in
+ let obj_args_slot = interior_slot (Ast.TY_tup obj_args_tup) in
+ let state_ty =
+ Ast.TY_tup [| interior_slot Ast.TY_type;
+ obj_args_slot |]
+ in
+ let state_rty = slot_referent_type abi (interior_slot state_ty) in
+ let state_ptr_slot = exterior_slot state_ty in
+ let state_ptr_rty = slot_referent_type abi state_ptr_slot in
+ let state_malloc_sz =
+ calculate_sz_in_current_frame
+ (SIZE_rt_add
+ ((SIZE_fixed (word_n Abi.exterior_rc_header_size)),
+ (Il.referent_ty_size word_bits state_rty)))
+ in
+
+ let ctor_ty = Hashtbl.find cx.ctxt_all_item_types obj_id in
+ let obj_ty =
+ match ctor_ty with
+ Ast.TY_fn (tsig, _) -> slot_ty tsig.Ast.sig_output_slot
+ | _ -> bug () "object constructor doesn't have function type"
+ in
+ let vtbl_ptr = get_obj_vtbl obj_id in
+ let _ =
+ iflog (fun _ -> annotate "calculate vtbl-ptr from displacement")
+ in
+ let vtbl_cell = crate_rel_to_ptr vtbl_ptr Il.CodeTy in
+
+ let _ = iflog (fun _ -> annotate "load destination obj pair ptr") in
+ let dst_pair_cell = deref (ptr_at (fp_imm out_mem_disp) obj_ty) in
+ let dst_pair_item_cell =
+ get_element_ptr dst_pair_cell Abi.binding_field_item
+ in
+ let dst_pair_state_cell =
+ get_element_ptr dst_pair_cell Abi.binding_field_binding
+ in
+
+ (* Load first cell of pair with vtbl ptr.*)
+ iflog (fun _ -> annotate "mov vtbl-ptr to obj.item cell");
+ mov dst_pair_item_cell (Il.Cell vtbl_cell);
+
+ (* Load second cell of pair with pointer to fresh state tuple.*)
+ iflog (fun _ -> annotate "malloc state-tuple to obj.state cell");
+ trans_malloc dst_pair_state_cell state_malloc_sz;
+
+ (* Copy args into the state tuple. *)
+ let state_ptr = next_vreg_cell (need_scalar_ty state_ptr_rty) in
+ iflog (fun _ -> annotate "load obj.state ptr to vreg");
+ mov state_ptr (Il.Cell dst_pair_state_cell);
+ let state = deref state_ptr in
+ let refcnt = get_element_ptr_dyn_in_current_frame state 0 in
+ let body = get_element_ptr_dyn_in_current_frame state 1 in
+ let obj_tydesc = get_element_ptr_dyn_in_current_frame body 0 in
+ let obj_args = get_element_ptr_dyn_in_current_frame body 1 in
+ iflog (fun _ -> annotate "write refcnt=1 to obj state");
+ mov refcnt one;
+ iflog (fun _ -> annotate "get args-tup tydesc");
+ mov obj_tydesc
+ (Il.Cell (get_tydesc
+ (Some obj_id)
+ (Ast.TY_tup obj_args_tup)));
+ iflog (fun _ -> annotate "copy ctor args to obj args");
+ trans_copy_tup
+ frame_ty_params true
+ obj_args frame_args obj_args_tup;
+ (* We have to do something curious here: we can't drop the
+ * arg slots directly as in the normal frame-exit sequence,
+ * because the arg slot ids are actually given layout
+ * positions inside the object state, and are at different
+ * offsets within that state than within the current
+ * frame. So we manually drop the argument tuple here,
+ * without mentioning the arg slot ids.
+ *)
+ drop_slot frame_ty_params frame_args obj_args_slot None;
+ trans_frame_exit obj_id false;
+ in
+
+ let string_of_name_component (nc:Ast.name_component) : string =
+ match nc with
+ Ast.COMP_ident i -> i
+ | _ -> bug ()
+ "Trans.string_of_name_component on non-COMP_ident"
+ in
+
+
+ let trans_static_name_components
+ (ncs:Ast.name_component list)
+ : Il.operand =
+ let f nc =
+ trans_crate_rel_static_string_frag (string_of_name_component nc)
+ in
+ trans_crate_rel_data_operand
+ (DATA_name (Walk.name_of ncs))
+ (fun _ -> Asm.SEQ (Array.append
+ (Array.map f (Array.of_list ncs))
+ [| Asm.WORD (word_ty_mach, Asm.IMM 0L) |]))
+ in
+
+ let trans_required_fn (fnid:node_id) (blockid:node_id) : unit =
+ trans_frame_entry fnid;
+ emit (Il.Enter (Hashtbl.find cx.ctxt_block_fixups blockid));
+ let (ilib, conv) = Hashtbl.find cx.ctxt_required_items fnid in
+ let lib_num =
+ htab_search_or_add cx.ctxt_required_lib_num ilib
+ (fun _ -> Hashtbl.length cx.ctxt_required_lib_num)
+ in
+ let f = next_vreg_cell (Il.AddrTy (Il.CodeTy)) in
+ let n_ty_params = n_item_ty_params cx fnid in
+ let args_rty = direct_call_args_referent_type cx fnid in
+ let caller_args_cell = caller_args_cell args_rty in
+ begin
+ match ilib with
+ REQUIRED_LIB_rust ls ->
+ begin
+ let c_sym_num =
+ htab_search_or_add cx.ctxt_required_c_sym_num
+ (ilib, "rust_crate")
+ (fun _ -> Hashtbl.length cx.ctxt_required_c_sym_num)
+ in
+ let rust_sym_num =
+ htab_search_or_add cx.ctxt_required_rust_sym_num fnid
+ (fun _ -> Hashtbl.length cx.ctxt_required_rust_sym_num)
+ in
+ let path_elts = stk_elts_from_bot path in
+ let _ =
+ assert (ls.required_prefix < (List.length path_elts))
+ in
+ let relative_path_elts =
+ list_drop ls.required_prefix path_elts
+ in
+ let libstr = trans_static_string ls.required_libname in
+ let relpath =
+ trans_static_name_components relative_path_elts
+ in
+ trans_upcall "upcall_require_rust_sym" f
+ [| Il.Cell (curr_crate_ptr());
+ imm (Int64.of_int lib_num);
+ imm (Int64.of_int c_sym_num);
+ imm (Int64.of_int rust_sym_num);
+ libstr;
+ relpath |];
+
+ trans_copy_forward_args args_rty;
+
+ call_code (code_of_operand (Il.Cell f));
+ end
+
+ | REQUIRED_LIB_c ls ->
+ begin
+ let c_sym_str =
+ match htab_search cx.ctxt_required_syms fnid with
+ Some s -> s
+ | None ->
+ string_of_name_component (Stack.top path)
+ in
+ let c_sym_num =
+ (* FIXME: permit remapping symbol names to handle
+ * mangled variants.
+ *)
+ htab_search_or_add cx.ctxt_required_c_sym_num
+ (ilib, c_sym_str)
+ (fun _ -> Hashtbl.length cx.ctxt_required_c_sym_num)
+ in
+ let libstr = trans_static_string ls.required_libname in
+ let symstr = trans_static_string c_sym_str in
+ let check_rty_sz rty =
+ let sz = force_sz (Il.referent_ty_size word_bits rty) in
+ if sz = 0L || sz = word_sz
+ then ()
+ else bug () "bad arg or ret cell size for native require"
+ in
+ let out =
+ get_element_ptr caller_args_cell Abi.calltup_elt_out_ptr
+ in
+ let _ = check_rty_sz (pointee_type out) in
+ let args =
+ let ty_params_cell =
+ get_element_ptr caller_args_cell Abi.calltup_elt_ty_params
+ in
+ let args_cell =
+ get_element_ptr caller_args_cell Abi.calltup_elt_args
+ in
+ let n_args =
+ match args_cell with
+ Il.Mem (_, Il.StructTy elts) -> Array.length elts
+ | _ -> bug () "non-StructTy in Trans.trans_required_fn"
+ in
+ let mk_ty_param i =
+ Il.Cell (get_element_ptr ty_params_cell i)
+ in
+ let mk_arg i =
+ let arg = get_element_ptr args_cell i in
+ let _ = check_rty_sz (Il.cell_referent_ty arg) in
+ Il.Cell arg
+ in
+ Array.append
+ (Array.init n_ty_params mk_ty_param)
+ (Array.init n_args mk_arg)
+ in
+ let nabi = { nabi_convention = conv;
+ nabi_indirect = true }
+ in
+ if conv <> CONV_rust
+ then assert (n_ty_params = 0);
+ trans_upcall "upcall_require_c_sym" f
+ [| Il.Cell (curr_crate_ptr());
+ imm (Int64.of_int lib_num);
+ imm (Int64.of_int c_sym_num);
+ libstr;
+ symstr |];
+
+ abi.Abi.abi_emit_native_call_in_thunk (emitter())
+ out nabi (Il.Cell f) args;
+ end
+
+ | _ -> bug ()
+ "Trans.required_rust_fn on unexpected form of require library"
+ end;
+ emit Il.Leave;
+ match ilib with
+ REQUIRED_LIB_rust _ ->
+ trans_frame_exit fnid false;
+ | REQUIRED_LIB_c _ ->
+ trans_frame_exit fnid true;
+ | _ -> bug ()
+ "Trans.required_rust_fn on unexpected form of require library"
+ in
+
+ let trans_tag
+ (n:Ast.ident)
+ (tagid:node_id)
+ (tag:(Ast.header_tup * Ast.ty_tag * node_id))
+ : unit =
+ trans_frame_entry tagid;
+ trace_str cx.ctxt_sess.Session.sess_trace_tag
+ ("in tag constructor " ^ n);
+ let (header_tup, _, _) = tag in
+ let ctor_ty = Hashtbl.find cx.ctxt_all_item_types tagid in
+ let ttag =
+ match ctor_ty with
+ Ast.TY_fn (tsig, _) ->
+ begin
+ match slot_ty tsig.Ast.sig_output_slot with
+ Ast.TY_tag ttag -> ttag
+ | Ast.TY_iso tiso -> get_iso_tag tiso
+ | _ -> bugi cx tagid "unexpected fn type for tag constructor"
+ end
+ | _ -> bugi cx tagid "unexpected type for tag constructor"
+ in
+ let slots =
+ Array.map (fun sloti -> referent_to_slot cx sloti.id) header_tup
+ in
+ let tag_keys = sorted_htab_keys ttag in
+ let i = arr_idx tag_keys (Ast.NAME_base (Ast.BASE_ident n)) in
+ let _ = log cx "tag variant: %s -> tag value #%d" n i in
+ let out_cell = deref (ptr_at (fp_imm out_mem_disp) (Ast.TY_tag ttag)) in
+ let tag_cell = get_element_ptr out_cell 0 in
+ let union_cell = get_element_ptr_dyn_in_current_frame out_cell 1 in
+ let dst = get_variant_ptr union_cell i in
+ let dst_ty = snd (need_mem_cell dst) in
+ let src = get_explicit_args_for_current_frame () in
+ (* A clever compiler will inline this. We are not clever. *)
+ iflog (fun _ -> annotate (Printf.sprintf "write tag #%d" i));
+ mov tag_cell (imm (Int64.of_int i));
+ iflog (fun _ -> annotate ("copy tag-content tuple: dst_ty=" ^
+ (Il.string_of_referent_ty dst_ty)));
+ trans_copy_tup (get_ty_params_of_current_frame()) true dst src slots;
+ trace_str cx.ctxt_sess.Session.sess_trace_tag
+ ("finished tag constructor " ^ n);
+ trans_frame_exit tagid true;
+ in
+
+ let enter_file_for id =
+ if Hashtbl.mem cx.ctxt_item_files id
+ then Stack.push id curr_file
+ in
+
+ let leave_file_for id =
+ if Hashtbl.mem cx.ctxt_item_files id
+ then
+ if Stack.is_empty curr_file
+ then bugi cx id "Missing source file on file-scope exit."
+ else ignore (Stack.pop curr_file)
+ in
+
+ let visit_local_mod_item_pre n _ i =
+ iflog (fun _ -> log cx "translating local 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_tag t -> trans_tag n i.id t
+ | Ast.MOD_ITEM_obj ob ->
+ trans_obj_ctor i.id
+ (Array.map (fun (sloti,ident) ->
+ ({sloti with node = get_slot cx sloti.id},ident))
+ ob.Ast.obj_state)
+ | _ -> ()
+ in
+
+ let visit_required_mod_item_pre _ _ i =
+ iflog (fun _ -> log cx "translating required item #%d = %s"
+ (int_of_node i.id) (path_name()));
+ match i.node.Ast.decl_item with
+ Ast.MOD_ITEM_fn f -> trans_required_fn i.id f.Ast.fn_body.id
+ | Ast.MOD_ITEM_mod _ -> ()
+ | Ast.MOD_ITEM_type _ -> ()
+ | _ -> bugi cx i.id "unsupported type of require: %s" (path_name())
+ in
+
+ let visit_obj_drop_pre obj b =
+ let g = GLUE_obj_drop obj.id in
+ let fix =
+ match htab_search cx.ctxt_glue_code g with
+ Some code -> code.code_fixup
+ | None -> bug () "visit_obj_drop_pre without assigned fixup"
+ in
+ let framesz = get_framesz cx b.id in
+ let callsz = get_callsz cx b.id in
+ let spill = Hashtbl.find cx.ctxt_spill_fixups b.id in
+ 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");
+ write_frame_info_ptrs None;
+ iflog (fun _ -> annotate "finished prologue");
+ trans_block b;
+ Hashtbl.remove cx.ctxt_glue_code g;
+ trans_glue_frame_exit fix spill g;
+ inner.Walk.visit_obj_drop_pre obj b
+ in
+
+ let visit_local_obj_fn_pre _ _ fn =
+ trans_fn fn.id fn.node.Ast.fn_body
+ in
+
+ let visit_required_obj_fn_pre _ _ _ =
+ ()
+ in
+
+ let visit_obj_fn_pre obj ident fn =
+ enter_file_for fn.id;
+ begin
+ if Hashtbl.mem cx.ctxt_required_items fn.id
+ then
+ visit_required_obj_fn_pre obj ident fn
+ else
+ visit_local_obj_fn_pre obj ident fn;
+ end;
+ inner.Walk.visit_obj_fn_pre obj ident fn
+ in
+
+ let visit_mod_item_pre n p i =
+ enter_file_for i.id;
+ begin
+ if Hashtbl.mem cx.ctxt_required_items i.id
+ then
+ visit_required_mod_item_pre n p i
+ else
+ visit_local_mod_item_pre n p i
+ end;
+ inner.Walk.visit_mod_item_pre n p i
+ in
+
+ let visit_mod_item_post n p i =
+ inner.Walk.visit_mod_item_post n p i;
+ leave_file_for i.id
+ in
+
+ let visit_obj_fn_post obj ident fn =
+ inner.Walk.visit_obj_fn_post obj ident fn;
+ leave_file_for fn.id
+ in
+
+ let visit_crate_pre crate =
+ enter_file_for crate.id;
+ inner.Walk.visit_crate_pre crate
+ in
+
+ let visit_crate_post crate =
+
+ inner.Walk.visit_crate_post crate;
+
+ let emit_aux_global_glue cx glue fix fn =
+ let glue_name = glue_str cx glue in
+ push_new_emitter_without_vregs None;
+ let e = emitter() in
+ fn e;
+ iflog (fun _ -> annotate_quads glue_name);
+ if (Il.num_vregs e) != 0
+ then bug () "%s uses nonzero vregs" glue_name;
+ pop_emitter();
+ let code =
+ { code_fixup = fix;
+ code_quads = emitted_quads e;
+ code_vregs_and_spill = None; }
+ in
+ htab_put cx.ctxt_glue_code glue code
+ in
+
+ let tab_sz htab =
+ Asm.WORD (word_ty_mach, Asm.IMM (Int64.of_int (Hashtbl.length htab)))
+ in
+
+ let crate_data =
+ (cx.ctxt_crate_fixup,
+ Asm.DEF
+ (cx.ctxt_crate_fixup,
+ Asm.SEQ [|
+ (*
+ * NB: this must match the rust_crate structure
+ * in the rust runtime library.
+ *)
+ crate_rel_word cx.ctxt_image_base_fixup;
+ Asm.WORD (word_ty_mach, Asm.M_POS cx.ctxt_crate_fixup);
+
+ crate_rel_word cx.ctxt_debug_abbrev_fixup;
+ Asm.WORD (word_ty_mach, Asm.M_SZ cx.ctxt_debug_abbrev_fixup);
+
+ crate_rel_word cx.ctxt_debug_info_fixup;
+ Asm.WORD (word_ty_mach, Asm.M_SZ cx.ctxt_debug_info_fixup);
+
+ crate_rel_word cx.ctxt_activate_fixup;
+ crate_rel_word cx.ctxt_exit_task_fixup;
+ crate_rel_word cx.ctxt_unwind_fixup;
+ crate_rel_word cx.ctxt_yield_fixup;
+
+ tab_sz cx.ctxt_required_rust_sym_num;
+ tab_sz cx.ctxt_required_c_sym_num;
+ tab_sz cx.ctxt_required_lib_num;
+ |]))
+ in
+
+ (* Emit additional glue we didn't do elsewhere. *)
+ emit_aux_global_glue cx GLUE_activate
+ cx.ctxt_activate_fixup
+ abi.Abi.abi_activate;
+
+ emit_aux_global_glue cx GLUE_yield
+ cx.ctxt_yield_fixup
+ abi.Abi.abi_yield;
+
+ emit_aux_global_glue cx GLUE_unwind
+ cx.ctxt_unwind_fixup
+ (fun e -> abi.Abi.abi_unwind
+ e nabi_rust (upcall_fixup "upcall_exit"));
+
+ ignore (get_exit_task_glue ());
+
+ begin
+ match abi.Abi.abi_get_next_pc_thunk with
+ None -> ()
+ | Some (_, fix, fn) ->
+ emit_aux_global_glue cx GLUE_get_next_pc fix fn
+ end;
+
+ htab_put cx.ctxt_data
+ DATA_crate crate_data;
+
+ provide_existing_native cx SEG_data "rust_crate" cx.ctxt_crate_fixup;
+
+ leave_file_for crate.id
+ in
+
+ { inner with
+ Walk.visit_crate_pre = visit_crate_pre;
+ Walk.visit_crate_post = visit_crate_post;
+ Walk.visit_mod_item_pre = visit_mod_item_pre;
+ Walk.visit_mod_item_post = visit_mod_item_post;
+ Walk.visit_obj_fn_pre = visit_obj_fn_pre;
+ Walk.visit_obj_fn_post = visit_obj_fn_post;
+ Walk.visit_obj_drop_pre = visit_obj_drop_pre;
+ }
+;;
+
+
+let fixup_assigning_visitor
+ (cx:ctxt)
+ (path:Ast.name_component Stack.t)
+ (inner:Walk.visitor)
+ : Walk.visitor =
+
+ let path_name (_:unit) : string =
+ Ast.fmt_to_str Ast.fmt_name (Walk.path_to_name path)
+ in
+
+ let enter_file_for id =
+ if Hashtbl.mem cx.ctxt_item_files id
+ then
+ begin
+ let name =
+ if Stack.is_empty path
+ then "crate root"
+ else path_name()
+ in
+ htab_put cx.ctxt_file_fixups id (new_fixup name);
+ if not (Hashtbl.mem cx.ctxt_file_code id)
+ then htab_put cx.ctxt_file_code id (Hashtbl.create 0);
+ end
+ in
+
+ let visit_mod_item_pre n p i =
+ enter_file_for i.id;
+ begin
+ match i.node.Ast.decl_item with
+
+ Ast.MOD_ITEM_tag _ ->
+ htab_put cx.ctxt_fn_fixups i.id
+ (new_fixup (path_name()));
+
+ | Ast.MOD_ITEM_fn _ ->
+ begin
+ let path = path_name () in
+ let fixup =
+ if (not cx.ctxt_sess.Session.sess_library_mode)
+ && (Some path) = cx.ctxt_main_name
+ then
+ match cx.ctxt_main_fn_fixup with
+ None -> bug () "missing main fixup in trans"
+ | Some fix -> fix
+ else
+ new_fixup path
+ in
+ htab_put cx.ctxt_fn_fixups i.id fixup;
+ end
+
+ | Ast.MOD_ITEM_obj _ ->
+ htab_put cx.ctxt_fn_fixups i.id
+ (new_fixup (path_name()));
+
+ | _ -> ()
+ end;
+ inner.Walk.visit_mod_item_pre n p i
+ in
+
+ let visit_obj_fn_pre obj ident fn =
+ htab_put cx.ctxt_fn_fixups fn.id
+ (new_fixup (path_name()));
+ inner.Walk.visit_obj_fn_pre obj ident fn
+ in
+
+ let visit_obj_drop_pre obj b =
+ let g = GLUE_obj_drop obj.id in
+ let fix = new_fixup (path_name()) in
+ let tmp_code = { code_fixup = fix;
+ code_quads = [| |];
+ code_vregs_and_spill = None; } in
+ htab_put cx.ctxt_glue_code g tmp_code;
+ inner.Walk.visit_obj_drop_pre obj b
+ in
+
+ let visit_block_pre b =
+ htab_put cx.ctxt_block_fixups b.id (new_fixup "lexical block");
+ inner.Walk.visit_block_pre b
+ in
+
+ let visit_crate_pre c =
+ enter_file_for c.id;
+ inner.Walk.visit_crate_pre c
+ in
+
+ { inner with
+ Walk.visit_crate_pre = visit_crate_pre;
+ Walk.visit_mod_item_pre = visit_mod_item_pre;
+ Walk.visit_obj_fn_pre = visit_obj_fn_pre;
+ Walk.visit_obj_drop_pre = visit_obj_drop_pre;
+ Walk.visit_block_pre = visit_block_pre; }
+
+
+let process_crate
+ (cx:ctxt)
+ (crate:Ast.crate)
+ : unit =
+ let path = Stack.create () in
+ let passes =
+ [|
+ (fixup_assigning_visitor cx path
+ Walk.empty_visitor);
+ (Walk.mod_item_logging_visitor
+ (log cx "translation pass: %s")
+ path
+ (trans_visitor cx path
+ Walk.empty_visitor))
+ |];
+ in
+ log cx "translating crate";
+ begin
+ match cx.ctxt_main_name with
+ None -> ()
+ | Some m -> log cx "with main fn %s" m
+ end;
+ run_passes cx "trans" path passes (log cx "%s") crate;
+;;
+
+(*
+ * 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/me/transutil.ml b/src/boot/me/transutil.ml
new file mode 100644
index 00000000..c430e034
--- /dev/null
+++ b/src/boot/me/transutil.ml
@@ -0,0 +1,238 @@
+open Common;;
+open Semant;;
+
+(* A note on GC:
+ *
+ * We employ -- or "will employ" when the last few pieces of it are done -- a
+ * "simple" precise, mark-sweep, single-generation, per-task (thereby
+ * preemptable and relatively quick) GC scheme on mutable memory.
+ *
+ * - For the sake of this note, call any exterior of 'state' effect a gc_val.
+ *
+ * - gc_vals come from the same malloc as all other values but undergo
+ * different storage management.
+ *
+ * - Every frame has a frame_glue_fns pointer in its fp[-1] slot, written on
+ * function-entry.
+ *
+ * - gc_vals have *three* extra words at their head, not one.
+ *
+ * - A pointer to a gc_val, however, points to the third of these three
+ * words. So a certain quantity of code can treat gc_vals the same way it
+ * would treat refcounted exterior vals.
+ *
+ * - The first word at the head of a gc_val is used as a refcount, as in
+ * non-gc allocations.
+ *
+ * - The (-1)st word at the head of a gc_val is a pointer to a tydesc,
+ * with the low bit of that pointer used as a mark bit.
+ *
+ * - The (-2)nd word at the head of a gc_val is a linked-list pointer to the
+ * gc_val that was allocated (temporally) just before it. Following this
+ * list traces through all the currently active gc_vals in a task.
+ *
+ * - The task has a gc_alloc_chain field that points to the most-recent
+ * gc_val allocated.
+ *
+ * - GC glue has two phases, mark and sweep:
+ *
+ * - The mark phase walks down the frame chain, like the unwinder. It calls
+ * each frame's mark glue as it's passing through. This will mark all the
+ * reachable parts of the task's gc_vals.
+ *
+ * - The sweep phase walks down the task's gc_alloc_chain checking to see
+ * if each allocation has been marked. If marked, it has its mark-bit
+ * reset and the sweep passes it by. If unmarked, it has its tydesc
+ * free_glue called on its body, and is unlinked from the chain. The
+ * free-glue will cause the allocation to (recursively) drop all of its
+ * references and/or run dtors.
+ *
+ * - Note that there is no "special gc state" at work here; the task looks
+ * like it's running normal code that happens to not perform any gc_val
+ * allocation. Mark-bit twiddling is open-coded into all the mark
+ * functions, which know their contents; we only have to do O(frames)
+ * indirect calls to mark, the rest are static. Sweeping costs O(gc-heap)
+ * indirect calls, unfortunately, because the set of sweep functions to
+ * call is arbitrary based on allocation order.
+ *)
+
+
+type mem_ctrl =
+ MEM_rc_opaque
+ | MEM_rc_struct
+ | MEM_gc
+ | MEM_interior
+;;
+
+type clone_ctrl =
+ CLONE_none
+ | CLONE_chan of Il.cell
+ | CLONE_all of Il.cell
+;;
+
+type call_ctrl =
+ CALL_direct
+ | CALL_vtbl
+ | CALL_indirect
+;;
+
+type for_each_ctrl =
+ {
+ for_each_fixup: fixup;
+ for_each_depth: int;
+ }
+;;
+
+let word_sz (abi:Abi.abi) : int64 =
+ abi.Abi.abi_word_sz
+;;
+
+let word_n (abi:Abi.abi) (n:int) : int64 =
+ Int64.mul (word_sz abi) (Int64.of_int n)
+;;
+
+let word_bits (abi:Abi.abi) : Il.bits =
+ abi.Abi.abi_word_bits
+;;
+
+let word_ty_mach (abi:Abi.abi) : ty_mach =
+ match word_bits abi with
+ Il.Bits8 -> TY_u8
+ | Il.Bits16 -> TY_u16
+ | Il.Bits32 -> TY_u32
+ | Il.Bits64 -> TY_u64
+;;
+
+let word_ty_signed_mach (abi:Abi.abi) : ty_mach =
+ match word_bits abi with
+ Il.Bits8 -> TY_i8
+ | Il.Bits16 -> TY_i16
+ | Il.Bits32 -> TY_i32
+ | Il.Bits64 -> TY_i64
+;;
+
+
+let slot_mem_ctrl (slot:Ast.slot) : mem_ctrl =
+ let ty = slot_ty slot in
+ match ty with
+ Ast.TY_port _
+ | Ast.TY_chan _
+ | Ast.TY_task
+ | Ast.TY_vec _
+ | Ast.TY_str -> MEM_rc_opaque
+ | _ ->
+ match slot.Ast.slot_mode with
+ Ast.MODE_exterior _ when type_is_structured ty ->
+ if type_has_state ty
+ then MEM_gc
+ else MEM_rc_struct
+ | Ast.MODE_exterior _ ->
+ MEM_rc_opaque
+ | _ ->
+ MEM_interior
+;;
+
+
+let iter_block_slots
+ (cx:Semant.ctxt)
+ (block_id:node_id)
+ (fn:Ast.slot_key -> node_id -> Ast.slot -> unit)
+ : unit =
+ let block_slots = Hashtbl.find cx.ctxt_block_slots block_id in
+ Hashtbl.iter
+ begin
+ fun key slot_id ->
+ let slot = referent_to_slot cx slot_id in
+ fn key slot_id slot
+ end
+ block_slots
+;;
+
+let iter_frame_slots
+ (cx:Semant.ctxt)
+ (frame_id:node_id)
+ (fn:Ast.slot_key -> node_id -> Ast.slot -> unit)
+ : unit =
+ let blocks = Hashtbl.find cx.ctxt_frame_blocks frame_id in
+ List.iter (fun block -> iter_block_slots cx block fn) blocks
+;;
+
+let iter_arg_slots
+ (cx:Semant.ctxt)
+ (frame_id:node_id)
+ (fn:Ast.slot_key -> node_id -> Ast.slot -> unit)
+ : unit =
+ match htab_search cx.ctxt_frame_args frame_id with
+ None -> ()
+ | Some ls ->
+ List.iter
+ begin
+ fun slot_id ->
+ let key = Hashtbl.find cx.ctxt_slot_keys slot_id in
+ let slot = referent_to_slot cx slot_id in
+ fn key slot_id slot
+ end
+ ls
+;;
+
+let iter_frame_and_arg_slots
+ (cx:Semant.ctxt)
+ (frame_id:node_id)
+ (fn:Ast.slot_key -> node_id -> Ast.slot -> unit)
+ : unit =
+ iter_frame_slots cx frame_id fn;
+ iter_arg_slots cx frame_id fn;
+;;
+
+let next_power_of_two (x:int64) : int64 =
+ let xr = ref (Int64.sub x 1L) in
+ xr := Int64.logor (!xr) (Int64.shift_right_logical (!xr) 1);
+ xr := Int64.logor (!xr) (Int64.shift_right_logical (!xr) 2);
+ xr := Int64.logor (!xr) (Int64.shift_right_logical (!xr) 4);
+ xr := Int64.logor (!xr) (Int64.shift_right_logical (!xr) 8);
+ xr := Int64.logor (!xr) (Int64.shift_right_logical (!xr) 16);
+ xr := Int64.logor (!xr) (Int64.shift_right_logical (!xr) 32);
+ Int64.add 1L (!xr)
+;;
+
+let iter_tup_slots
+ (get_element_ptr:'a -> int -> 'a)
+ (dst_ptr:'a)
+ (src_ptr:'a)
+ (slots:Ast.ty_tup)
+ (f:'a -> 'a -> Ast.slot -> (Ast.ty_iso option) -> unit)
+ (curr_iso:Ast.ty_iso option)
+ : unit =
+ Array.iteri
+ begin
+ fun i slot ->
+ f (get_element_ptr dst_ptr i)
+ (get_element_ptr src_ptr i)
+ slot curr_iso
+ end
+ slots
+;;
+
+let iter_rec_slots
+ (get_element_ptr:'a -> int -> 'a)
+ (dst_ptr:'a)
+ (src_ptr:'a)
+ (entries:Ast.ty_rec)
+ (f:'a -> 'a -> Ast.slot -> (Ast.ty_iso option) -> unit)
+ (curr_iso:Ast.ty_iso option)
+ : unit =
+ iter_tup_slots get_element_ptr dst_ptr src_ptr
+ (Array.map snd entries) f curr_iso
+;;
+
+
+
+
+(*
+ * 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/me/type.ml b/src/boot/me/type.ml
new file mode 100644
index 00000000..2d4dd94a
--- /dev/null
+++ b/src/boot/me/type.ml
@@ -0,0 +1,1294 @@
+open Common;;
+open Semant;;
+
+type tyspec =
+ TYSPEC_equiv of tyvar
+ | TYSPEC_all
+ | TYSPEC_resolved of (Ast.ty_param array) * Ast.ty
+ | TYSPEC_callable of (tyvar * tyvar array) (* out, ins *)
+ | TYSPEC_collection of tyvar (* vec or str *)
+ | TYSPEC_comparable (* comparable with = and != *)
+ | TYSPEC_plusable (* nums, vecs, and strings *)
+ | TYSPEC_dictionary of dict
+ | TYSPEC_integral (* int-like *)
+ | TYSPEC_loggable
+ | TYSPEC_numeric (* int-like or float-like *)
+ | TYSPEC_ordered (* comparable with < etc. *)
+ | TYSPEC_record of dict
+ | TYSPEC_tuple of tyvar array (* heterogeneous tuple *)
+ | TYSPEC_vector of tyvar
+ | TYSPEC_app of (tyvar * Ast.ty array)
+
+and dict = (Ast.ident, tyvar) Hashtbl.t
+
+and tyvar = tyspec ref;;
+
+(* Signatures for binary operators. *)
+type binopsig =
+ BINOPSIG_bool_bool_bool (* bool * bool -> bool *)
+ | BINOPSIG_comp_comp_bool (* comparable a * comparable a -> bool *)
+ | BINOPSIG_ord_ord_bool (* ordered a * ordered a -> bool *)
+ | BINOPSIG_integ_integ_integ (* integral a * integral a -> integral a *)
+ | BINOPSIG_num_num_num (* numeric a * numeric a -> numeric a *)
+ | BINOPSIG_plus_plus_plus (* plusable a * plusable a -> plusable a *)
+;;
+
+let rec tyspec_to_str (ts:tyspec) : string =
+
+ let fmt = Format.fprintf in
+ let fmt_ident (ff:Format.formatter) (i:Ast.ident) : unit =
+ fmt ff "%s" i
+ in
+ let fmt_obox ff = Format.pp_open_box ff 4 in
+ let fmt_cbox ff = Format.pp_close_box ff () in
+ let fmt_obr ff = fmt ff "<" in
+ let fmt_cbr ff = fmt ff ">" in
+ let fmt_obb ff = (fmt_obox ff; fmt_obr ff) in
+ let fmt_cbb ff = (fmt_cbox ff; fmt_cbr ff) in
+
+ let rec fmt_fields (flav:string) (ff:Format.formatter) (flds:dict) : unit =
+ fmt_obb ff;
+ fmt ff "%s :" flav;
+ let fmt_entry ident tv =
+ fmt ff "@\n";
+ fmt_ident ff ident;
+ fmt ff " : ";
+ fmt_tyspec ff (!tv);
+ in
+ Hashtbl.iter fmt_entry flds;
+ fmt_cbb ff
+
+ and fmt_app ff tv args =
+ begin
+ assert (Array.length args <> 0);
+ fmt_obb ff;
+ fmt ff "app(";
+ fmt_tyspec ff (!tv);
+ fmt ff ")";
+ Ast.fmt_app_args ff args;
+ fmt_cbb ff;
+ end
+
+ and fmt_tvs ff tvs =
+ fmt_obox ff;
+ let fmt_tv i tv =
+ if i <> 0
+ then fmt ff ", ";
+ fmt_tyspec ff (!tv)
+ in
+ Array.iteri fmt_tv tvs;
+ fmt_cbox ff;
+
+ and fmt_tyspec ff ts =
+ match ts with
+ TYSPEC_all -> fmt ff "<?>"
+ | TYSPEC_comparable -> fmt ff "<comparable>"
+ | TYSPEC_plusable -> fmt ff "<plusable>"
+ | TYSPEC_integral -> fmt ff "<integral>"
+ | TYSPEC_loggable -> fmt ff "<loggable>"
+ | TYSPEC_numeric -> fmt ff "<numeric>"
+ | TYSPEC_ordered -> fmt ff "<ordered>"
+ | TYSPEC_resolved (params, ty) ->
+ if Array.length params <> 0
+ then
+ begin
+ fmt ff "abs";
+ Ast.fmt_decl_params ff params;
+ fmt ff "(";
+ Ast.fmt_ty ff ty;
+ fmt ff ")"
+ end
+ else
+ Ast.fmt_ty ff ty
+
+ | TYSPEC_equiv tv ->
+ fmt_tyspec ff (!tv)
+
+ | TYSPEC_callable (out, ins) ->
+ fmt_obb ff;
+ fmt ff "callable fn(";
+ fmt_tvs ff ins;
+ fmt ff ") -> ";
+ fmt_tyspec ff (!out);
+ fmt_cbb ff;
+
+ | TYSPEC_collection tv ->
+ fmt_obb ff;
+ fmt ff "collection : ";
+ fmt_tyspec ff (!tv);
+ fmt_cbb ff;
+
+ | TYSPEC_tuple tvs ->
+ fmt ff "(";
+ fmt_tvs ff tvs;
+ fmt ff ")";
+
+ | TYSPEC_vector tv ->
+ fmt_obb ff;
+ fmt ff "vector ";
+ fmt_tyspec ff (!tv);
+ fmt_cbb ff;
+
+ | TYSPEC_dictionary dct ->
+ fmt_fields "dictionary" ff dct
+
+ | TYSPEC_record dct ->
+ fmt_fields "record" ff dct
+
+ | TYSPEC_app (tv, args) ->
+ fmt_app ff tv args
+
+ in
+ let buf = Buffer.create 16 in
+ let bf = Format.formatter_of_buffer buf in
+ begin
+ fmt_tyspec bf ts;
+ Format.pp_print_flush bf ();
+ Buffer.contents buf
+ end
+;;
+
+let iflog cx thunk =
+ if cx.ctxt_sess.Session.sess_log_type
+ then thunk ()
+ else ()
+;;
+
+let rec resolve_tyvar (tv:tyvar) : tyvar =
+ match !tv with
+ TYSPEC_equiv subtv -> resolve_tyvar subtv
+ | _ -> tv
+;;
+
+let process_crate (cx:ctxt) (crate:Ast.crate) : unit =
+ let log cx = Session.log "type"
+ cx.ctxt_sess.Session.sess_log_type
+ cx.ctxt_sess.Session.sess_log_out
+ in
+ let retval_tvs = Stack.create () in
+ let push_retval_tv tv =
+ Stack.push tv retval_tvs
+ in
+ let pop_retval_tv _ =
+ ignore (Stack.pop retval_tvs)
+ in
+ let retval_tv _ =
+ Stack.top retval_tvs
+ in
+ let (bindings:(node_id, tyvar) Hashtbl.t) = Hashtbl.create 10 in
+ let (item_params:(node_id, tyvar array) Hashtbl.t) = Hashtbl.create 10 in
+ let (lval_tyvars:(node_id, tyvar) Hashtbl.t) = Hashtbl.create 0 in
+
+ let path = Stack.create () in
+
+ let visitor (cx:ctxt) (inner:Walk.visitor) : Walk.visitor =
+
+ let rec unify_slot
+ (slot:Ast.slot)
+ (id_opt:node_id option)
+ (tv:tyvar) : unit =
+ match id_opt with
+ Some id -> unify_tyvars (Hashtbl.find bindings id) tv
+ | None ->
+ match slot.Ast.slot_ty with
+ None -> bug () "untyped unidentified slot"
+ | Some ty -> unify_ty ty tv
+
+ and check_sane_tyvar tv =
+ match !tv with
+ TYSPEC_resolved (_, (Ast.TY_named _)) ->
+ bug () "named-type in type checker"
+ | _ -> ()
+
+ and unify_tyvars (av:tyvar) (bv:tyvar) : unit =
+ iflog cx (fun _ ->
+ log cx "unifying types:";
+ log cx "input tyvar A: %s" (tyspec_to_str !av);
+ log cx "input tyvar B: %s" (tyspec_to_str !bv));
+ check_sane_tyvar av;
+ check_sane_tyvar bv;
+
+ unify_tyvars' av bv;
+
+ iflog cx (fun _ ->
+ log cx "unified types:";
+ log cx "output tyvar A: %s" (tyspec_to_str !av);
+ log cx "output tyvar B: %s" (tyspec_to_str !bv));
+ check_sane_tyvar av;
+ check_sane_tyvar bv;
+
+ and unify_tyvars' (av:tyvar) (bv:tyvar) : unit =
+ let (a, b) = ((resolve_tyvar av), (resolve_tyvar bv)) in
+ let fail () =
+ err None "mismatched types: %s vs. %s" (tyspec_to_str !av)
+ (tyspec_to_str !bv);
+ in
+
+ let merge_dicts a b =
+ let c = Hashtbl.create ((Hashtbl.length a) + (Hashtbl.length b)) in
+ let merge ident tv_a =
+ if Hashtbl.mem c ident
+ then unify_tyvars (Hashtbl.find c ident) tv_a
+ else Hashtbl.add c ident tv_a
+ in
+ Hashtbl.iter (Hashtbl.add c) b;
+ Hashtbl.iter merge a;
+ c
+ in
+
+ let unify_dict_with_record_fields
+ (dct:dict)
+ (fields:Ast.ty_rec)
+ : unit =
+ let rec find_slot (query:Ast.ident) i : Ast.slot =
+ if i = Array.length fields
+ then fail ()
+ else match fields.(i) with
+ (ident, slot) ->
+ if ident = query then slot
+ else find_slot query (i + 1)
+ in
+
+ let check_entry ident tv =
+ unify_slot (find_slot ident 0) None tv
+ in
+ Hashtbl.iter check_entry dct
+ in
+
+ let unify_dict_with_obj_fns
+ (dct:dict)
+ (fns:(Ast.ident,Ast.ty_fn) Hashtbl.t) : unit =
+ let check_entry (query:Ast.ident) tv : unit =
+ match htab_search fns query with
+ None -> fail ()
+ | Some fn -> unify_ty (Ast.TY_fn fn) tv
+ in
+ Hashtbl.iter check_entry dct
+ in
+
+ let rec is_comparable_or_ordered (comparable:bool) (ty:Ast.ty) : bool =
+ match ty with
+ Ast.TY_mach _ | Ast.TY_int | Ast.TY_uint
+ | Ast.TY_char | Ast.TY_str -> true
+ | Ast.TY_any | Ast.TY_nil | Ast.TY_bool | Ast.TY_chan _
+ | Ast.TY_port _ | Ast.TY_task | Ast.TY_tup _ | Ast.TY_vec _
+ | Ast.TY_rec _ | Ast.TY_tag _ | Ast.TY_iso _ | Ast.TY_idx _ ->
+ comparable
+ | Ast.TY_fn _ | Ast.TY_obj _
+ | Ast.TY_param _ | Ast.TY_native _ | Ast.TY_type -> false
+ | Ast.TY_named _ -> bug () "unexpected named type"
+ | Ast.TY_constrained (ty, _) ->
+ is_comparable_or_ordered comparable ty
+ in
+
+ let floating (ty:Ast.ty) : bool =
+ match ty with
+ Ast.TY_mach TY_f32 | Ast.TY_mach TY_f64 -> true
+ | _ -> false
+ in
+
+ let integral (ty:Ast.ty) : bool =
+ match ty with
+ Ast.TY_int | Ast.TY_mach TY_u8 | Ast.TY_mach TY_u16
+ | Ast.TY_mach TY_u32 | Ast.TY_mach TY_u64 | Ast.TY_mach TY_i8
+ | Ast.TY_mach TY_i16 | Ast.TY_mach TY_i32
+ | Ast.TY_mach TY_i64 ->
+ true
+ | _ -> false
+ in
+
+ let numeric (ty:Ast.ty) : bool = (integral ty) || (floating ty) in
+
+ let plusable (ty:Ast.ty) : bool =
+ match ty with
+ Ast.TY_str -> true
+ | Ast.TY_vec _ -> true
+ | _ -> numeric ty
+ in
+
+ let loggable (ty:Ast.ty) : bool =
+ match ty with
+ Ast.TY_str | Ast.TY_bool | Ast.TY_int | Ast.TY_uint
+ | Ast.TY_char
+ | Ast.TY_mach TY_u8 | Ast.TY_mach TY_u16 | Ast.TY_mach TY_u32
+ | Ast.TY_mach TY_i8 | Ast.TY_mach TY_i16 | Ast.TY_mach TY_i32
+ -> true
+ | _ -> false
+ in
+
+ let result =
+ match (!a, !b) with
+ (TYSPEC_equiv _, _) | (_, TYSPEC_equiv _) ->
+ bug () "equiv found even though tyvar was resolved"
+
+ | (TYSPEC_all, other) | (other, TYSPEC_all) -> other
+
+ (* resolved *)
+
+ | (TYSPEC_resolved (params_a, ty_a),
+ TYSPEC_resolved (params_b, ty_b)) ->
+ if params_a <> params_b || ty_a <> ty_b
+ then fail()
+ else TYSPEC_resolved (params_a, ty_a)
+
+ | (TYSPEC_resolved (params, ty),
+ TYSPEC_callable (out_tv, in_tvs))
+ | (TYSPEC_callable (out_tv, in_tvs),
+ TYSPEC_resolved (params, ty)) ->
+ let unify_in_slot i in_slot =
+ unify_slot in_slot None in_tvs.(i)
+ in
+ begin
+ match ty with
+ Ast.TY_fn ({
+ Ast.sig_input_slots = in_slots;
+ Ast.sig_output_slot = out_slot
+ }, _) ->
+ if Array.length in_slots != Array.length in_tvs
+ then fail ();
+ unify_slot out_slot None out_tv;
+ Array.iteri unify_in_slot in_slots
+ | _ -> fail ()
+ end;
+ TYSPEC_resolved (params, ty)
+
+ | (TYSPEC_resolved (params, ty), TYSPEC_collection tv)
+ | (TYSPEC_collection tv, TYSPEC_resolved (params, ty)) ->
+ begin
+ match ty with
+ Ast.TY_vec slot -> unify_slot slot None tv
+ | Ast.TY_str -> unify_ty (Ast.TY_mach TY_u8) tv
+ | _ -> fail ()
+ end;
+ TYSPEC_resolved (params, ty)
+
+ | (TYSPEC_resolved (params, ty), TYSPEC_comparable)
+ | (TYSPEC_comparable, TYSPEC_resolved (params, ty)) ->
+ if not (is_comparable_or_ordered true ty) then fail ()
+ else TYSPEC_resolved (params, ty)
+
+ | (TYSPEC_resolved (params, ty), TYSPEC_plusable)
+ | (TYSPEC_plusable, TYSPEC_resolved (params, ty)) ->
+ if not (plusable ty) then fail ()
+ else TYSPEC_resolved (params, ty)
+
+ | (TYSPEC_resolved (params, ty), TYSPEC_dictionary dct)
+ | (TYSPEC_dictionary dct, TYSPEC_resolved (params, ty)) ->
+ begin
+ match ty with
+ Ast.TY_rec fields ->
+ unify_dict_with_record_fields dct fields
+ | Ast.TY_obj (_, fns) ->
+ unify_dict_with_obj_fns dct fns
+ | _ -> fail ()
+ end;
+ TYSPEC_resolved (params, ty)
+
+ | (TYSPEC_resolved (params, ty), TYSPEC_integral)
+ | (TYSPEC_integral, TYSPEC_resolved (params, ty)) ->
+ if not (integral ty)
+ then fail ()
+ else TYSPEC_resolved (params, ty)
+
+ | (TYSPEC_resolved (params, ty), TYSPEC_loggable)
+ | (TYSPEC_loggable, TYSPEC_resolved (params, ty)) ->
+ if not (loggable ty)
+ then fail ()
+ else TYSPEC_resolved (params, ty)
+
+ | (TYSPEC_resolved (params, ty), TYSPEC_numeric)
+ | (TYSPEC_numeric, TYSPEC_resolved (params, ty)) ->
+ if not (numeric ty) then fail ()
+ else TYSPEC_resolved (params, ty)
+
+ | (TYSPEC_resolved (params, ty), TYSPEC_ordered)
+ | (TYSPEC_ordered, TYSPEC_resolved (params, ty)) ->
+ if not (is_comparable_or_ordered false ty) then fail ()
+ else TYSPEC_resolved (params, ty)
+
+ | (TYSPEC_resolved (params, ty), TYSPEC_app (tv, args))
+ | (TYSPEC_app (tv, args), TYSPEC_resolved (params, ty)) ->
+ let ty = rebuild_ty_under_params ty params args false in
+ unify_ty ty tv;
+ TYSPEC_resolved ([| |], ty)
+
+ | (TYSPEC_resolved (params, ty), TYSPEC_record dct)
+ | (TYSPEC_record dct, TYSPEC_resolved (params, ty)) ->
+ begin
+ match ty with
+ Ast.TY_rec fields ->
+ unify_dict_with_record_fields dct fields
+ | _ -> fail ()
+ end;
+ TYSPEC_resolved (params, ty)
+
+ | (TYSPEC_resolved (params, ty), TYSPEC_tuple tvs)
+ | (TYSPEC_tuple tvs, TYSPEC_resolved (params, ty)) ->
+ begin
+ match ty with
+ Ast.TY_tup (elem_slots:Ast.slot array) ->
+ if (Array.length elem_slots) < (Array.length tvs)
+ then fail ()
+ else
+ let check_elem i tv =
+ unify_slot (elem_slots.(i)) None tv
+ in
+ Array.iteri check_elem tvs
+ | _ -> fail ()
+ end;
+ TYSPEC_resolved (params, ty)
+
+ | (TYSPEC_resolved (params, ty), TYSPEC_vector tv)
+ | (TYSPEC_vector tv, TYSPEC_resolved (params, ty)) ->
+ begin
+ match ty with
+ Ast.TY_vec slot ->
+ unify_slot slot None tv;
+ TYSPEC_resolved (params, ty)
+ | _ -> fail ()
+ end
+
+ (* callable *)
+
+ | (TYSPEC_callable (a_out_tv, a_in_tvs),
+ TYSPEC_callable (b_out_tv, b_in_tvs)) ->
+ unify_tyvars a_out_tv b_out_tv;
+ let check_in_tv i a_in_tv =
+ unify_tyvars a_in_tv b_in_tvs.(i)
+ in
+ Array.iteri check_in_tv a_in_tvs;
+ TYSPEC_callable (a_out_tv, a_in_tvs)
+
+ | (TYSPEC_callable _, TYSPEC_collection _)
+ | (TYSPEC_callable _, TYSPEC_comparable)
+ | (TYSPEC_callable _, TYSPEC_plusable)
+ | (TYSPEC_callable _, TYSPEC_dictionary _)
+ | (TYSPEC_callable _, TYSPEC_integral)
+ | (TYSPEC_callable _, TYSPEC_loggable)
+ | (TYSPEC_callable _, TYSPEC_numeric)
+ | (TYSPEC_callable _, TYSPEC_ordered)
+ | (TYSPEC_callable _, TYSPEC_app _)
+ | (TYSPEC_callable _, TYSPEC_record _)
+ | (TYSPEC_callable _, TYSPEC_tuple _)
+ | (TYSPEC_callable _, TYSPEC_vector _)
+ | (TYSPEC_collection _, TYSPEC_callable _)
+ | (TYSPEC_comparable, TYSPEC_callable _)
+ | (TYSPEC_plusable, TYSPEC_callable _)
+ | (TYSPEC_dictionary _, TYSPEC_callable _)
+ | (TYSPEC_integral, TYSPEC_callable _)
+ | (TYSPEC_loggable, TYSPEC_callable _)
+ | (TYSPEC_numeric, TYSPEC_callable _)
+ | (TYSPEC_ordered, TYSPEC_callable _)
+ | (TYSPEC_app _, TYSPEC_callable _)
+ | (TYSPEC_record _, TYSPEC_callable _)
+ | (TYSPEC_tuple _, TYSPEC_callable _)
+ | (TYSPEC_vector _, TYSPEC_callable _) -> fail ()
+
+ (* collection *)
+
+ | (TYSPEC_collection av, TYSPEC_collection bv) ->
+ unify_tyvars av bv;
+ TYSPEC_collection av
+
+ | (TYSPEC_collection av, TYSPEC_comparable)
+ | (TYSPEC_comparable, TYSPEC_collection av) ->
+ TYSPEC_collection av
+
+ | (TYSPEC_collection v, TYSPEC_plusable)
+ | (TYSPEC_plusable, TYSPEC_collection v) -> TYSPEC_collection v
+
+ | (TYSPEC_collection _, TYSPEC_dictionary _)
+ | (TYSPEC_collection _, TYSPEC_integral)
+ | (TYSPEC_collection _, TYSPEC_loggable)
+ | (TYSPEC_collection _, TYSPEC_numeric)
+ | (TYSPEC_collection _, TYSPEC_ordered)
+ | (TYSPEC_collection _, TYSPEC_app _)
+ | (TYSPEC_collection _, TYSPEC_record _)
+ | (TYSPEC_collection _, TYSPEC_tuple _)
+ | (TYSPEC_dictionary _, TYSPEC_collection _)
+ | (TYSPEC_integral, TYSPEC_collection _)
+ | (TYSPEC_loggable, TYSPEC_collection _)
+ | (TYSPEC_numeric, TYSPEC_collection _)
+ | (TYSPEC_ordered, TYSPEC_collection _)
+ | (TYSPEC_app _, TYSPEC_collection _)
+ | (TYSPEC_record _, TYSPEC_collection _)
+ | (TYSPEC_tuple _, TYSPEC_collection _) -> fail ()
+
+ | (TYSPEC_collection av, TYSPEC_vector bv)
+ | (TYSPEC_vector bv, TYSPEC_collection av) ->
+ unify_tyvars av bv;
+ TYSPEC_vector av
+
+ (* comparable *)
+
+ | (TYSPEC_comparable, TYSPEC_comparable) -> TYSPEC_comparable
+
+ | (TYSPEC_comparable, TYSPEC_plusable)
+ | (TYSPEC_plusable, TYSPEC_comparable) -> TYSPEC_plusable
+
+ | (TYSPEC_comparable, TYSPEC_dictionary dict)
+ | (TYSPEC_dictionary dict, TYSPEC_comparable) ->
+ TYSPEC_dictionary dict
+
+ | (TYSPEC_comparable, TYSPEC_integral)
+ | (TYSPEC_integral, TYSPEC_comparable) -> TYSPEC_integral
+
+ | (TYSPEC_comparable, TYSPEC_loggable)
+ | (TYSPEC_loggable, TYSPEC_comparable) -> TYSPEC_loggable
+
+ | (TYSPEC_comparable, TYSPEC_numeric)
+ | (TYSPEC_numeric, TYSPEC_comparable) -> TYSPEC_numeric
+
+ | (TYSPEC_comparable, TYSPEC_ordered)
+ | (TYSPEC_ordered, TYSPEC_comparable) -> TYSPEC_ordered
+
+ | (TYSPEC_comparable, TYSPEC_app _)
+ | (TYSPEC_app _, TYSPEC_comparable) -> fail ()
+
+ | (TYSPEC_comparable, TYSPEC_record r)
+ | (TYSPEC_record r, TYSPEC_comparable) -> TYSPEC_record r
+
+ | (TYSPEC_comparable, TYSPEC_tuple t)
+ | (TYSPEC_tuple t, TYSPEC_comparable) -> TYSPEC_tuple t
+
+ | (TYSPEC_comparable, TYSPEC_vector v)
+ | (TYSPEC_vector v, TYSPEC_comparable) -> TYSPEC_vector v
+
+ (* plusable *)
+
+ | (TYSPEC_plusable, TYSPEC_plusable) -> TYSPEC_plusable
+
+ | (TYSPEC_plusable, TYSPEC_dictionary _)
+ | (TYSPEC_dictionary _, TYSPEC_plusable) -> fail ()
+
+ | (TYSPEC_plusable, TYSPEC_integral)
+ | (TYSPEC_integral, TYSPEC_plusable) -> TYSPEC_integral
+
+ | (TYSPEC_plusable, TYSPEC_loggable)
+ | (TYSPEC_loggable, TYSPEC_plusable) -> TYSPEC_plusable
+
+ | (TYSPEC_plusable, TYSPEC_numeric)
+ | (TYSPEC_numeric, TYSPEC_plusable) -> TYSPEC_numeric
+
+ | (TYSPEC_plusable, TYSPEC_ordered)
+ | (TYSPEC_ordered, TYSPEC_plusable) -> TYSPEC_plusable
+
+ | (TYSPEC_plusable, TYSPEC_record _)
+ | (TYSPEC_record _, TYSPEC_plusable) -> fail ()
+
+ | (TYSPEC_plusable, TYSPEC_tuple _)
+ | (TYSPEC_tuple _, TYSPEC_plusable) -> fail ()
+
+ | (TYSPEC_plusable, TYSPEC_vector v)
+ | (TYSPEC_vector v, TYSPEC_plusable) -> TYSPEC_vector v
+
+ | (TYSPEC_plusable, TYSPEC_app _)
+ | (TYSPEC_app _, TYSPEC_plusable) -> fail ()
+
+ (* dictionary *)
+
+ | (TYSPEC_dictionary da, TYSPEC_dictionary db) ->
+ TYSPEC_dictionary (merge_dicts da db)
+
+ | (TYSPEC_dictionary _, TYSPEC_integral)
+ | (TYSPEC_dictionary _, TYSPEC_loggable)
+ | (TYSPEC_dictionary _, TYSPEC_numeric)
+ | (TYSPEC_dictionary _, TYSPEC_ordered)
+ | (TYSPEC_dictionary _, TYSPEC_app _)
+ | (TYSPEC_integral, TYSPEC_dictionary _)
+ | (TYSPEC_loggable, TYSPEC_dictionary _)
+ | (TYSPEC_numeric, TYSPEC_dictionary _)
+ | (TYSPEC_ordered, TYSPEC_dictionary _)
+ | (TYSPEC_app _, TYSPEC_dictionary _) -> fail ()
+
+ | (TYSPEC_dictionary d, TYSPEC_record r)
+ | (TYSPEC_record r, TYSPEC_dictionary d) ->
+ TYSPEC_record (merge_dicts d r)
+
+ | (TYSPEC_dictionary _, TYSPEC_tuple _)
+ | (TYSPEC_dictionary _, TYSPEC_vector _)
+ | (TYSPEC_tuple _, TYSPEC_dictionary _)
+ | (TYSPEC_vector _, TYSPEC_dictionary _) -> fail ()
+
+ (* integral *)
+
+ | (TYSPEC_integral, TYSPEC_integral)
+ | (TYSPEC_integral, TYSPEC_loggable)
+ | (TYSPEC_integral, TYSPEC_numeric)
+ | (TYSPEC_integral, TYSPEC_ordered)
+ | (TYSPEC_loggable, TYSPEC_integral)
+ | (TYSPEC_numeric, TYSPEC_integral)
+ | (TYSPEC_ordered, TYSPEC_integral) -> TYSPEC_integral
+
+ | (TYSPEC_integral, TYSPEC_app _)
+ | (TYSPEC_integral, TYSPEC_record _)
+ | (TYSPEC_integral, TYSPEC_tuple _)
+ | (TYSPEC_integral, TYSPEC_vector _)
+ | (TYSPEC_app _, TYSPEC_integral)
+ | (TYSPEC_record _, TYSPEC_integral)
+ | (TYSPEC_tuple _, TYSPEC_integral)
+ | (TYSPEC_vector _, TYSPEC_integral) -> fail ()
+
+ (* loggable *)
+
+ | (TYSPEC_loggable, TYSPEC_loggable) -> TYSPEC_loggable
+
+ | (TYSPEC_loggable, TYSPEC_numeric)
+ | (TYSPEC_numeric, TYSPEC_loggable) -> TYSPEC_numeric
+
+ | (TYSPEC_loggable, TYSPEC_ordered)
+ | (TYSPEC_ordered, TYSPEC_loggable) -> TYSPEC_ordered
+
+ | (TYSPEC_loggable, TYSPEC_app _)
+ | (TYSPEC_loggable, TYSPEC_record _)
+ | (TYSPEC_loggable, TYSPEC_tuple _)
+ | (TYSPEC_loggable, TYSPEC_vector _)
+ | (TYSPEC_app _, TYSPEC_loggable)
+ | (TYSPEC_record _, TYSPEC_loggable)
+ | (TYSPEC_tuple _, TYSPEC_loggable)
+ | (TYSPEC_vector _, TYSPEC_loggable) -> fail ()
+
+ (* numeric *)
+
+ | (TYSPEC_numeric, TYSPEC_numeric) -> TYSPEC_numeric
+
+ | (TYSPEC_numeric, TYSPEC_ordered)
+ | (TYSPEC_ordered, TYSPEC_numeric) -> TYSPEC_ordered
+
+ | (TYSPEC_numeric, TYSPEC_app _)
+ | (TYSPEC_numeric, TYSPEC_record _)
+ | (TYSPEC_numeric, TYSPEC_tuple _)
+ | (TYSPEC_numeric, TYSPEC_vector _)
+ | (TYSPEC_app _, TYSPEC_numeric)
+ | (TYSPEC_record _, TYSPEC_numeric)
+ | (TYSPEC_tuple _, TYSPEC_numeric)
+ | (TYSPEC_vector _, TYSPEC_numeric) -> fail ()
+
+ (* ordered *)
+
+ | (TYSPEC_ordered, TYSPEC_ordered) -> TYSPEC_ordered
+
+ | (TYSPEC_ordered, TYSPEC_app _)
+ | (TYSPEC_ordered, TYSPEC_record _)
+ | (TYSPEC_ordered, TYSPEC_tuple _)
+ | (TYSPEC_ordered, TYSPEC_vector _)
+ | (TYSPEC_app _, TYSPEC_ordered)
+ | (TYSPEC_record _, TYSPEC_ordered)
+ | (TYSPEC_tuple _, TYSPEC_ordered)
+ | (TYSPEC_vector _, TYSPEC_ordered) -> fail ()
+
+ (* app *)
+
+ | (TYSPEC_app (tv_a, args_a),
+ TYSPEC_app (tv_b, args_b)) ->
+ if args_a <> args_b
+ then fail()
+ else
+ begin
+ unify_tyvars tv_a tv_b;
+ TYSPEC_app (tv_a, args_a)
+ end
+
+ | (TYSPEC_app _, TYSPEC_record _)
+ | (TYSPEC_app _, TYSPEC_tuple _)
+ | (TYSPEC_app _, TYSPEC_vector _)
+ | (TYSPEC_record _, TYSPEC_app _)
+ | (TYSPEC_tuple _, TYSPEC_app _)
+ | (TYSPEC_vector _, TYSPEC_app _) -> fail ()
+
+ (* record *)
+
+ | (TYSPEC_record da, TYSPEC_record db) ->
+ TYSPEC_record (merge_dicts da db)
+
+ | (TYSPEC_record _, TYSPEC_tuple _)
+ | (TYSPEC_record _, TYSPEC_vector _)
+ | (TYSPEC_tuple _, TYSPEC_record _)
+ | (TYSPEC_vector _, TYSPEC_record _) -> fail ()
+
+ (* tuple *)
+
+ | (TYSPEC_tuple tvs_a, TYSPEC_tuple tvs_b) ->
+ let len_a = Array.length tvs_a in
+ let len_b = Array.length tvs_b in
+ let max_len = max len_a len_b in
+ let init_tuple_elem i =
+ if i >= len_a
+ then tvs_b.(i)
+ else if i >= len_b
+ then tvs_a.(i)
+ else begin
+ unify_tyvars tvs_a.(i) tvs_b.(i);
+ tvs_a.(i)
+ end
+ in
+ TYSPEC_tuple (Array.init max_len init_tuple_elem)
+
+ | (TYSPEC_tuple _, TYSPEC_vector _)
+ | (TYSPEC_vector _, TYSPEC_tuple _) -> fail ()
+
+ (* vector *)
+
+ | (TYSPEC_vector av, TYSPEC_vector bv) ->
+ unify_tyvars av bv;
+ TYSPEC_vector av
+ in
+ let c = ref result in
+ a := TYSPEC_equiv c;
+ b := TYSPEC_equiv c
+
+ and unify_ty (ty:Ast.ty) (tv:tyvar) : unit =
+ unify_tyvars (ref (TYSPEC_resolved ([||], ty))) tv
+ in
+
+ let rec unify_atom (atom:Ast.atom) (tv:tyvar) : unit =
+ match atom with
+ Ast.ATOM_literal { node = literal; id = _ } ->
+ let ty = match literal with
+ Ast.LIT_nil -> Ast.TY_nil
+ | Ast.LIT_bool _ -> Ast.TY_bool
+ | Ast.LIT_mach (mty, _, _) -> Ast.TY_mach mty
+ | Ast.LIT_int (_, _) -> Ast.TY_int
+ | Ast.LIT_uint (_, _) -> Ast.TY_uint
+ | Ast.LIT_char _ -> Ast.TY_char
+ in
+ unify_ty ty tv
+ | Ast.ATOM_lval lval -> unify_lval lval tv
+
+ and unify_expr (expr:Ast.expr) (tv:tyvar) : unit =
+ match expr with
+ Ast.EXPR_binary (binop, lhs, rhs) ->
+ let binop_sig = match binop with
+ Ast.BINOP_eq
+ | Ast.BINOP_ne -> BINOPSIG_comp_comp_bool
+
+ | Ast.BINOP_lt
+ | Ast.BINOP_le
+ | Ast.BINOP_ge
+ | Ast.BINOP_gt -> BINOPSIG_ord_ord_bool
+
+ | Ast.BINOP_or
+ | Ast.BINOP_and
+ | Ast.BINOP_xor
+ | Ast.BINOP_lsl
+ | Ast.BINOP_lsr
+ | Ast.BINOP_asr -> BINOPSIG_integ_integ_integ
+
+ | Ast.BINOP_add -> BINOPSIG_plus_plus_plus
+
+ | Ast.BINOP_sub
+ | Ast.BINOP_mul
+ | Ast.BINOP_div
+ | Ast.BINOP_mod -> BINOPSIG_num_num_num
+
+ | Ast.BINOP_send -> bug () "BINOP_send found in expr"
+ in
+ begin
+ match binop_sig with
+ BINOPSIG_bool_bool_bool ->
+ unify_atom lhs
+ (ref (TYSPEC_resolved ([||], Ast.TY_bool)));
+ unify_atom rhs
+ (ref (TYSPEC_resolved ([||], Ast.TY_bool)));
+ unify_ty Ast.TY_bool tv
+ | BINOPSIG_comp_comp_bool ->
+ let tv_a = ref TYSPEC_comparable in
+ unify_atom lhs tv_a;
+ unify_atom rhs tv_a;
+ unify_ty Ast.TY_bool tv
+ | BINOPSIG_ord_ord_bool ->
+ let tv_a = ref TYSPEC_ordered in
+ unify_atom lhs tv_a;
+ unify_atom rhs tv_a;
+ unify_ty Ast.TY_bool tv
+ | BINOPSIG_integ_integ_integ ->
+ let tv_a = ref TYSPEC_integral in
+ unify_atom lhs tv_a;
+ unify_atom rhs tv_a;
+ unify_tyvars tv tv_a
+ | BINOPSIG_num_num_num ->
+ let tv_a = ref TYSPEC_numeric in
+ unify_atom lhs tv_a;
+ unify_atom rhs tv_a;
+ unify_tyvars tv tv_a
+ | BINOPSIG_plus_plus_plus ->
+ let tv_a = ref TYSPEC_plusable in
+ unify_atom lhs tv_a;
+ unify_atom rhs tv_a;
+ unify_tyvars tv tv_a
+ end
+ | Ast.EXPR_unary (unop, atom) ->
+ begin
+ match unop with
+ Ast.UNOP_not ->
+ unify_atom atom
+ (ref (TYSPEC_resolved ([||], Ast.TY_bool)));
+ unify_ty Ast.TY_bool tv
+ | Ast.UNOP_bitnot ->
+ let tv_a = ref TYSPEC_integral in
+ unify_atom atom tv_a;
+ unify_tyvars tv tv_a
+ | Ast.UNOP_neg ->
+ let tv_a = ref TYSPEC_numeric in
+ unify_atom atom tv_a;
+ unify_tyvars tv tv_a
+ | Ast.UNOP_cast t ->
+ (*
+ * FIXME: check cast-validity in post-typecheck pass.
+ * Only some casts make sense.
+ *)
+ let tv_a = ref TYSPEC_all in
+ let t = Hashtbl.find cx.ctxt_all_cast_types t.id in
+ unify_atom atom tv_a;
+ unify_ty t tv
+ end
+ | Ast.EXPR_atom atom -> unify_atom atom tv
+
+ and unify_lval' (lval:Ast.lval) (tv:tyvar) : unit =
+ let note_args args =
+ iflog cx (fun _ -> log cx "noting lval '%a' type arguments: %a"
+ Ast.sprintf_lval lval Ast.sprintf_app_args args);
+ Hashtbl.add
+ cx.ctxt_call_lval_params
+ (lval_base_id lval)
+ args;
+ in
+ match lval with
+ Ast.LVAL_base nbi ->
+ let referent = Hashtbl.find cx.ctxt_lval_to_referent nbi.id in
+ begin
+ match Hashtbl.find cx.ctxt_all_defns referent with
+ DEFN_slot slot ->
+ iflog cx
+ begin
+ fun _ ->
+ let tv = Hashtbl.find bindings referent in
+ log cx "lval-base slot tyspec for %a = %s"
+ Ast.sprintf_lval lval (tyspec_to_str (!tv));
+ end;
+ unify_slot slot (Some referent) tv
+
+ | _ ->
+ let spec = (!(Hashtbl.find bindings referent)) in
+ let _ =
+ iflog cx
+ begin
+ fun _ ->
+ log cx "lval-base item tyspec for %a = %s"
+ Ast.sprintf_lval lval (tyspec_to_str spec);
+ log cx "unifying with supplied spec %s"
+ (tyspec_to_str !tv)
+ end
+ in
+ let tv =
+ match nbi.node with
+ Ast.BASE_ident _ -> tv
+ | Ast.BASE_app (_, args) ->
+ note_args args;
+ ref (TYSPEC_app (tv, args))
+ | _ -> err None "bad lval / tyspec combination"
+ in
+ unify_tyvars (ref spec) tv
+ end
+ | Ast.LVAL_ext (base, comp) ->
+ let base_ts = match comp with
+ Ast.COMP_named (Ast.COMP_ident id) ->
+ let names = Hashtbl.create 1 in
+ Hashtbl.add names id tv;
+ TYSPEC_dictionary names
+
+ | Ast.COMP_named (Ast.COMP_app (id, args)) ->
+ note_args args;
+ let tv = ref (TYSPEC_app (tv, args)) in
+ let names = Hashtbl.create 1 in
+ Hashtbl.add names id tv;
+ TYSPEC_dictionary names
+
+ | Ast.COMP_named (Ast.COMP_idx i) ->
+ let init j = if i + 1 == j then tv else ref TYSPEC_all in
+ TYSPEC_tuple (Array.init (i + 1) init)
+
+ | Ast.COMP_atom atom ->
+ unify_atom atom (ref (TYSPEC_resolved ([||], Ast.TY_int)));
+ TYSPEC_collection tv
+ in
+ let base_tv = ref base_ts in
+ unify_lval' base base_tv;
+ match !(resolve_tyvar base_tv) with
+ TYSPEC_resolved (_, ty) ->
+ unify_ty (slot_ty (project_type_to_slot ty comp)) tv
+ | _ ->
+ ()
+
+ and unify_lval (lval:Ast.lval) (tv:tyvar) : unit =
+ let id = lval_base_id lval in
+ (* Fetch lval with type components resolved. *)
+ let lval = Hashtbl.find cx.ctxt_all_lvals id in
+ iflog cx (fun _ -> log cx
+ "fetched resolved version of lval #%d = %a"
+ (int_of_node id) Ast.sprintf_lval lval);
+ Hashtbl.add lval_tyvars id tv;
+ unify_lval' lval tv
+
+ in
+ let gen_atom_tvs atoms =
+ let gen_atom_tv atom =
+ let tv = ref TYSPEC_all in
+ unify_atom atom tv;
+ tv
+ in
+ Array.map gen_atom_tv atoms
+ in
+ let visit_stmt_pre_full (stmt:Ast.stmt) : unit =
+
+ let check_callable out_tv callee args =
+ let in_tvs = gen_atom_tvs args in
+ let callee_tv = ref (TYSPEC_callable (out_tv, in_tvs)) in
+ unify_lval callee callee_tv;
+ in
+ match stmt.node with
+ Ast.STMT_spawn (out, _, callee, args) ->
+ let out_tv = ref (TYSPEC_resolved ([||], Ast.TY_nil)) in
+ unify_lval out (ref (TYSPEC_resolved ([||], Ast.TY_task)));
+ check_callable out_tv callee args
+
+ | Ast.STMT_init_rec (lval, fields, Some base) ->
+ let dct = Hashtbl.create 10 in
+ let tvrec = ref (TYSPEC_record dct) in
+ let add_field (ident, _, _, atom) =
+ let tv = ref TYSPEC_all in
+ unify_atom atom tv;
+ Hashtbl.add dct ident tv
+ in
+ Array.iter add_field fields;
+ let tvbase = ref TYSPEC_all in
+ unify_lval base tvbase;
+ unify_tyvars tvrec tvbase;
+ unify_lval lval tvrec
+
+ | Ast.STMT_init_rec (lval, fields, None) ->
+ let dct = Hashtbl.create 10 in
+ let add_field (ident, _, _, atom) =
+ let tv = ref TYSPEC_all in
+ unify_atom atom tv;
+ Hashtbl.add dct ident tv
+ in
+ Array.iter add_field fields;
+ unify_lval lval (ref (TYSPEC_record dct))
+
+ | Ast.STMT_init_tup (lval, members) ->
+ let member_to_tv (_, _, atom) =
+ let tv = ref TYSPEC_all in
+ unify_atom atom tv;
+ tv
+ in
+ let member_tvs = Array.map member_to_tv members in
+ unify_lval lval (ref (TYSPEC_tuple member_tvs))
+
+ | Ast.STMT_init_vec (lval, _, atoms) ->
+ let tv = ref TYSPEC_all in
+ let unify_with_tv atom = unify_atom atom tv in
+ Array.iter unify_with_tv atoms;
+ unify_lval lval (ref (TYSPEC_vector tv))
+
+ | Ast.STMT_init_str (lval, _) ->
+ unify_lval lval (ref (TYSPEC_resolved ([||], Ast.TY_str)))
+
+ | Ast.STMT_copy (lval, expr) ->
+ let tv = ref TYSPEC_all in
+ unify_expr expr tv;
+ unify_lval lval tv
+
+ | Ast.STMT_copy_binop (lval, binop, at) ->
+ let tv = ref TYSPEC_all in
+ unify_expr (Ast.EXPR_binary (binop, Ast.ATOM_lval lval, at)) tv;
+ unify_lval lval tv;
+
+ | Ast.STMT_call (out, callee, args) ->
+ let out_tv = ref TYSPEC_all in
+ unify_lval out out_tv;
+ check_callable out_tv callee args
+
+ | Ast.STMT_log atom -> unify_atom atom (ref TYSPEC_loggable)
+
+ | Ast.STMT_check_expr expr ->
+ unify_expr expr (ref (TYSPEC_resolved ([||], Ast.TY_bool)))
+
+ | Ast.STMT_check (_, check_calls) ->
+ let out_tv = ref (TYSPEC_resolved ([||], Ast.TY_bool)) in
+ Array.iter
+ (fun (callee, args) ->
+ check_callable out_tv callee args)
+ check_calls
+
+ | Ast.STMT_while { Ast.while_lval = (_, expr); Ast.while_body = _ } ->
+ unify_expr expr (ref (TYSPEC_resolved ([||], Ast.TY_bool)))
+
+ | Ast.STMT_if { Ast.if_test = if_test } ->
+ unify_expr if_test (ref (TYSPEC_resolved ([||], Ast.TY_bool)));
+
+ | Ast.STMT_decl _ -> ()
+
+ (* FIXME: deal with difference between return-type vs. put-type *)
+ | Ast.STMT_ret atom_opt
+ | Ast.STMT_put atom_opt ->
+ begin
+ match atom_opt with
+ None -> unify_ty Ast.TY_nil (retval_tv())
+ | Some atom -> unify_atom atom (retval_tv())
+ end
+
+ | Ast.STMT_be (callee, args) ->
+ check_callable (retval_tv()) callee args
+
+ | Ast.STMT_bind (bound, callee, arg_opts) ->
+ (* FIXME: handle binding type parameters eventually. *)
+ let out_tv = ref TYSPEC_all in
+ let residue = ref [] in
+ let gen_atom_opt_tvs atoms =
+ let gen_atom_tv atom_opt =
+ let tv = ref TYSPEC_all in
+ begin
+ match atom_opt with
+ None -> residue := tv :: (!residue);
+ | Some atom -> unify_atom atom tv
+ end;
+ tv
+ in
+ Array.map gen_atom_tv atoms
+ in
+
+ let in_tvs = gen_atom_opt_tvs arg_opts in
+ let arg_residue_tvs = Array.of_list (List.rev (!residue)) in
+ let callee_tv = ref (TYSPEC_callable (out_tv, in_tvs)) in
+ let bound_tv = ref (TYSPEC_callable (out_tv, arg_residue_tvs)) in
+ unify_lval callee callee_tv;
+ unify_lval bound bound_tv
+
+ | Ast.STMT_for_each fe ->
+ let out_tv = ref TYSPEC_all in
+ let (si, _) = fe.Ast.for_each_slot in
+ let (callee, args) = fe.Ast.for_each_call in
+ unify_slot si.node (Some si.id) out_tv;
+ check_callable out_tv callee args
+
+ | Ast.STMT_for fo ->
+ let mem_tv = ref TYSPEC_all in
+ let seq_tv = ref (TYSPEC_collection mem_tv) in
+ let (si, _) = fo.Ast.for_slot in
+ let (_, seq) = fo.Ast.for_seq in
+ unify_lval seq seq_tv;
+ unify_slot si.node (Some si.id) mem_tv
+
+ (* FIXME (issue #52): plenty more to handle here. *)
+ | _ ->
+ log cx "warning: not typechecking stmt %s\n"
+ (Ast.sprintf_stmt () stmt)
+ in
+
+ let visit_stmt_pre (stmt:Ast.stmt) : unit =
+ try
+ visit_stmt_pre_full stmt;
+ (*
+ * Reset any item-parameters that were resolved to types
+ * during inference for this statement.
+ *)
+ Hashtbl.iter
+ (fun _ params -> Array.iter (fun tv -> tv := TYSPEC_all) params)
+ item_params;
+ with
+ Semant_err (None, msg) ->
+ raise (Semant_err ((Some stmt.id), msg))
+ in
+
+ let enter_fn fn retspec =
+ let out = fn.Ast.fn_output_slot in
+ push_retval_tv (ref retspec);
+ unify_slot out.node (Some out.id) (retval_tv())
+ in
+
+ let visit_obj_fn_pre obj ident fn =
+ enter_fn fn.node TYSPEC_all;
+ inner.Walk.visit_obj_fn_pre obj ident fn
+ in
+
+ let visit_obj_fn_post obj ident fn =
+ inner.Walk.visit_obj_fn_post obj ident fn;
+ pop_retval_tv ();
+ in
+
+ let visit_mod_item_pre n p mod_item =
+ begin
+ try
+ match mod_item.node.Ast.decl_item with
+ Ast.MOD_ITEM_fn fn ->
+ enter_fn fn TYSPEC_all
+
+ | _ -> ()
+ with Semant_err (None, msg) ->
+ raise (Semant_err ((Some mod_item.id), msg))
+ end;
+ inner.Walk.visit_mod_item_pre n p mod_item
+ in
+
+ let path_name (_:unit) : string =
+ string_of_name (Walk.path_to_name path)
+ in
+
+ let visit_mod_item_post n p mod_item =
+ inner.Walk.visit_mod_item_post n p mod_item;
+ match mod_item.node.Ast.decl_item with
+
+ | Ast.MOD_ITEM_fn _ ->
+ pop_retval_tv ();
+ if (Some (path_name())) = cx.ctxt_main_name
+ then
+ begin
+ match Hashtbl.find cx.ctxt_all_item_types mod_item.id with
+ Ast.TY_fn (tsig, _) ->
+ begin
+ let vec_str =
+ interior_slot (Ast.TY_vec
+ (interior_slot Ast.TY_str))
+ in
+ match tsig.Ast.sig_input_slots with
+ [| |] -> ()
+ | [| vs |] when vs = vec_str -> ()
+ | _ -> err (Some mod_item.id)
+ "main fn has bad type signature"
+ end
+ | _ ->
+ err (Some mod_item.id) "main item is not a function"
+ end
+ | _ -> ()
+ in
+
+ {
+ inner with
+ Walk.visit_mod_item_pre = visit_mod_item_pre;
+ Walk.visit_mod_item_post = visit_mod_item_post;
+ Walk.visit_obj_fn_pre = visit_obj_fn_pre;
+ Walk.visit_obj_fn_post = visit_obj_fn_post;
+ Walk.visit_stmt_pre = visit_stmt_pre
+ }
+
+ in
+ try
+ let auto_queue = Queue.create () in
+
+ let init_slot_tyvar id defn =
+ match defn with
+ DEFN_slot { Ast.slot_mode = _; Ast.slot_ty = None } ->
+ Queue.add id auto_queue;
+ Hashtbl.add bindings id (ref TYSPEC_all)
+ | DEFN_slot { Ast.slot_mode = _; Ast.slot_ty = Some ty } ->
+ let _ = iflog cx (fun _ -> log cx "initial slot #%d type: %a"
+ (int_of_node id) Ast.sprintf_ty ty)
+ in
+ Hashtbl.add bindings id (ref (TYSPEC_resolved ([||], ty)))
+ | _ -> ()
+ in
+
+ let init_item_tyvar id ty =
+ let _ = iflog cx (fun _ -> log cx "initial item #%d type: %a"
+ (int_of_node id) Ast.sprintf_ty ty)
+ in
+ let params =
+ match Hashtbl.find cx.ctxt_all_defns id with
+ DEFN_item i -> Array.map (fun p -> p.node) i.Ast.decl_params
+ | DEFN_obj_fn _ -> [| |]
+ | DEFN_obj_drop _ -> [| |]
+ | DEFN_loop_body _ -> [| |]
+ | _ -> err (Some id) "expected item defn for item tyvar"
+ in
+ let spec = TYSPEC_resolved (params, ty) in
+ Hashtbl.add bindings id (ref spec)
+ in
+
+ let init_mod_dict id defn =
+ let rec tv_of_item id item =
+ match item.Ast.decl_item with
+ Ast.MOD_ITEM_mod (_, items) ->
+ if Hashtbl.mem bindings id
+ then Hashtbl.find bindings id
+ else
+ let dict = htab_map items
+ (fun i item -> (i, tv_of_item item.id item.node))
+ in
+ let spec = TYSPEC_dictionary dict in
+ let tv = ref spec in
+ Hashtbl.add bindings id tv;
+ tv
+ | _ ->
+ Hashtbl.find bindings id
+ in
+ match defn with
+ DEFN_item ({ Ast.decl_item=Ast.MOD_ITEM_mod _ } as item) ->
+ ignore (tv_of_item id item)
+ | _ -> ()
+ in
+ Hashtbl.iter init_slot_tyvar cx.ctxt_all_defns;
+ Hashtbl.iter init_item_tyvar cx.ctxt_all_item_types;
+ Hashtbl.iter init_mod_dict cx.ctxt_all_defns;
+ Walk.walk_crate
+ (Walk.path_managing_visitor path
+ (Walk.mod_item_logging_visitor
+ (log cx "typechecking pass: %s")
+ path
+ (visitor cx Walk.empty_visitor)))
+ crate;
+
+ let update_auto_tyvar id ty =
+ let defn = Hashtbl.find cx.ctxt_all_defns id in
+ match defn with
+ DEFN_slot slot_defn ->
+ Hashtbl.replace cx.ctxt_all_defns id
+ (DEFN_slot { slot_defn with Ast.slot_ty = Some ty })
+ | _ -> bug () "check_auto_tyvar: no slot defn"
+ in
+
+ let get_resolved_ty tv id =
+ let ts = !(resolve_tyvar tv) in
+ match ts with
+ TYSPEC_resolved ([||], ty) -> ty
+ | TYSPEC_vector (tv) ->
+ begin
+ match !(resolve_tyvar tv) with
+ TYSPEC_resolved ([||], ty) ->
+ (Ast.TY_vec (interior_slot ty))
+ | _ ->
+ err (Some id)
+ "unresolved vector-element type in %s (%d)"
+ (tyspec_to_str ts) (int_of_node id)
+ end
+ | _ -> err (Some id)
+ "unresolved type %s (%d)"
+ (tyspec_to_str ts)
+ (int_of_node id)
+ in
+
+ let check_auto_tyvar id =
+ let tv = Hashtbl.find bindings id in
+ let ty = get_resolved_ty tv id in
+ update_auto_tyvar id ty
+ in
+
+ let record_lval_ty id tv =
+ let ty = get_resolved_ty tv id in
+ Hashtbl.add cx.ctxt_all_lval_types id ty
+ in
+
+ Queue.iter check_auto_tyvar auto_queue;
+ Hashtbl.iter record_lval_ty lval_tyvars;
+ with Semant_err (ido, str) -> report_err cx ido str
+;;
+
+(*
+ * 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/me/typestate.ml b/src/boot/me/typestate.ml
new file mode 100644
index 00000000..4671d0f4
--- /dev/null
+++ b/src/boot/me/typestate.ml
@@ -0,0 +1,1089 @@
+open Semant;;
+open Common;;
+
+
+let log cx = Session.log "typestate"
+ cx.ctxt_sess.Session.sess_log_typestate
+ cx.ctxt_sess.Session.sess_log_out
+;;
+
+let iflog cx thunk =
+ if cx.ctxt_sess.Session.sess_log_typestate
+ then thunk ()
+ else ()
+;;
+
+let name_base_to_slot_key (nb:Ast.name_base) : Ast.slot_key =
+ match nb with
+ Ast.BASE_ident ident -> Ast.KEY_ident ident
+ | Ast.BASE_temp tmp -> Ast.KEY_temp tmp
+ | Ast.BASE_app _ -> bug () "name_base_to_slot_key on parametric name"
+;;
+
+let determine_constr_key
+ (cx:ctxt)
+ (scopes:(scope list))
+ (formal_base:node_id option)
+ (c:Ast.constr)
+ : constr_key =
+
+ let cid =
+ match lookup_by_name cx scopes c.Ast.constr_name with
+ Some (_, cid) ->
+ if referent_is_item cx cid
+ then
+ begin
+ match Hashtbl.find cx.ctxt_all_item_types cid with
+ Ast.TY_fn (_, taux) ->
+ begin
+ if taux.Ast.fn_effect = Ast.PURE
+ then cid
+ else err (Some cid) "impure function used in constraint"
+ end
+ | _ -> bug () "bad type of predicate"
+ end
+ else
+ bug () "slot used as predicate"
+ | None -> bug () "predicate not found"
+ in
+
+ let constr_arg_of_carg carg =
+ match carg with
+ Ast.CARG_path pth ->
+ let rec node_base_of pth =
+ match pth with
+ Ast.CARG_base Ast.BASE_formal ->
+ begin
+ match formal_base with
+ Some id -> id
+ | None ->
+ bug () "formal symbol * used in free constraint"
+ end
+ | Ast.CARG_ext (pth, _) -> node_base_of pth
+ | Ast.CARG_base (Ast.BASE_named nb) ->
+ begin
+ match lookup_by_name cx scopes (Ast.NAME_base nb) with
+ None -> bug () "constraint-arg not found"
+ | Some (_, aid) ->
+ if referent_is_slot cx aid
+ then
+ if type_has_state
+ (slot_ty (referent_to_slot cx aid))
+ then err (Some aid)
+ "predicate applied to slot of mutable type"
+ else aid
+ else
+ (* Items are always constant, they're ok.
+ * Weird to be using them in a constr, but ok. *)
+ aid
+ end
+ in
+ Constr_arg_node (node_base_of pth, pth)
+
+ | Ast.CARG_lit lit -> Constr_arg_lit lit
+ in
+ Constr_pred (cid, Array.map constr_arg_of_carg c.Ast.constr_args)
+;;
+
+let fmt_constr_key cx ckey =
+ match ckey with
+ Constr_pred (cid, args) ->
+ let fmt_constr_arg carg =
+ match carg with
+ Constr_arg_lit lit ->
+ Ast.fmt_to_str Ast.fmt_lit lit
+ | Constr_arg_node (id, pth) ->
+ let rec fmt_pth pth =
+ match pth with
+ Ast.CARG_base _ ->
+ if referent_is_slot cx id
+ then
+ let key = Hashtbl.find cx.ctxt_slot_keys id in
+ Ast.fmt_to_str Ast.fmt_slot_key key
+ else
+ let n = Hashtbl.find cx.ctxt_all_item_names id in
+ Ast.fmt_to_str Ast.fmt_name n
+ | Ast.CARG_ext (pth, nc) ->
+ let b = fmt_pth pth in
+ b ^ (Ast.fmt_to_str Ast.fmt_name_component nc)
+ in
+ fmt_pth pth
+ in
+ let pred_name = Hashtbl.find cx.ctxt_all_item_names cid in
+ Printf.sprintf "%s(%s)"
+ (Ast.fmt_to_str Ast.fmt_name pred_name)
+ (String.concat ", "
+ (List.map
+ fmt_constr_arg
+ (Array.to_list args)))
+
+ | Constr_init n when Hashtbl.mem cx.ctxt_slot_keys n ->
+ Printf.sprintf "<init #%d = %s>"
+ (int_of_node n)
+ (Ast.fmt_to_str Ast.fmt_slot_key (Hashtbl.find cx.ctxt_slot_keys n))
+ | Constr_init n ->
+ Printf.sprintf "<init #%d>" (int_of_node n)
+;;
+
+let entry_keys header constrs resolver =
+ let init_keys =
+ Array.map
+ (fun (sloti, _) -> (Constr_init sloti.id))
+ header
+ in
+ let names =
+ Array.map
+ (fun (_, ident) -> (Some (Ast.BASE_ident ident)))
+ header
+ in
+ let input_constrs =
+ Array.map (apply_names_to_constr names) constrs in
+ let input_keys = Array.map resolver input_constrs in
+ (input_keys, init_keys)
+;;
+
+let obj_keys ob resolver =
+ entry_keys ob.Ast.obj_state ob.Ast.obj_constrs resolver
+;;
+
+let fn_keys fn resolver =
+ entry_keys fn.Ast.fn_input_slots fn.Ast.fn_input_constrs resolver
+;;
+
+let constr_id_assigning_visitor
+ (cx:ctxt)
+ (scopes:(scope list) ref)
+ (idref:int ref)
+ (inner:Walk.visitor)
+ : Walk.visitor =
+
+ let resolve_constr_to_key
+ (formal_base:node_id)
+ (constr:Ast.constr)
+ : constr_key =
+ determine_constr_key cx (!scopes) (Some formal_base) constr
+ in
+
+ let note_constr_key key =
+ if not (Hashtbl.mem cx.ctxt_constr_ids key)
+ then
+ begin
+ let cid = Constr (!idref) in
+ iflog cx
+ (fun _ -> log cx "assigning constr id #%d to constr %s"
+ (!idref) (fmt_constr_key cx key));
+ incr idref;
+ htab_put cx.ctxt_constrs cid key;
+ htab_put cx.ctxt_constr_ids key cid;
+ end
+ in
+
+ let note_keys = Array.iter note_constr_key in
+
+ let visit_mod_item_pre n p i =
+ let resolver = resolve_constr_to_key i.id in
+ begin
+ match i.node.Ast.decl_item with
+ Ast.MOD_ITEM_fn f ->
+ let (input_keys, init_keys) = fn_keys f resolver in
+ note_keys input_keys;
+ note_keys init_keys
+ | Ast.MOD_ITEM_obj ob ->
+ let (input_keys, init_keys) = obj_keys ob resolver in
+ note_keys input_keys;
+ note_keys init_keys
+ | _ -> ()
+ end;
+ inner.Walk.visit_mod_item_pre n p i
+ in
+
+ let visit_constr_pre formal_base c =
+ let key = determine_constr_key cx (!scopes) formal_base c in
+ note_constr_key key;
+ inner.Walk.visit_constr_pre formal_base c
+ in
+ (*
+ * We want to generate, for any call site, a variant of
+ * the callee's entry typestate specialized to the arguments
+ * that the caller passes.
+ *
+ * Also, for any slot-decl node, we have to generate a
+ * variant of Constr_init for the slot (because the slot is
+ * the sort of thing that can vary in init-ness over time).
+ *)
+ let visit_stmt_pre s =
+ begin
+ match s.node with
+ Ast.STMT_call (_, lv, args) ->
+ let referent = lval_to_referent cx (lval_base_id lv) in
+ let referent_ty = lval_ty cx lv in
+ begin
+ match referent_ty with
+ Ast.TY_fn (tsig,_) ->
+ let constrs = tsig.Ast.sig_input_constrs in
+ let names = atoms_to_names args in
+ let constrs' =
+ Array.map (apply_names_to_constr names) constrs
+ in
+ Array.iter (visit_constr_pre (Some referent)) constrs'
+
+ | _ -> ()
+ end
+
+ | _ -> ()
+ end;
+ inner.Walk.visit_stmt_pre s
+ in
+
+ let visit_slot_identified_pre s =
+ note_constr_key (Constr_init s.id);
+ inner.Walk.visit_slot_identified_pre s
+ in
+ { inner with
+ Walk.visit_mod_item_pre = visit_mod_item_pre;
+ Walk.visit_slot_identified_pre = visit_slot_identified_pre;
+ Walk.visit_stmt_pre = visit_stmt_pre;
+ Walk.visit_constr_pre = visit_constr_pre }
+;;
+
+let bitmap_assigning_visitor
+ (cx:ctxt)
+ (idref:int ref)
+ (inner:Walk.visitor)
+ : Walk.visitor =
+ let visit_stmt_pre s =
+ iflog cx (fun _ -> log cx "building %d-entry bitmap for node %d"
+ (!idref) (int_of_node s.id));
+ htab_put cx.ctxt_preconditions s.id (Bits.create (!idref) false);
+ htab_put cx.ctxt_postconditions s.id (Bits.create (!idref) false);
+ htab_put cx.ctxt_prestates s.id (Bits.create (!idref) false);
+ htab_put cx.ctxt_poststates s.id (Bits.create (!idref) false);
+ inner.Walk.visit_stmt_pre s
+ in
+ let visit_block_pre b =
+ iflog cx (fun _ -> log cx "building %d-entry bitmap for node %d"
+ (!idref) (int_of_node b.id));
+ htab_put cx.ctxt_preconditions b.id (Bits.create (!idref) false);
+ htab_put cx.ctxt_postconditions b.id (Bits.create (!idref) false);
+ htab_put cx.ctxt_prestates b.id (Bits.create (!idref) false);
+ htab_put cx.ctxt_poststates b.id (Bits.create (!idref) false);
+ inner.Walk.visit_block_pre b
+ in
+ { inner with
+ Walk.visit_stmt_pre = visit_stmt_pre;
+ Walk.visit_block_pre = visit_block_pre }
+;;
+
+let condition_assigning_visitor
+ (cx:ctxt)
+ (scopes:(scope list) ref)
+ (inner:Walk.visitor)
+ : Walk.visitor =
+
+ let raise_bits (bitv:Bits.t) (keys:constr_key array) : unit =
+ Array.iter
+ (fun key ->
+ let cid = Hashtbl.find cx.ctxt_constr_ids key in
+ let i = int_of_constr cid in
+ iflog cx (fun _ -> log cx "setting bit %d, constraint %s"
+ i (fmt_constr_key cx key));
+ Bits.set bitv (int_of_constr cid) true)
+ keys
+ in
+
+ let slot_inits ss = Array.map (fun s -> Constr_init s) ss in
+
+ let raise_postcondition (id:node_id) (keys:constr_key array) : unit =
+ let bitv = Hashtbl.find cx.ctxt_postconditions id in
+ raise_bits bitv keys
+ in
+
+ let raise_precondition (id:node_id) (keys:constr_key array) : unit =
+ let bitv = Hashtbl.find cx.ctxt_preconditions id in
+ raise_bits bitv keys
+ in
+
+ let resolve_constr_to_key
+ (formal_base:node_id option)
+ (constr:Ast.constr)
+ : constr_key =
+ determine_constr_key cx (!scopes) formal_base constr
+ in
+
+ let raise_entry_state input_keys init_keys block =
+ iflog cx
+ (fun _ -> log cx
+ "setting entry state as block %d postcondition (\"entry\" prestate)"
+ (int_of_node block.id));
+ raise_postcondition block.id input_keys;
+ raise_postcondition block.id init_keys;
+ iflog cx (fun _ -> log cx "done setting block postcondition")
+ in
+
+ let visit_mod_item_pre n p i =
+ begin
+ match i.node.Ast.decl_item with
+ Ast.MOD_ITEM_fn f ->
+ let (input_keys, init_keys) =
+ fn_keys f (resolve_constr_to_key (Some i.id))
+ in
+ raise_entry_state input_keys init_keys f.Ast.fn_body
+
+ | _ -> ()
+ end;
+ inner.Walk.visit_mod_item_pre n p i
+ in
+
+ let visit_obj_fn_pre obj ident fn =
+ let (obj_input_keys, obj_init_keys) =
+ obj_keys obj.node (resolve_constr_to_key (Some obj.id))
+ in
+ let (fn_input_keys, fn_init_keys) =
+ fn_keys fn.node (resolve_constr_to_key (Some fn.id))
+ in
+ raise_entry_state obj_input_keys obj_init_keys fn.node.Ast.fn_body;
+ raise_entry_state fn_input_keys fn_init_keys fn.node.Ast.fn_body;
+ inner.Walk.visit_obj_fn_pre obj ident fn
+ in
+
+ let visit_obj_drop_pre obj b =
+ let (obj_input_keys, obj_init_keys) =
+ obj_keys obj.node (resolve_constr_to_key (Some obj.id))
+ in
+ raise_entry_state obj_input_keys obj_init_keys b;
+ inner.Walk.visit_obj_drop_pre obj b
+ in
+
+ let visit_callable_pre s dst lv args =
+ let referent_ty = lval_ty cx lv in
+ begin
+ match referent_ty with
+ Ast.TY_fn (tsig,_) ->
+ let formal_constrs = tsig.Ast.sig_input_constrs in
+ let names = atoms_to_names args in
+ let constrs =
+ Array.map (apply_names_to_constr names) formal_constrs
+ in
+ let keys = Array.map (resolve_constr_to_key None) constrs in
+ raise_precondition s.id keys
+ | _ -> ()
+ end;
+ begin
+ let postcond =
+ slot_inits (lval_slots cx dst)
+ in
+ raise_postcondition s.id postcond
+ end
+ in
+
+ let visit_stmt_pre s =
+ begin
+ match s.node with
+ Ast.STMT_check (constrs, _) ->
+ let postcond = Array.map (resolve_constr_to_key None) constrs in
+ raise_postcondition s.id postcond
+
+ | Ast.STMT_recv (dst, src) ->
+ let precond = slot_inits (lval_slots cx src) in
+ let postcond = slot_inits (lval_slots cx dst) in
+ raise_precondition s.id precond;
+ raise_postcondition s.id postcond
+
+ | Ast.STMT_send (dst, src) ->
+ let precond = Array.append
+ (slot_inits (lval_slots cx dst))
+ (slot_inits (lval_slots cx src))
+ in
+ raise_precondition s.id precond;
+
+ | Ast.STMT_init_rec (dst, entries, base) ->
+ let base_slots =
+ begin
+ match base with
+ None -> [| |]
+ | Some lval -> lval_slots cx lval
+ end
+ in
+ let precond = slot_inits
+ (Array.append (rec_inputs_slots cx entries) base_slots)
+ in
+ let postcond = slot_inits (lval_slots cx dst) in
+ raise_precondition s.id precond;
+ raise_postcondition s.id postcond
+
+ | Ast.STMT_init_tup (dst, modes_atoms) ->
+ let precond = slot_inits
+ (tup_inputs_slots cx modes_atoms)
+ in
+ let postcond = slot_inits (lval_slots cx dst) in
+ raise_precondition s.id precond;
+ raise_postcondition s.id postcond
+
+ | Ast.STMT_init_vec (dst, _, atoms) ->
+ let precond = slot_inits (atoms_slots cx atoms) in
+ let postcond = slot_inits (lval_slots cx dst) in
+ raise_precondition s.id precond;
+ raise_postcondition s.id postcond
+
+ | Ast.STMT_init_str (dst, _) ->
+ let postcond = slot_inits (lval_slots cx dst) in
+ raise_postcondition s.id postcond
+
+ | Ast.STMT_init_port dst ->
+ let postcond = slot_inits (lval_slots cx dst) in
+ raise_postcondition s.id postcond
+
+ | Ast.STMT_init_chan (dst, port) ->
+ let precond = slot_inits (lval_option_slots cx port) in
+ let postcond = slot_inits (lval_slots cx dst) in
+ raise_precondition s.id precond;
+ raise_postcondition s.id postcond
+
+ | Ast.STMT_copy (dst, src) ->
+ let precond = slot_inits (expr_slots cx src) in
+ let postcond = slot_inits (lval_slots cx dst) in
+ raise_precondition s.id precond;
+ raise_postcondition s.id postcond
+
+ | Ast.STMT_copy_binop (dst, _, src) ->
+ let dst_init = slot_inits (lval_slots cx dst) in
+ let src_init = slot_inits (atom_slots cx src) in
+ let precond = Array.append dst_init src_init in
+ raise_precondition s.id precond;
+
+ | Ast.STMT_spawn (dst, _, lv, args)
+ | Ast.STMT_call (dst, lv, args) ->
+ visit_callable_pre s dst lv args
+
+ | Ast.STMT_bind (dst, lv, args_opt) ->
+ let args = arr_map_partial args_opt (fun a -> a) in
+ visit_callable_pre s dst lv args
+
+ | Ast.STMT_ret (Some at) ->
+ let precond = slot_inits (atom_slots cx at) in
+ raise_precondition s.id precond
+
+ | Ast.STMT_put (Some at) ->
+ let precond = slot_inits (atom_slots cx at) in
+ raise_precondition s.id precond
+
+ | Ast.STMT_join lval ->
+ let precond = slot_inits (lval_slots cx lval) in
+ raise_precondition s.id precond
+
+ | Ast.STMT_log atom ->
+ let precond = slot_inits (atom_slots cx atom) in
+ raise_precondition s.id precond
+
+ | Ast.STMT_check_expr expr ->
+ let precond = slot_inits (expr_slots cx expr) in
+ raise_precondition s.id precond
+
+ | Ast.STMT_while sw ->
+ let (_, expr) = sw.Ast.while_lval in
+ let precond = slot_inits (expr_slots cx expr) in
+ raise_precondition s.id precond
+
+ | Ast.STMT_alt_tag at ->
+ let precond = slot_inits (lval_slots cx at.Ast.alt_tag_lval) in
+ let visit_arm { node = (pat, block) } =
+ (* FIXME: propagate tag-carried constrs here. *)
+ let rec get_slots pat =
+ match pat with
+ Ast.PAT_slot header_slot -> [| header_slot |]
+ | Ast.PAT_tag (_, pats) ->
+ Array.concat (List.map get_slots (Array.to_list pats))
+ | _ -> [| |]
+ in
+ let header_slots = get_slots pat in
+ let (input_keys, init_keys) =
+ entry_keys header_slots [| |] (resolve_constr_to_key None)
+ in
+ raise_entry_state input_keys init_keys block
+ in
+ raise_precondition s.id precond;
+ Array.iter visit_arm at.Ast.alt_tag_arms
+
+ | Ast.STMT_for_each fe ->
+ let (si, _) = fe.Ast.for_each_slot in
+ let block_entry_state = [| Constr_init si.id |] in
+ raise_postcondition fe.Ast.for_each_body.id block_entry_state
+
+ | Ast.STMT_for fo ->
+ let (si, _) = fo.Ast.for_slot in
+ let block_entry_state = [| Constr_init si.id |] in
+ raise_postcondition fo.Ast.for_body.id block_entry_state
+
+ | _ -> ()
+ end;
+ inner.Walk.visit_stmt_pre s
+ in
+ { inner with
+ Walk.visit_mod_item_pre = visit_mod_item_pre;
+ Walk.visit_obj_fn_pre = visit_obj_fn_pre;
+ Walk.visit_obj_drop_pre = visit_obj_drop_pre;
+ Walk.visit_stmt_pre = visit_stmt_pre }
+;;
+
+let lset_add (x:node_id) (xs:node_id list) : node_id list =
+ if List.mem x xs
+ then xs
+ else x::xs
+;;
+
+let lset_remove (x:node_id) (xs:node_id list) : node_id list =
+ List.filter (fun a -> not (a = x)) xs
+;;
+
+let lset_union (xs:node_id list) (ys:node_id list) : node_id list =
+ List.fold_left (fun ns n -> lset_add n ns) xs ys
+;;
+
+let lset_diff (xs:node_id list) (ys:node_id list) : node_id list =
+ List.fold_left (fun ns n -> lset_remove n ns) xs ys
+;;
+
+let lset_fmt lset =
+ "[" ^
+ (String.concat ", "
+ (List.map
+ (fun n -> string_of_int (int_of_node n)) lset)) ^
+ "]"
+;;
+
+type node_graph = (node_id, (node_id list)) Hashtbl.t;;
+
+let graph_sequence_building_visitor
+ (cx:ctxt)
+ (graph:node_graph)
+ (inner:Walk.visitor)
+ : Walk.visitor =
+
+ (* Flow each stmt to its sequence-successor. *)
+ let visit_stmts stmts =
+ let len = Array.length stmts in
+ for i = 0 to len - 2
+ do
+ let stmt = stmts.(i) in
+ let next = stmts.(i+1) in
+ log cx "sequential stmt edge %d -> %d"
+ (int_of_node stmt.id) (int_of_node next.id);
+ htab_put graph stmt.id [next.id]
+ done;
+ (* Flow last node to nowhere. *)
+ if len > 0
+ then htab_put graph stmts.(len-1).id []
+ in
+
+ let visit_stmt_pre s =
+ (* Sequence the prelude nodes on special stmts. *)
+ begin
+ match s.node with
+ Ast.STMT_while sw ->
+ let (stmts, _) = sw.Ast.while_lval in
+ visit_stmts stmts
+ | _ -> ()
+ end;
+ inner.Walk.visit_stmt_pre s
+ in
+
+ let visit_block_pre b =
+ visit_stmts b.node;
+ inner.Walk.visit_block_pre b
+ in
+
+ { inner with
+ Walk.visit_stmt_pre = visit_stmt_pre;
+ Walk.visit_block_pre = visit_block_pre }
+;;
+
+let add_flow_edges (graph:node_graph) (n:node_id) (dsts:node_id list) : unit =
+ let existing = Hashtbl.find graph n in
+ Hashtbl.replace graph n (lset_union existing dsts)
+;;
+
+let remove_flow_edges
+ (graph:node_graph)
+ (n:node_id)
+ (dsts:node_id list)
+ : unit =
+ let existing = Hashtbl.find graph n in
+ Hashtbl.replace graph n (lset_diff existing dsts)
+;;
+
+let graph_general_block_structure_building_visitor
+ ((*cx*)_:ctxt)
+ (graph:node_graph)
+ (inner:Walk.visitor)
+ : Walk.visitor =
+
+ let stmts = Stack.create () in
+
+ let visit_stmt_pre s =
+ Stack.push s stmts;
+ inner.Walk.visit_stmt_pre s
+ in
+
+ let visit_stmt_post s =
+ inner.Walk.visit_stmt_post s;
+ ignore (Stack.pop stmts)
+ in
+
+ let visit_block_pre b =
+ begin
+ let len = Array.length b.node in
+
+ (* Flow container-stmt to block, save existing out-edges for below. *)
+ let dsts =
+ if Stack.is_empty stmts
+ then []
+ else
+ let s = Stack.top stmts in
+ let dsts = Hashtbl.find graph s.id in
+ add_flow_edges graph s.id [b.id];
+ dsts
+ in
+
+ (*
+ * If block has len,
+ * then flow block to block.node.(0) and block.node.(len-1) to dsts
+ * else flow block to dsts
+ *
+ * so AST:
+ *
+ * block#n{ stmt#0 ... stmt#k };
+ * stmt#j;
+ *
+ * turns into graph:
+ *
+ * block#n -> stmt#0 -> ... -> stmt#k -> stmt#j
+ *
+ *)
+
+ if len > 0
+ then
+ begin
+ htab_put graph b.id [b.node.(0).id];
+ add_flow_edges graph b.node.(len-1).id dsts
+ end
+ else
+ htab_put graph b.id dsts
+ end;
+ inner.Walk.visit_block_pre b
+ in
+
+ { inner with
+ Walk.visit_stmt_pre = visit_stmt_pre;
+ Walk.visit_stmt_post = visit_stmt_post;
+ Walk.visit_block_pre = visit_block_pre }
+;;
+
+
+let graph_special_block_structure_building_visitor
+ ((*cx*)_:ctxt)
+ (graph:(node_id, (node_id list)) Hashtbl.t)
+ (inner:Walk.visitor)
+ : Walk.visitor =
+
+ let visit_stmt_pre s =
+ begin
+ match s.node with
+
+ | Ast.STMT_if sif ->
+ (*
+ * Drop implicit stmt-bypass edge(s);
+ * can only flow to inner block(s).
+ *)
+ let block_ids =
+ [sif.Ast.if_then.id] @
+ match sif.Ast.if_else with
+ None -> []
+ | Some eb -> [eb.id]
+ in
+ Hashtbl.replace graph s.id block_ids
+
+ | Ast.STMT_while sw ->
+ (* There are a bunch of rewirings to do on 'while' nodes. *)
+
+ begin
+ let dsts = Hashtbl.find graph s.id in
+ let body = sw.Ast.while_body in
+ let succ_stmts =
+ List.filter (fun x -> not (x = body.id)) dsts
+ in
+
+ let (pre_loop_stmts, _) = sw.Ast.while_lval in
+ let loop_head_id =
+ (* Splice loop prelude into flow graph, save loop-head
+ * node.
+ *)
+ let slen = Array.length pre_loop_stmts in
+ if slen > 0
+ then
+ begin
+ remove_flow_edges graph s.id [body.id];
+ add_flow_edges graph s.id [pre_loop_stmts.(0).id];
+ add_flow_edges graph
+ pre_loop_stmts.(slen-1).id [body.id];
+ pre_loop_stmts.(slen - 1).id
+ end
+ else
+ body.id
+ in
+
+ (* Always flow s into the loop prelude; prelude may end
+ * loop.
+ *)
+ remove_flow_edges graph s.id succ_stmts;
+ add_flow_edges graph loop_head_id succ_stmts;
+
+ (* Flow loop-end to loop-head. *)
+ let blen = Array.length body.node in
+ if blen > 0
+ then add_flow_edges graph
+ body.node.(blen - 1).id [loop_head_id]
+ else add_flow_edges graph
+ body.id [loop_head_id]
+ end
+
+ | Ast.STMT_alt_tag at ->
+ let dsts = Hashtbl.find graph s.id in
+ let arm_blocks =
+ let arm_block_id { node = (_, block) } = block.id in
+ Array.to_list (Array.map arm_block_id at.Ast.alt_tag_arms)
+ in
+ let succ_stmts =
+ List.filter (fun x -> not (List.mem x arm_blocks)) dsts
+ in
+ remove_flow_edges graph s.id succ_stmts
+
+ | _ -> ()
+ end;
+ inner.Walk.visit_stmt_post s
+ in
+ { inner with
+ Walk.visit_stmt_pre = visit_stmt_pre }
+;;
+
+let find_roots
+ (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;
+ roots
+;;
+
+let run_dataflow cx graph : unit =
+ let roots = find_roots graph in
+ let nodes = Queue.create () in
+ let progress = ref true in
+ let fmt_constr_bitv bitv =
+ String.concat ", "
+ (List.map
+ (fun i ->
+ fmt_constr_key cx
+ (Hashtbl.find cx.ctxt_constrs (Constr i)))
+ (Bits.to_list bitv))
+ in
+ let set_bits dst src =
+ if Bits.copy dst src
+ then (progress := true;
+ iflog cx (fun _ -> log cx "made progress setting bits"))
+ in
+ let intersect_bits dst src =
+ if Bits.intersect dst src
+ then (progress := true;
+ iflog cx (fun _ -> log cx
+ "made progress intersecting bits"))
+ in
+ let raise_bits dst src =
+ if Bits.union dst src
+ then (progress := true;
+ iflog cx (fun _ -> log cx
+ "made progress unioning bits"))
+ in
+ let iter = ref 0 in
+ let written = Hashtbl.create 0 in
+ Hashtbl.iter (fun n _ -> Queue.push n nodes) roots;
+ while !progress do
+ incr iter;
+ progress := false;
+ iflog cx (fun _ -> log cx "dataflow pass %d" (!iter));
+ Queue.iter
+ begin
+ fun node ->
+ let prestate = Hashtbl.find cx.ctxt_prestates node in
+ let postcond = Hashtbl.find cx.ctxt_postconditions node in
+ let poststate = Hashtbl.find cx.ctxt_poststates node in
+ iflog cx (fun _ -> log cx "stmt %d: '%s'" (int_of_node node)
+ (match htab_search cx.ctxt_all_stmts node with
+ None -> "??"
+ | Some stmt -> Ast.fmt_to_str Ast.fmt_stmt stmt));
+ iflog cx (fun _ -> log cx "stmt %d:" (int_of_node node));
+ iflog cx (fun _ -> log cx
+ " prestate %s" (fmt_constr_bitv prestate));
+ raise_bits poststate prestate;
+ raise_bits poststate postcond;
+ iflog cx (fun _ -> log cx
+ " poststate %s" (fmt_constr_bitv poststate));
+ Hashtbl.replace written node ();
+ let successors = Hashtbl.find graph node in
+ let i = int_of_node node in
+ iflog cx (fun _ -> log cx
+ "out-edges for %d: %s" i (lset_fmt successors));
+ List.iter
+ begin
+ fun succ ->
+ let succ_prestates =
+ Hashtbl.find cx.ctxt_prestates succ
+ in
+ if Hashtbl.mem written succ
+ then
+ begin
+ intersect_bits succ_prestates poststate;
+ Hashtbl.replace written succ ()
+ end
+ else
+ begin
+ progress := true;
+ Queue.push succ nodes;
+ set_bits succ_prestates poststate
+ end
+ end
+ successors
+ end
+ nodes
+ done
+;;
+
+let typestate_verify_visitor
+ (cx:ctxt)
+ (inner:Walk.visitor)
+ : Walk.visitor =
+ let visit_stmt_pre s =
+ let prestate = Hashtbl.find cx.ctxt_prestates s.id in
+ let precond = Hashtbl.find cx.ctxt_preconditions s.id in
+ List.iter
+ (fun i ->
+ if not (Bits.get prestate i)
+ then
+ let ckey = Hashtbl.find cx.ctxt_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)
+ (Ast.fmt_to_str Ast.fmt_stmt
+ (Hashtbl.find cx.ctxt_all_stmts s.id)))
+ (Bits.to_list precond);
+ inner.Walk.visit_stmt_pre s
+ in
+ { inner with
+ Walk.visit_stmt_pre = visit_stmt_pre }
+;;
+
+let lifecycle_visitor
+ (cx:ctxt)
+ (inner:Walk.visitor)
+ : Walk.visitor =
+
+ (*
+ * This visitor doesn't *calculate* part of the typestate; it uses
+ * the typestates calculated in earlier passes to extract "summaries"
+ * of slot-lifecycle events into the ctxt tables
+ * ctxt_copy_stmt_is_init and ctxt_post_stmt_slot_drops. These are
+ * used later on in translation.
+ *)
+
+ let (live_block_slots:(node_id Stack.t) Stack.t) = Stack.create () in
+
+ let (implicit_init_block_slots:(node_id,node_id) Hashtbl.t) =
+ Hashtbl.create 0
+ in
+
+ let mark_slot_init sl =
+ Stack.push sl (Stack.top live_block_slots)
+ in
+
+
+ let visit_block_pre b =
+ Stack.push (Stack.create()) live_block_slots;
+ begin
+ match htab_search implicit_init_block_slots b.id with
+ None -> ()
+ | Some slot -> mark_slot_init slot
+ end;
+ inner.Walk.visit_block_pre b
+ in
+
+ let note_drops stmt slots =
+ iflog cx
+ begin
+ fun _ ->
+ log cx "implicit drop of %d slots after stmt %a: "
+ (List.length slots)
+ Ast.sprintf_stmt stmt;
+ List.iter (fun s -> log cx "drop: %a"
+ Ast.sprintf_slot_key
+ (Hashtbl.find cx.ctxt_slot_keys s))
+ slots
+ end;
+ htab_put cx.ctxt_post_stmt_slot_drops stmt.id slots
+ in
+
+ let visit_block_post b =
+ inner.Walk.visit_block_post b;
+ let blk_live = Stack.pop live_block_slots in
+ let stmts = b.node in
+ let len = Array.length stmts in
+ if len > 0
+ then
+ begin
+ let s = stmts.(len-1) in
+ match s.node with
+ Ast.STMT_ret _
+ | Ast.STMT_be _ ->
+ () (* Taken care of in visit_stmt_post below. *)
+ | _ ->
+ let slots = stk_elts_from_top blk_live in
+ note_drops s slots
+ end;
+ in
+
+ let visit_stmt_pre s =
+ begin
+ let init_lval lv_dst =
+ let dst_slots = lval_slots cx lv_dst in
+ Array.iter mark_slot_init dst_slots;
+ in
+ match s.node with
+ Ast.STMT_copy (lv_dst, _)
+ | Ast.STMT_call (lv_dst, _, _)
+ | Ast.STMT_spawn (lv_dst, _, _, _)
+ | Ast.STMT_recv (lv_dst, _)
+ | Ast.STMT_bind (lv_dst, _, _) ->
+ let prestate = Hashtbl.find cx.ctxt_prestates s.id in
+ let poststate = Hashtbl.find cx.ctxt_poststates s.id in
+ let dst_slots = lval_slots cx lv_dst in
+ let is_initializing slot =
+ let cid =
+ Hashtbl.find cx.ctxt_constr_ids (Constr_init slot)
+ in
+ let i = int_of_constr cid in
+ (not (Bits.get prestate i)) && (Bits.get poststate i)
+ in
+ let initializing =
+ List.exists is_initializing (Array.to_list dst_slots)
+ in
+ if initializing
+ then
+ begin
+ Hashtbl.add cx.ctxt_copy_stmt_is_init s.id ();
+ init_lval lv_dst
+ end;
+
+ | Ast.STMT_init_rec (lv_dst, _, _)
+ | Ast.STMT_init_tup (lv_dst, _)
+ | Ast.STMT_init_vec (lv_dst, _, _)
+ | Ast.STMT_init_str (lv_dst, _)
+ | Ast.STMT_init_port lv_dst
+ | Ast.STMT_init_chan (lv_dst, _) ->
+ init_lval lv_dst
+
+ | Ast.STMT_for f ->
+ log cx "noting implicit init for slot %d in for-block %d"
+ (int_of_node (fst f.Ast.for_slot).id)
+ (int_of_node (f.Ast.for_body.id));
+ htab_put implicit_init_block_slots
+ f.Ast.for_body.id
+ (fst f.Ast.for_slot).id
+
+ | Ast.STMT_for_each f ->
+ log cx "noting implicit init for slot %d in for_each-block %d"
+ (int_of_node (fst f.Ast.for_each_slot).id)
+ (int_of_node (f.Ast.for_each_body.id));
+ htab_put implicit_init_block_slots
+ f.Ast.for_each_body.id
+ (fst f.Ast.for_each_slot).id
+
+
+ | _ -> ()
+ end;
+ inner.Walk.visit_stmt_pre s
+ in
+
+ let visit_stmt_post s =
+ inner.Walk.visit_stmt_post s;
+ match s.node with
+ Ast.STMT_ret _
+ | Ast.STMT_be _ ->
+ let stks = stk_elts_from_top live_block_slots in
+ let slots = List.concat (List.map stk_elts_from_top stks) in
+ note_drops s slots
+ | _ -> ()
+ in
+
+ { inner with
+ Walk.visit_block_pre = visit_block_pre;
+ Walk.visit_block_post = visit_block_post;
+ Walk.visit_stmt_pre = visit_stmt_pre;
+ Walk.visit_stmt_post = visit_stmt_post
+ }
+;;
+
+let process_crate
+ (cx:ctxt)
+ (crate:Ast.crate)
+ : unit =
+ let path = Stack.create () in
+ let (scopes:(scope list) ref) = ref [] in
+ let constr_id = ref 0 in
+ let (graph:(node_id, (node_id list)) Hashtbl.t) = Hashtbl.create 0 in
+ let setup_passes =
+ [|
+ (scope_stack_managing_visitor scopes
+ (constr_id_assigning_visitor cx scopes constr_id
+ Walk.empty_visitor));
+ (bitmap_assigning_visitor cx constr_id
+ Walk.empty_visitor);
+ (scope_stack_managing_visitor scopes
+ (condition_assigning_visitor cx scopes
+ Walk.empty_visitor));
+ (graph_sequence_building_visitor cx graph
+ Walk.empty_visitor);
+ (graph_general_block_structure_building_visitor cx graph
+ Walk.empty_visitor);
+ (graph_special_block_structure_building_visitor cx graph
+ Walk.empty_visitor);
+ |]
+ in
+ let verify_passes =
+ [|
+ (scope_stack_managing_visitor scopes
+ (typestate_verify_visitor cx
+ Walk.empty_visitor))
+ |]
+ in
+ let aux_passes =
+ [|
+ (lifecycle_visitor cx
+ Walk.empty_visitor)
+ |]
+ in
+ run_passes cx "typestate setup" path setup_passes (log cx "%s") crate;
+ run_dataflow cx graph;
+ run_passes cx "typestate verify" path verify_passes (log cx "%s") crate;
+ run_passes cx "typestate aux" path aux_passes (log cx "%s") crate
+;;
+
+
+(*
+ * 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/me/walk.ml b/src/boot/me/walk.ml
new file mode 100644
index 00000000..3486bb16
--- /dev/null
+++ b/src/boot/me/walk.ml
@@ -0,0 +1,687 @@
+
+open Common;;
+
+(*
+ * The purpose of this module is just to decouple the AST from the
+ * various passes that are interested in visiting "parts" of it.
+ * If the AST shifts, we have better odds of the shift only affecting
+ * this module rather than all of its clients. Similarly if the
+ * clients only need to visit part, they only have to define the
+ * part of the walk they're interested in, making it cheaper to define
+ * multiple passes.
+ *)
+
+type visitor =
+ {
+ visit_stmt_pre: Ast.stmt -> unit;
+ visit_stmt_post: Ast.stmt -> unit;
+ visit_slot_identified_pre: (Ast.slot identified) -> unit;
+ visit_slot_identified_post: (Ast.slot identified) -> unit;
+ visit_expr_pre: Ast.expr -> unit;
+ visit_expr_post: Ast.expr -> unit;
+ visit_ty_pre: Ast.ty -> unit;
+ visit_ty_post: Ast.ty -> unit;
+ visit_constr_pre: node_id option -> Ast.constr -> unit;
+ visit_constr_post: node_id option -> Ast.constr -> unit;
+ visit_pat_pre: Ast.pat -> unit;
+ visit_pat_post: Ast.pat -> unit;
+ visit_block_pre: Ast.block -> unit;
+ visit_block_post: Ast.block -> unit;
+
+ visit_lit_pre: Ast.lit -> unit;
+ visit_lit_post: Ast.lit -> unit;
+ visit_lval_pre: Ast.lval -> unit;
+ visit_lval_post: Ast.lval -> unit;
+ visit_mod_item_pre:
+ (Ast.ident
+ -> ((Ast.ty_param identified) array)
+ -> Ast.mod_item
+ -> unit);
+ visit_mod_item_post:
+ (Ast.ident
+ -> ((Ast.ty_param identified) array)
+ -> Ast.mod_item
+ -> unit);
+ visit_obj_fn_pre:
+ (Ast.obj identified) -> Ast.ident -> (Ast.fn identified) -> unit;
+ visit_obj_fn_post:
+ (Ast.obj identified) -> Ast.ident -> (Ast.fn identified) -> unit;
+ visit_obj_drop_pre:
+ (Ast.obj identified) -> Ast.block -> unit;
+ visit_obj_drop_post:
+ (Ast.obj identified) -> Ast.block -> unit;
+ visit_crate_pre: Ast.crate -> unit;
+ visit_crate_post: Ast.crate -> unit;
+ }
+;;
+
+
+let empty_visitor =
+ { visit_stmt_pre = (fun _ -> ());
+ visit_stmt_post = (fun _ -> ());
+ visit_slot_identified_pre = (fun _ -> ());
+ visit_slot_identified_post = (fun _ -> ());
+ visit_expr_pre = (fun _ -> ());
+ visit_expr_post = (fun _ -> ());
+ visit_ty_pre = (fun _ -> ());
+ visit_ty_post = (fun _ -> ());
+ visit_constr_pre = (fun _ _ -> ());
+ visit_constr_post = (fun _ _ -> ());
+ visit_pat_pre = (fun _ -> ());
+ visit_pat_post = (fun _ -> ());
+ visit_block_pre = (fun _ -> ());
+ visit_block_post = (fun _ -> ());
+ visit_lit_pre = (fun _ -> ());
+ visit_lit_post = (fun _ -> ());
+ visit_lval_pre = (fun _ -> ());
+ visit_lval_post = (fun _ -> ());
+ visit_mod_item_pre = (fun _ _ _ -> ());
+ visit_mod_item_post = (fun _ _ _ -> ());
+ visit_obj_fn_pre = (fun _ _ _ -> ());
+ visit_obj_fn_post = (fun _ _ _ -> ());
+ visit_obj_drop_pre = (fun _ _ -> ());
+ visit_obj_drop_post = (fun _ _ -> ());
+ visit_crate_pre = (fun _ -> ());
+ visit_crate_post = (fun _ -> ()); }
+;;
+
+let path_managing_visitor
+ (path:Ast.name_component Stack.t)
+ (inner:visitor)
+ : visitor =
+ let visit_mod_item_pre ident params item =
+ Stack.push (Ast.COMP_ident ident) path;
+ inner.visit_mod_item_pre ident params item
+ in
+ let visit_mod_item_post ident params item =
+ inner.visit_mod_item_post ident params item;
+ ignore (Stack.pop path)
+ in
+ let visit_obj_fn_pre obj ident fn =
+ Stack.push (Ast.COMP_ident ident) path;
+ inner.visit_obj_fn_pre obj ident fn
+ in
+ let visit_obj_fn_post obj ident fn =
+ inner.visit_obj_fn_post obj ident fn;
+ ignore (Stack.pop path)
+ in
+ let visit_obj_drop_pre obj b =
+ Stack.push (Ast.COMP_ident "drop") path;
+ inner.visit_obj_drop_pre obj b
+ in
+ let visit_obj_drop_post obj b =
+ inner.visit_obj_drop_post obj b;
+ ignore (Stack.pop path)
+ in
+ { inner with
+ visit_mod_item_pre = visit_mod_item_pre;
+ visit_mod_item_post = visit_mod_item_post;
+ visit_obj_fn_pre = visit_obj_fn_pre;
+ visit_obj_fn_post = visit_obj_fn_post;
+ visit_obj_drop_pre = visit_obj_drop_pre;
+ visit_obj_drop_post = visit_obj_drop_post;
+ }
+;;
+
+let rec name_of ncs =
+ match ncs with
+ [] -> bug () "Walk.name_of_ncs: empty path"
+ | [(Ast.COMP_ident i)] -> Ast.NAME_base (Ast.BASE_ident i)
+ | [(Ast.COMP_app x)] -> Ast.NAME_base (Ast.BASE_app x)
+ | [(Ast.COMP_idx _)] ->
+ bug () "Walk.name_of_ncs: path-name contains COMP_idx"
+ | nc::ncs -> Ast.NAME_ext (name_of ncs, nc)
+;;
+
+let path_to_name
+ (path:Ast.name_component Stack.t)
+ : Ast.name =
+ name_of (stk_elts_from_top path)
+;;
+
+
+let mod_item_logging_visitor
+ (logfn:string->unit)
+ (path:Ast.name_component Stack.t)
+ (inner:visitor)
+ : visitor =
+ let path_name _ = Ast.fmt_to_str Ast.fmt_name (path_to_name path) in
+ let visit_mod_item_pre name params item =
+ logfn (Printf.sprintf "entering %s" (path_name()));
+ inner.visit_mod_item_pre name params item;
+ logfn (Printf.sprintf "entered %s" (path_name()));
+ in
+ let visit_mod_item_post name params item =
+ logfn (Printf.sprintf "leaving %s" (path_name()));
+ inner.visit_mod_item_post name params item;
+ logfn (Printf.sprintf "left %s" (path_name()));
+ in
+ let visit_obj_fn_pre obj ident fn =
+ logfn (Printf.sprintf "entering %s" (path_name()));
+ inner.visit_obj_fn_pre obj ident fn;
+ logfn (Printf.sprintf "entered %s" (path_name()));
+ in
+ let visit_obj_fn_post obj ident fn =
+ logfn (Printf.sprintf "leaving %s" (path_name()));
+ inner.visit_obj_fn_post obj ident fn;
+ logfn (Printf.sprintf "left %s" (path_name()));
+ in
+ let visit_obj_drop_pre obj b =
+ logfn (Printf.sprintf "entering %s" (path_name()));
+ inner.visit_obj_drop_pre obj b;
+ logfn (Printf.sprintf "entered %s" (path_name()));
+ in
+ let visit_obj_drop_post obj fn =
+ logfn (Printf.sprintf "leaving %s" (path_name()));
+ inner.visit_obj_drop_post obj fn;
+ logfn (Printf.sprintf "left %s" (path_name()));
+ in
+ { inner with
+ visit_mod_item_pre = visit_mod_item_pre;
+ visit_mod_item_post = visit_mod_item_post;
+ visit_obj_fn_pre = visit_obj_fn_pre;
+ visit_obj_fn_post = visit_obj_fn_post;
+ visit_obj_drop_pre = visit_obj_drop_pre;
+ visit_obj_drop_post = visit_obj_drop_post;
+ }
+;;
+
+
+let walk_bracketed
+ (pre:'a -> unit)
+ (children:unit -> unit)
+ (post:'a -> unit)
+ (x:'a)
+ : unit =
+ begin
+ pre x;
+ children ();
+ post x
+ end
+;;
+
+
+let walk_option
+ (walker:'a -> unit)
+ (opt:'a option)
+ : unit =
+ match opt with
+ None -> ()
+ | Some v -> walker v
+;;
+
+
+let rec walk_crate
+ (v:visitor)
+ (crate:Ast.crate)
+ : unit =
+ walk_bracketed
+ v.visit_crate_pre
+ (fun _ -> walk_mod_items v (snd crate.node.Ast.crate_items))
+ v.visit_crate_post
+ crate
+
+and walk_mod_items
+ (v:visitor)
+ (items:Ast.mod_items)
+ : unit =
+ Hashtbl.iter (walk_mod_item v) items
+
+
+and walk_mod_item
+ (v:visitor)
+ (name:Ast.ident)
+ (item:Ast.mod_item)
+ : unit =
+ let children _ =
+ match item.node.Ast.decl_item with
+ Ast.MOD_ITEM_type ty -> walk_ty v ty
+ | Ast.MOD_ITEM_fn f -> walk_fn v f item.id
+ | Ast.MOD_ITEM_tag (htup, ttag, _) ->
+ walk_header_tup v htup;
+ walk_ty_tag v ttag
+ | Ast.MOD_ITEM_mod (_, items) ->
+ walk_mod_items v items
+ | Ast.MOD_ITEM_obj ob ->
+ walk_header_slots v ob.Ast.obj_state;
+ walk_constrs v (Some item.id) ob.Ast.obj_constrs;
+ let oid = { node = ob; id = item.id } in
+ Hashtbl.iter (walk_obj_fn v oid) ob.Ast.obj_fns;
+ match ob.Ast.obj_drop with
+ None -> ()
+ | Some d ->
+ v.visit_obj_drop_pre oid d;
+ walk_block v d;
+ v.visit_obj_drop_post oid d
+
+ in
+ walk_bracketed
+ (v.visit_mod_item_pre name item.node.Ast.decl_params)
+ children
+ (v.visit_mod_item_post name item.node.Ast.decl_params)
+ item
+
+
+and walk_ty_tup v ttup = Array.iter (walk_slot v) ttup
+
+and walk_ty_tag v ttag = Hashtbl.iter (fun _ t -> walk_ty_tup v t) ttag
+
+and walk_ty
+ (v:visitor)
+ (ty:Ast.ty)
+ : unit =
+ let children _ =
+ match ty with
+ Ast.TY_tup ttup -> walk_ty_tup v ttup
+ | Ast.TY_vec s -> walk_slot v s
+ | Ast.TY_rec trec -> Array.iter (fun (_, s) -> walk_slot v s) trec
+ | Ast.TY_tag ttag -> walk_ty_tag v ttag
+ | Ast.TY_iso tiso -> Array.iter (walk_ty_tag v) tiso.Ast.iso_group
+ | Ast.TY_fn tfn -> walk_ty_fn v tfn
+ | Ast.TY_obj (_, fns) ->
+ Hashtbl.iter (fun _ tfn -> walk_ty_fn v tfn) fns
+ | Ast.TY_chan t -> walk_ty v t
+ | Ast.TY_port t -> walk_ty v t
+ | Ast.TY_constrained (t,cs) ->
+ begin
+ walk_ty v t;
+ walk_constrs v None cs
+ end
+ | Ast.TY_named _ -> ()
+ | Ast.TY_param _ -> ()
+ | Ast.TY_native _ -> ()
+ | Ast.TY_idx _ -> ()
+ | Ast.TY_mach _ -> ()
+ | Ast.TY_type -> ()
+ | Ast.TY_str -> ()
+ | Ast.TY_char -> ()
+ | Ast.TY_int -> ()
+ | Ast.TY_uint -> ()
+ | Ast.TY_bool -> ()
+ | Ast.TY_nil -> ()
+ | Ast.TY_task -> ()
+ | Ast.TY_any -> ()
+ in
+ walk_bracketed
+ v.visit_ty_pre
+ children
+ v.visit_ty_post
+ ty
+
+
+and walk_ty_sig
+ (v:visitor)
+ (s:Ast.ty_sig)
+ : unit =
+ begin
+ Array.iter (walk_slot v) s.Ast.sig_input_slots;
+ walk_constrs v None s.Ast.sig_input_constrs;
+ walk_slot v s.Ast.sig_output_slot;
+ end
+
+
+and walk_ty_fn
+ (v:visitor)
+ (tfn:Ast.ty_fn)
+ : unit =
+ let (tsig, _) = tfn in
+ walk_ty_sig v tsig
+
+
+and walk_constrs
+ (v:visitor)
+ (formal_base:node_id option)
+ (cs:Ast.constrs)
+ : unit =
+ Array.iter (walk_constr v formal_base) cs
+
+and walk_check_calls
+ (v:visitor)
+ (calls:Ast.check_calls)
+ : unit =
+ Array.iter
+ begin
+ fun (f, args) ->
+ walk_lval v f;
+ Array.iter (walk_atom v) args
+ end
+ calls
+
+
+and walk_constr
+ (v:visitor)
+ (formal_base:node_id option)
+ (c:Ast.constr)
+ : unit =
+ walk_bracketed
+ (v.visit_constr_pre formal_base)
+ (fun _ -> ())
+ (v.visit_constr_post formal_base)
+ c
+
+and walk_header_slots
+ (v:visitor)
+ (hslots:Ast.header_slots)
+ : unit =
+ Array.iter (fun (s,_) -> walk_slot_identified v s) hslots
+
+and walk_header_tup
+ (v:visitor)
+ (htup:Ast.header_tup)
+ : unit =
+ Array.iter (walk_slot_identified v) htup
+
+and walk_obj_fn
+ (v:visitor)
+ (obj:Ast.obj identified)
+ (ident:Ast.ident)
+ (f:Ast.fn identified)
+ : unit =
+ v.visit_obj_fn_pre obj ident f;
+ walk_fn v f.node f.id;
+ v.visit_obj_fn_post obj ident f
+
+and walk_fn
+ (v:visitor)
+ (f:Ast.fn)
+ (id:node_id)
+ : unit =
+ walk_header_slots v f.Ast.fn_input_slots;
+ walk_constrs v (Some id) f.Ast.fn_input_constrs;
+ walk_slot_identified v f.Ast.fn_output_slot;
+ walk_block v f.Ast.fn_body
+
+and walk_slot_identified
+ (v:visitor)
+ (s:Ast.slot identified)
+ : unit =
+ walk_bracketed
+ v.visit_slot_identified_pre
+ (fun _ -> walk_slot v s.node)
+ v.visit_slot_identified_post
+ s
+
+
+and walk_slot
+ (v:visitor)
+ (s:Ast.slot)
+ : unit =
+ walk_option (walk_ty v) s.Ast.slot_ty
+
+
+and walk_stmt
+ (v:visitor)
+ (s:Ast.stmt)
+ : unit =
+ let walk_stmt_for
+ (s:Ast.stmt_for)
+ : unit =
+ let (si,_) = s.Ast.for_slot in
+ let (ss,lv) = s.Ast.for_seq in
+ walk_slot_identified v si;
+ Array.iter (walk_stmt v) ss;
+ walk_lval v lv;
+ walk_block v s.Ast.for_body
+ in
+ let walk_stmt_for_each
+ (s:Ast.stmt_for_each)
+ : unit =
+ let (si,_) = s.Ast.for_each_slot in
+ let (f,az) = s.Ast.for_each_call in
+ walk_slot_identified v si;
+ walk_lval v f;
+ Array.iter (walk_atom v) az;
+ walk_block v s.Ast.for_each_head
+ in
+ let walk_stmt_while
+ (s:Ast.stmt_while)
+ : unit =
+ let (ss,e) = s.Ast.while_lval in
+ Array.iter (walk_stmt v) ss;
+ walk_expr v e;
+ walk_block v s.Ast.while_body
+ in
+ let children _ =
+ match s.node with
+ Ast.STMT_log a ->
+ walk_atom v a
+
+ | Ast.STMT_init_rec (lv, atab, base) ->
+ walk_lval v lv;
+ Array.iter (fun (_, _, _, a) -> walk_atom v a) atab;
+ walk_option (walk_lval v) base;
+
+ | Ast.STMT_init_vec (lv, _, atoms) ->
+ walk_lval v lv;
+ Array.iter (walk_atom v) atoms
+
+ | Ast.STMT_init_tup (lv, mut_atoms) ->
+ walk_lval v lv;
+ Array.iter (fun (_, _, a) -> walk_atom v a) mut_atoms
+
+ | Ast.STMT_init_str (lv, _) ->
+ walk_lval v lv
+
+ | Ast.STMT_init_port lv ->
+ walk_lval v lv
+
+ | Ast.STMT_init_chan (chan,port) ->
+ walk_option (walk_lval v) port;
+ walk_lval v chan;
+
+ | Ast.STMT_for f ->
+ walk_stmt_for f
+
+ | Ast.STMT_for_each f ->
+ walk_stmt_for_each f
+
+ | Ast.STMT_while w ->
+ walk_stmt_while w
+
+ | Ast.STMT_do_while w ->
+ walk_stmt_while w
+
+ | Ast.STMT_if i ->
+ begin
+ walk_expr v i.Ast.if_test;
+ walk_block v i.Ast.if_then;
+ walk_option (walk_block v) i.Ast.if_else
+ end
+
+ | Ast.STMT_block b ->
+ walk_block v b
+
+ | Ast.STMT_copy (lv,e) ->
+ walk_lval v lv;
+ walk_expr v e
+
+ | Ast.STMT_copy_binop (lv,_,a) ->
+ walk_lval v lv;
+ walk_atom v a
+
+ | Ast.STMT_call (dst,f,az) ->
+ walk_lval v dst;
+ walk_lval v f;
+ Array.iter (walk_atom v) az
+
+ | Ast.STMT_bind (dst, f, az) ->
+ walk_lval v dst;
+ walk_lval v f;
+ Array.iter (walk_opt_atom v) az
+
+ | Ast.STMT_spawn (dst,_,p,az) ->
+ walk_lval v dst;
+ walk_lval v p;
+ Array.iter (walk_atom v) az
+
+ | Ast.STMT_ret ao ->
+ walk_option (walk_atom v) ao
+
+ | Ast.STMT_put at ->
+ walk_option (walk_atom v) at
+
+ | Ast.STMT_put_each (lv, ats) ->
+ walk_lval v lv;
+ Array.iter (walk_atom v) ats
+
+ (* FIXME: this should have a param array, and invoke the visitors. *)
+ | Ast.STMT_decl (Ast.DECL_mod_item (id, mi)) ->
+ walk_mod_item v id mi
+
+ | Ast.STMT_decl (Ast.DECL_slot (_, slot)) ->
+ walk_slot_identified v slot
+
+ | Ast.STMT_yield
+ | Ast.STMT_fail ->
+ ()
+
+ | Ast.STMT_join task ->
+ walk_lval v task
+
+ | Ast.STMT_send (dst,src) ->
+ walk_lval v dst;
+ walk_lval v src
+
+ | Ast.STMT_recv (dst,src) ->
+ walk_lval v dst;
+ walk_lval v src
+
+ | Ast.STMT_be (lv, ats) ->
+ walk_lval v lv;
+ Array.iter (walk_atom v) ats
+
+ | Ast.STMT_check_expr e ->
+ walk_expr v e
+
+ | Ast.STMT_check (cs, calls) ->
+ walk_constrs v None cs;
+ walk_check_calls v calls
+
+ | Ast.STMT_check_if (cs,calls,b) ->
+ walk_constrs v None cs;
+ walk_check_calls v calls;
+ walk_block v b
+
+ | Ast.STMT_prove cs ->
+ walk_constrs v None cs
+
+ | Ast.STMT_alt_tag
+ { Ast.alt_tag_lval = lval; Ast.alt_tag_arms = arms } ->
+ walk_lval v lval;
+ let walk_arm { node = (pat, block) } =
+ walk_pat v pat;
+ walk_block v block
+ in
+ Array.iter walk_arm arms
+
+ (* FIXME (issue #20): finish this as needed. *)
+ | Ast.STMT_slice _
+ | Ast.STMT_note _
+ | Ast.STMT_alt_type _
+ | Ast.STMT_alt_port _ ->
+ bug () "unimplemented statement type in Walk.walk_stmt"
+ in
+ walk_bracketed
+ v.visit_stmt_pre
+ children
+ v.visit_stmt_post
+ s
+
+
+and walk_expr
+ (v:visitor)
+ (e:Ast.expr)
+ : unit =
+ let children _ =
+ match e with
+ Ast.EXPR_binary (_,aa,ab) ->
+ walk_atom v aa;
+ walk_atom v ab
+ | Ast.EXPR_unary (_,a) ->
+ walk_atom v a
+ | Ast.EXPR_atom a ->
+ walk_atom v a
+ in
+ walk_bracketed
+ v.visit_expr_pre
+ children
+ v.visit_expr_post
+ e
+
+and walk_atom
+ (v:visitor)
+ (a:Ast.atom)
+ : unit =
+ match a with
+ Ast.ATOM_literal ls -> walk_lit v ls.node
+ | Ast.ATOM_lval lv -> walk_lval v lv
+
+
+and walk_opt_atom
+ (v:visitor)
+ (ao:Ast.atom option)
+ : unit =
+ match ao with
+ None -> ()
+ | Some a -> walk_atom v a
+
+
+and walk_lit
+ (v:visitor)
+ (li:Ast.lit)
+ : unit =
+ walk_bracketed
+ v.visit_lit_pre
+ (fun _ -> ())
+ v.visit_lit_post
+ li
+
+
+and walk_lval
+ (v:visitor)
+ (lv:Ast.lval)
+ : unit =
+ walk_bracketed
+ v.visit_lval_pre
+ (fun _ -> ())
+ v.visit_lval_post
+ lv
+
+
+and walk_pat
+ (v:visitor)
+ (p:Ast.pat)
+ : unit =
+ let rec walk p =
+ match p with
+ Ast.PAT_lit lit -> walk_lit v lit
+ | Ast.PAT_tag (_, pats) -> Array.iter walk pats
+ | Ast.PAT_slot (si, _) -> walk_slot_identified v si
+ | Ast.PAT_wild -> ()
+ in
+ walk_bracketed
+ v.visit_pat_pre
+ (fun _ -> walk p)
+ v.visit_pat_post
+ p
+
+
+and walk_block
+ (v:visitor)
+ (b:Ast.block)
+ : unit =
+ walk_bracketed
+ v.visit_block_pre
+ (fun _ -> (Array.iter (walk_stmt v) b.node))
+ v.visit_block_post
+ b
+;;
+
+(*
+ * 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/util/bits.ml b/src/boot/util/bits.ml
new file mode 100644
index 00000000..3114bd66
--- /dev/null
+++ b/src/boot/util/bits.ml
@@ -0,0 +1,107 @@
+type t = {
+ storage: int array;
+ nbits: int;
+}
+;;
+
+let int_bits =
+ if max_int = (1 lsl 30) - 1
+ then 31
+ else 63
+;;
+
+let create nbits flag =
+ { storage = Array.make (nbits / int_bits + 1) (if flag then lnot 0 else 0);
+ nbits = nbits }
+;;
+
+(*
+ * mutate v0 in place: v0.(i) <- v0.(i) op v1.(i), returning bool indicating
+ * whether any bits in v0 changed in the process.
+ *)
+let process (op:int -> int -> int) (v0:t) (v1:t) : bool =
+ let changed = ref false in
+ assert (v0.nbits = v1.nbits);
+ assert ((Array.length v0.storage) = (Array.length v1.storage));
+ Array.iteri
+ begin
+ fun i w1 ->
+ let w0 = v0.storage.(i) in
+ let w0' = op w0 w1 in
+ if not (w0' = w0)
+ then changed := true;
+ v0.storage.(i) <- w0';
+ end
+ v1.storage;
+ !changed
+;;
+
+let union = process (lor) ;;
+let intersect = process (land) ;;
+let copy = process (fun _ w1 -> w1) ;;
+
+let get (v:t) (i:int) : bool =
+ assert (i >= 0);
+ assert (i < v.nbits);
+ let w = i / int_bits in
+ let b = i mod int_bits in
+ let x = 1 land (v.storage.(w) lsr b) in
+ x = 1
+;;
+
+let equal (v1:t) (v0:t) : bool =
+ v0 = v1
+;;
+
+let clear (v:t) : unit =
+ for i = 0 to (Array.length v.storage) - 1
+ do
+ v.storage.(i) <- 0
+ done
+;;
+
+let invert (v:t) : unit =
+ for i = 0 to (Array.length v.storage) - 1
+ do
+ v.storage.(i) <- lnot v.storage.(i)
+ done
+;;
+
+let set (v:t) (i:int) (x:bool) : unit =
+ assert (i >= 0);
+ assert (i < v.nbits);
+ let w = i / int_bits in
+ let b = i mod int_bits in
+ let w0 = v.storage.(w) in
+ let flag = 1 lsl b in
+ v.storage.(w) <-
+ if x
+ then w0 lor flag
+ else w0 land (lnot flag)
+;;
+
+let to_list (v:t) : int list =
+ if v.nbits = 0
+ then []
+ else
+ let accum = ref [] in
+ let word = ref v.storage.(0) in
+ for i = 0 to (v.nbits-1) do
+ if i mod int_bits = 0
+ then word := v.storage.(i / int_bits);
+ if (1 land (!word)) = 1
+ then accum := i :: (!accum);
+ word := (!word) lsr 1;
+ done;
+ !accum
+;;
+
+
+(*
+ * 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/util/common.ml b/src/boot/util/common.ml
new file mode 100644
index 00000000..f33a6ea1
--- /dev/null
+++ b/src/boot/util/common.ml
@@ -0,0 +1,709 @@
+(*
+ * This module goes near the *bottom* of the dependency DAG, and holds basic
+ * types shared across all phases of the compiler.
+ *)
+
+type filename = string
+type pos = (filename * int * int)
+type span = {lo: pos; hi: pos}
+
+type node_id = Node of int
+type temp_id = Temp of int
+type opaque_id = Opaque of int
+type constr_id = Constr of int
+
+let int_of_node (Node i) = i
+let int_of_temp (Temp i) = i
+let int_of_opaque (Opaque i) = i
+let int_of_constr (Constr i) = i
+
+type 'a identified = { node: 'a; id: node_id }
+;;
+
+let bug _ =
+ let k s = failwith s
+ in Printf.ksprintf k
+;;
+
+exception Semant_err of ((node_id option) * string)
+;;
+
+let err (idopt:node_id option) =
+ let k s =
+ raise (Semant_err (idopt, s))
+ in
+ Printf.ksprintf k
+;;
+
+(* Some ubiquitous low-level types. *)
+
+type target =
+ Linux_x86_elf
+ | Win32_x86_pe
+ | MacOS_x86_macho
+;;
+
+type ty_mach =
+ TY_u8
+ | TY_u16
+ | TY_u32
+ | TY_u64
+ | TY_i8
+ | TY_i16
+ | TY_i32
+ | TY_i64
+ | TY_f32
+ | TY_f64
+;;
+
+let mach_is_integral (mach:ty_mach) : bool =
+ match mach with
+ TY_i8 | TY_i16 | TY_i32 | TY_i64
+ | TY_u8 | TY_u16 | TY_u32 | TY_u64 -> true
+ | TY_f32 | TY_f64 -> false
+;;
+
+
+let mach_is_signed (mach:ty_mach) : bool =
+ match mach with
+ TY_i8 | TY_i16 | TY_i32 | TY_i64 -> true
+ | TY_u8 | TY_u16 | TY_u32 | TY_u64
+ | TY_f32 | TY_f64 -> false
+;;
+
+let string_of_ty_mach (mach:ty_mach) : string =
+ match mach with
+ TY_u8 -> "u8"
+ | TY_u16 -> "u16"
+ | TY_u32 -> "u32"
+ | TY_u64 -> "u64"
+ | TY_i8 -> "i8"
+ | TY_i16 -> "i16"
+ | TY_i32 -> "i32"
+ | TY_i64 -> "i64"
+ | TY_f32 -> "f32"
+ | TY_f64 -> "f64"
+;;
+
+let bytes_of_ty_mach (mach:ty_mach) : int =
+ match mach with
+ TY_u8 -> 1
+ | TY_u16 -> 2
+ | TY_u32 -> 4
+ | TY_u64 -> 8
+ | TY_i8 -> 1
+ | TY_i16 -> 2
+ | TY_i32 -> 4
+ | TY_i64 -> 8
+ | TY_f32 -> 4
+ | TY_f64 -> 8
+;;
+
+type ty_param_idx = int
+;;
+
+type nabi_conv =
+ CONV_rust
+ | CONV_cdecl
+;;
+
+type nabi = { nabi_indirect: bool;
+ nabi_convention: nabi_conv }
+;;
+
+let string_to_conv (a:string) : nabi_conv option =
+ match a with
+ "cdecl" -> Some CONV_cdecl
+ | "rust" -> Some CONV_rust
+ | _ -> None
+
+(* FIXME: remove this when native items go away. *)
+let string_to_nabi (s:string) (indirect:bool) : nabi option =
+ match string_to_conv s with
+ None -> None
+ | Some c ->
+ Some { nabi_indirect = indirect;
+ nabi_convention = c }
+;;
+
+type required_lib_spec =
+ {
+ required_libname: string;
+ required_prefix: int;
+ }
+;;
+
+type required_lib =
+ REQUIRED_LIB_rustrt
+ | REQUIRED_LIB_crt
+ | REQUIRED_LIB_rust of required_lib_spec
+ | REQUIRED_LIB_c of required_lib_spec
+;;
+
+type segment =
+ SEG_text
+ | SEG_data
+;;
+
+type fixup =
+ { fixup_name: string;
+ mutable fixup_file_pos: int option;
+ mutable fixup_file_sz: int option;
+ mutable fixup_mem_pos: int64 option;
+ mutable fixup_mem_sz: int64 option }
+;;
+
+
+let new_fixup (s:string)
+ : fixup =
+ { fixup_name = s;
+ fixup_file_pos = None;
+ fixup_file_sz = None;
+ fixup_mem_pos = None;
+ fixup_mem_sz = None }
+;;
+
+
+(*
+ * Auxiliary hashtable functions.
+ *)
+
+let htab_keys (htab:('a,'b) Hashtbl.t) : ('a list) =
+ Hashtbl.fold (fun k _ accum -> k :: accum) htab []
+;;
+
+let sorted_htab_keys (tab:('a, 'b) Hashtbl.t) : 'a array =
+ let keys = Array.of_list (htab_keys tab) in
+ Array.sort compare keys;
+ keys
+;;
+
+let htab_vals (htab:('a,'b) Hashtbl.t) : ('b list) =
+ Hashtbl.fold (fun _ v accum -> v :: accum) htab []
+;;
+
+let htab_pairs (htab:('a,'b) Hashtbl.t) : (('a * 'b) list) =
+ Hashtbl.fold (fun k v accum -> (k,v) :: accum) htab []
+;;
+
+let htab_search (htab:('a,'b) Hashtbl.t) (k:'a) : ('b option) =
+ if Hashtbl.mem htab k
+ then Some (Hashtbl.find htab k)
+ else None
+;;
+
+let htab_search_or_default
+ (htab:('a,'b) Hashtbl.t)
+ (k:'a)
+ (def:unit -> 'b)
+ : 'b =
+ match htab_search htab k with
+ Some v -> v
+ | None -> def()
+;;
+
+let htab_search_or_add
+ (htab:('a,'b) Hashtbl.t)
+ (k:'a)
+ (mk:unit -> 'b)
+ : 'b =
+ let def () =
+ let v = mk() in
+ Hashtbl.add htab k v;
+ v
+ in
+ htab_search_or_default htab k def
+;;
+
+let htab_put (htab:('a,'b) Hashtbl.t) (a:'a) (b:'b) : unit =
+ assert (not (Hashtbl.mem htab a));
+ Hashtbl.add htab a b
+;;
+
+let htab_map
+ (htab:('a,'b) Hashtbl.t)
+ (f:'a -> 'b -> ('c * 'd))
+ : (('c,'d) Hashtbl.t) =
+ let ntab = Hashtbl.create (Hashtbl.length htab) in
+ let g a b =
+ let (c,d) = f a b in
+ htab_put ntab c d
+ in
+ Hashtbl.iter g htab;
+ ntab
+;;
+
+
+let htab_fold
+ (fn:'a -> 'b -> 'c -> 'c)
+ (init:'c)
+ (h:('a, 'b) Hashtbl.t) : 'c =
+ let accum = ref init in
+ let f a b = accum := (fn a b (!accum)) in
+ Hashtbl.iter f h;
+ !accum
+;;
+
+
+let reduce_hash_to_list
+ (fn:'a -> 'b -> 'c)
+ (h:('a, 'b) Hashtbl.t)
+ : ('c list) =
+ htab_fold (fun a b ls -> (fn a b) :: ls) [] h
+;;
+
+(*
+ * Auxiliary association-array and association-list operations.
+ *)
+let atab_search (atab:('a * 'b) array) (a:'a) : ('b option) =
+ let lim = Array.length atab in
+ let rec step i =
+ if i = lim
+ then None
+ else
+ let (k,v) = atab.(i) in
+ if k = a
+ then Some v
+ else step (i+1)
+ in
+ step 0
+
+let atab_find (atab:('a * 'b) array) (a:'a) : 'b =
+ match atab_search atab a with
+ None -> bug () "atab_find: element not found"
+ | Some b -> b
+
+let atab_mem (atab:('a * 'b) array) (a:'a) : bool =
+ match atab_search atab a with
+ None -> false
+ | Some _ -> true
+
+let rec ltab_search (ltab:('a * 'b) list) (a:'a) : ('b option) =
+ match ltab with
+ [] -> None
+ | (k,v)::_ when k = a -> Some v
+ | _::lz -> ltab_search lz a
+
+let ltab_put (ltab:('a * 'b) list) (a:'a) (b:'b) : (('a * 'b) list) =
+ assert ((ltab_search ltab a) = None);
+ (a,b)::ltab
+
+(*
+ * Auxiliary list functions.
+ *)
+
+let rec list_search (list:'a list) (f:'a -> 'b option) : ('b option) =
+ match list with
+ [] -> None
+ | a::az ->
+ match f a with
+ Some b -> Some b
+ | None -> list_search az f
+
+let rec list_search_ctxt
+ (list:'a list)
+ (f:'a -> 'b option)
+ : ((('a list) * 'b) option) =
+ match list with
+ [] -> None
+ | a::az ->
+ match f a with
+ Some b -> Some (list, b)
+ | None -> list_search_ctxt az f
+
+let rec list_drop n ls =
+ if n = 0
+ then ls
+ else list_drop (n-1) (List.tl ls)
+;;
+
+
+(*
+ * Auxiliary option functions.
+ *)
+
+let bool_of_option x =
+ match x with
+ Some _ -> true
+ | None -> false
+
+
+(*
+ * Auxiliary stack functions.
+ *)
+
+let stk_fold (s:'a Stack.t) (f:'a -> 'b -> 'b) (x:'b) : 'b =
+ let r = ref x in
+ Stack.iter (fun e -> r := f e (!r)) s;
+ !r
+
+let stk_elts_from_bot (s:'a Stack.t) : ('a list) =
+ stk_fold s (fun x y -> x::y) []
+
+let stk_elts_from_top (s:'a Stack.t) : ('a list) =
+ List.rev (stk_elts_from_bot s)
+
+let stk_search (s:'a Stack.t) (f:'a -> 'b option) : 'b option =
+ stk_fold s (fun e accum -> match accum with None -> (f e) | x -> x) None
+
+
+(*
+ * Auxiliary array functions.
+ *)
+
+let arr_search (a:'a array) (f:int -> 'a -> 'b option) : 'b option =
+ let max = Array.length a in
+ let rec iter i =
+ if i < max
+ then
+ let v = a.(i) in
+ let r = f i v in
+ match r with
+ Some _ -> r
+ | None -> iter (i+1)
+ else
+ None
+ in
+ iter 0
+;;
+
+let arr_idx (arr:'a array) (a:'a) : int =
+ let find i v = if v = a then Some i else None in
+ match arr_search arr find with
+ None -> bug () "arr_idx: element not found"
+ | Some i -> i
+;;
+
+let arr_map_partial (a:'a array) (f:'a -> 'b option) : 'b array =
+ let accum a ls =
+ match f a with
+ None -> ls
+ | Some b -> b :: ls
+ in
+ Array.of_list (Array.fold_right accum a [])
+;;
+
+let arr_filter_some (a:'a option array) : 'a array =
+ arr_map_partial a (fun x -> x)
+;;
+
+let arr_find_dups (a:'a array) : ('a * 'a) option =
+ let copy = Array.copy a in
+ Array.sort compare copy;
+ let lasti = (Array.length copy) - 1 in
+ let rec find_dups i =
+ if i < lasti then
+ let this = copy.(i) in
+ let next = copy.(i+1) in
+ (if (this = next) then
+ Some (this, next)
+ else
+ find_dups (i+1))
+ else
+ None
+ in
+ find_dups 0
+;;
+
+let arr_check_dups (a:'a array) (f:'a -> 'a -> unit) : unit =
+ match arr_find_dups a with
+ Some (x, y) -> f x y
+ | None -> ()
+;;
+
+let arr_map2 (f:'a -> 'b -> 'c) (a:'a array) (b:'b array) : 'c array =
+ assert ((Array.length a) = (Array.length b));
+ Array.init (Array.length a) (fun i -> f a.(i) b.(i))
+;;
+
+let arr_for_all (f:int -> 'a -> bool) (a:'a array) : bool =
+ let len = Array.length a in
+ let rec loop i =
+ (i >= len) || ((f i a.(i)) && (loop (i+1)))
+ in
+ loop 0
+;;
+
+let arr_exists (f:int -> 'a -> bool) (a:'a array) : bool =
+ let len = Array.length a in
+ let rec loop i =
+ (i < len) && ((f i a.(i)) || (loop (i+1)))
+ in
+ loop 0
+;;
+
+(*
+ * Auxiliary queue functions.
+ *)
+
+let queue_to_list (q:'a Queue.t) : 'a list =
+ List.rev (Queue.fold (fun ls elt -> elt :: ls) [] q)
+;;
+
+let queue_to_arr (q:'a Queue.t) : 'a array =
+ Array.init (Queue.length q) (fun _ -> Queue.take q)
+;;
+
+(*
+ * Auxiliary int64 functions
+ *)
+
+let i64_lt (a:int64) (b:int64) : bool = (Int64.compare a b) < 0
+let i64_le (a:int64) (b:int64) : bool = (Int64.compare a b) <= 0
+let i64_ge (a:int64) (b:int64) : bool = (Int64.compare a b) >= 0
+let i64_gt (a:int64) (b:int64) : bool = (Int64.compare a b) > 0
+let i64_max (a:int64) (b:int64) : int64 =
+ (if (Int64.compare a b) > 0 then a else b)
+let i64_min (a:int64) (b:int64) : int64 =
+ (if (Int64.compare a b) < 0 then a else b)
+let i64_align (align:int64) (v:int64) : int64 =
+ (assert (align <> 0L));
+ let mask = Int64.sub align 1L in
+ Int64.logand (Int64.lognot mask) (Int64.add v mask)
+;;
+
+let rec i64_for (lo:int64) (hi:int64) (thunk:int64 -> unit) : unit =
+ if i64_lt lo hi then
+ begin
+ thunk lo;
+ i64_for (Int64.add lo 1L) hi thunk;
+ end
+;;
+
+let rec i64_for_rev (hi:int64) (lo:int64) (thunk:int64 -> unit) : unit =
+ if i64_ge hi lo then
+ begin
+ thunk hi;
+ i64_for_rev (Int64.sub hi 1L) lo thunk;
+ end
+;;
+
+
+(*
+ * Auxiliary int32 functions
+ *)
+
+let i32_lt (a:int32) (b:int32) : bool = (Int32.compare a b) < 0
+let i32_le (a:int32) (b:int32) : bool = (Int32.compare a b) <= 0
+let i32_ge (a:int32) (b:int32) : bool = (Int32.compare a b) >= 0
+let i32_gt (a:int32) (b:int32) : bool = (Int32.compare a b) > 0
+let i32_max (a:int32) (b:int32) : int32 =
+ (if (Int32.compare a b) > 0 then a else b)
+let i32_min (a:int32) (b:int32) : int32 =
+ (if (Int32.compare a b) < 0 then a else b)
+let i32_align (align:int32) (v:int32) : int32 =
+ (assert (align <> 0l));
+ let mask = Int32.sub align 1l in
+ Int32.logand (Int32.lognot mask) (Int32.add v mask)
+;;
+
+(*
+ * Int-as-unichar functions.
+ *)
+
+let bounds lo c hi = (lo <= c) && (c <= hi)
+;;
+
+let escaped_char i =
+ if bounds 0 i 0x7f
+ then Char.escaped (Char.chr i)
+ else
+ if bounds 0 i 0xffff
+ then Printf.sprintf "\\u%4.4X" i
+ else Printf.sprintf "\\U%8.8X" i
+;;
+
+let char_as_utf8 i =
+ let buf = Buffer.create 8 in
+ let addb i =
+ Buffer.add_char buf (Char.chr (i land 0xff))
+ in
+ let fini _ =
+ Buffer.contents buf
+ in
+ let rec add_trailing_bytes n i =
+ if n = 0
+ then fini()
+ else
+ begin
+ addb (0b1000_0000 lor ((i lsr ((n-1) * 6)) land 0b11_1111));
+ add_trailing_bytes (n-1) i
+ end
+ in
+ if bounds 0 i 0x7f
+ then (addb i; fini())
+ else
+ if bounds 0x80 i 0x7ff
+ then (addb ((0b1100_0000) lor (i lsr 6));
+ add_trailing_bytes 1 i)
+ else
+ if bounds 0x800 i 0xffff
+ then (addb ((0b1110_0000) lor (i lsr 12));
+ add_trailing_bytes 2 i)
+ else
+ if bounds 0x1000 i 0x1f_ffff
+ then (addb ((0b1111_0000) lor (i lsr 18));
+ add_trailing_bytes 3 i)
+ else
+ if bounds 0x20_0000 i 0x3ff_ffff
+ then (addb ((0b1111_1000) lor (i lsr 24));
+ add_trailing_bytes 4 i)
+ else
+ if bounds 0x400_0000 i 0x7fff_ffff
+ then (addb ((0b1111_1100) lor (i lsr 30));
+ add_trailing_bytes 5 i)
+ else bug () "bad unicode character 0x%X" i
+;;
+
+(*
+ * Size-expressions.
+ *)
+
+
+type size =
+ SIZE_fixed of int64
+ | SIZE_fixup_mem_sz of fixup
+ | SIZE_fixup_mem_pos of fixup
+ | SIZE_param_size of ty_param_idx
+ | SIZE_param_align of ty_param_idx
+ | SIZE_rt_neg of size
+ | SIZE_rt_add of size * size
+ | SIZE_rt_mul of size * size
+ | SIZE_rt_max of size * size
+ | SIZE_rt_align of size * size
+;;
+
+let rec string_of_size (s:size) : string =
+ match s with
+ SIZE_fixed i -> Printf.sprintf "%Ld" i
+ | SIZE_fixup_mem_sz f -> Printf.sprintf "%s.mem_sz" f.fixup_name
+ | SIZE_fixup_mem_pos f -> Printf.sprintf "%s.mem_pos" f.fixup_name
+ | SIZE_param_size i -> Printf.sprintf "ty[%d].size" i
+ | SIZE_param_align i -> Printf.sprintf "ty[%d].align" i
+ | SIZE_rt_neg a ->
+ Printf.sprintf "-(%s)" (string_of_size a)
+ | SIZE_rt_add (a, b) ->
+ Printf.sprintf "(%s + %s)" (string_of_size a) (string_of_size b)
+ | SIZE_rt_mul (a, b) ->
+ Printf.sprintf "(%s * %s)" (string_of_size a) (string_of_size b)
+ | SIZE_rt_max (a, b) ->
+ Printf.sprintf "max(%s,%s)" (string_of_size a) (string_of_size b)
+ | SIZE_rt_align (align, off) ->
+ Printf.sprintf "align(%s,%s)"
+ (string_of_size align) (string_of_size off)
+;;
+
+let neg_sz (a:size) : size =
+ match a with
+ SIZE_fixed a -> SIZE_fixed (Int64.neg a)
+ | _ -> SIZE_rt_neg a
+;;
+
+let add_sz (a:size) (b:size) : size =
+ match (a, b) with
+ (SIZE_fixed a, SIZE_fixed b) -> SIZE_fixed (Int64.add a b)
+
+ | ((SIZE_rt_add ((SIZE_fixed a), c)), SIZE_fixed b)
+ | ((SIZE_rt_add (c, (SIZE_fixed a))), SIZE_fixed b)
+ | (SIZE_fixed a, (SIZE_rt_add ((SIZE_fixed b), c)))
+ | (SIZE_fixed a, (SIZE_rt_add (c, (SIZE_fixed b)))) ->
+ SIZE_rt_add (SIZE_fixed (Int64.add a b), c)
+
+ | (SIZE_fixed 0L, b) -> b
+ | (a, SIZE_fixed 0L) -> a
+ | (a, SIZE_fixed b) -> SIZE_rt_add (SIZE_fixed b, a)
+ | (a, b) -> SIZE_rt_add (a, b)
+;;
+
+let mul_sz (a:size) (b:size) : size =
+ match (a, b) with
+ (SIZE_fixed a, SIZE_fixed b) -> SIZE_fixed (Int64.mul a b)
+ | (a, SIZE_fixed b) -> SIZE_rt_mul (SIZE_fixed b, a)
+ | (a, b) -> SIZE_rt_mul (a, b)
+;;
+
+let rec max_sz (a:size) (b:size) : size =
+ let rec no_negs x =
+ match x with
+ SIZE_fixed _
+ | SIZE_fixup_mem_sz _
+ | SIZE_fixup_mem_pos _
+ | SIZE_param_size _
+ | SIZE_param_align _ -> true
+ | SIZE_rt_neg _ -> false
+ | SIZE_rt_add (a,b) -> (no_negs a) && (no_negs b)
+ | SIZE_rt_mul (a,b) -> (no_negs a) && (no_negs b)
+ | SIZE_rt_max (a,b) -> (no_negs a) && (no_negs b)
+ | SIZE_rt_align (a,b) -> (no_negs a) && (no_negs b)
+ in
+ match (a, b) with
+ (SIZE_rt_align _, SIZE_fixed 1L) -> a
+ | (SIZE_fixed 1L, SIZE_rt_align _) -> b
+ | (SIZE_param_align _, SIZE_fixed 1L) -> a
+ | (SIZE_fixed 1L, SIZE_param_align _) -> b
+ | (a, SIZE_rt_max (b, c)) when a = b -> max_sz a c
+ | (a, SIZE_rt_max (b, c)) when a = c -> max_sz a b
+ | (SIZE_rt_max (b, c), a) when a = b -> max_sz a c
+ | (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 b) -> max_sz (SIZE_fixed b) a
+ | (a, b) when a = b -> a
+ | (a, b) -> SIZE_rt_max (a, b)
+;;
+
+(* FIXME: audit this carefuly; I am not terribly certain of the
+ * algebraic simplification going on here. Sadly, without it
+ * the diagnostic output from translation becomes completely
+ * illegible.
+ *)
+
+let align_sz (a:size) (b:size) : size =
+ let rec alignment_of s =
+ match s with
+ SIZE_rt_align (SIZE_fixed n, s) ->
+ let inner_alignment = alignment_of s in
+ if (Int64.rem n inner_alignment) = 0L
+ then inner_alignment
+ else n
+ | SIZE_rt_add (SIZE_fixed n, s)
+ | SIZE_rt_add (s, SIZE_fixed n) ->
+ let inner_alignment = alignment_of s in
+ if (Int64.rem n inner_alignment) = 0L
+ then inner_alignment
+ else 1L (* This could be lcd(...) or such. *)
+ | SIZE_rt_max (a, SIZE_fixed 1L) -> alignment_of a
+ | SIZE_rt_max (SIZE_fixed 1L, b) -> alignment_of b
+ | _ -> 1L
+ in
+ match (a, b) with
+ (SIZE_fixed a, SIZE_fixed b) -> SIZE_fixed (i64_align a b)
+ | (SIZE_fixed x, _) when i64_lt x 1L -> bug () "alignment less than 1"
+ | (SIZE_fixed 1L, b) -> b (* everything is 1-aligned. *)
+ | (_, SIZE_fixed 0L) -> b (* 0 is everything-aligned. *)
+ | (SIZE_fixed a, b) ->
+ let inner_alignment = alignment_of b in
+ if (Int64.rem a inner_alignment) = 0L
+ then b
+ else SIZE_rt_align (SIZE_fixed a, b)
+ | (SIZE_rt_max (a, SIZE_fixed 1L), b) -> SIZE_rt_align (a, b)
+ | (SIZE_rt_max (SIZE_fixed 1L, a), b) -> SIZE_rt_align (a, b)
+ | (a, b) -> SIZE_rt_align (a, b)
+;;
+
+let force_sz (a:size) : int64 =
+ match a with
+ SIZE_fixed i -> i
+ | _ -> bug () "force_sz: forced non-fixed size expression %s"
+ (string_of_size a)
+;;
+
+(*
+ * 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/comp/driver/rustc.rs b/src/comp/driver/rustc.rs
new file mode 100644
index 00000000..35ebba1f
--- /dev/null
+++ b/src/comp/driver/rustc.rs
@@ -0,0 +1,12 @@
+// -*- rust -*-
+
+fn main(vec[str] args) -> () {
+ let int i = 0;
+ for (str filename in args) {
+ if (i > 0) {
+ auto br = std._io.mk_buf_reader(filename);
+ log "opened file: " + filename;
+ }
+ i += 1;
+ }
+}
diff --git a/src/comp/fe/lexer.rs b/src/comp/fe/lexer.rs
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/comp/fe/lexer.rs
diff --git a/src/comp/fe/parser.rs b/src/comp/fe/parser.rs
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/comp/fe/parser.rs
diff --git a/src/comp/rustc.rc b/src/comp/rustc.rc
new file mode 100644
index 00000000..3bf3bbcc
--- /dev/null
+++ b/src/comp/rustc.rc
@@ -0,0 +1,20 @@
+
+// -*- rust -*-
+
+use std;
+
+mod fe {
+ mod lexer;
+ mod parser;
+}
+
+mod driver {
+ mod rustc;
+}
+
+// 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/etc/tidy.py b/src/etc/tidy.py
new file mode 100644
index 00000000..eff967bf
--- /dev/null
+++ b/src/etc/tidy.py
@@ -0,0 +1,25 @@
+#!/usr/bin/python
+
+import sys, fileinput
+
+err=0
+cols=78
+
+def report_err(s):
+ global err
+ print("%s:%d: %s" % (fileinput.filename(), fileinput.filelineno(), s))
+ err=1
+
+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:
+ report_err("CR character")
+
+ if len(line)-1 > cols:
+ report_err("line longer than %d chars" % cols)
+
+
+sys.exit(err)
+
diff --git a/src/etc/x86.supp b/src/etc/x86.supp
new file mode 100644
index 00000000..f829f2ad
--- /dev/null
+++ b/src/etc/x86.supp
@@ -0,0 +1,14 @@
+{
+ our-failure-to-setup-freeres-structure
+ Memcheck:Free
+ fun:free
+ ...
+ fun:_vgnU_freeres
+}
+
+{
+ leaked-TLS-chunk-x86-exit-path-fails-to-clean-up
+ Memcheck:Leak
+ fun:calloc
+ fun:_dl_allocate_tls
+} \ No newline at end of file
diff --git a/src/lib/_int.rs b/src/lib/_int.rs
new file mode 100644
index 00000000..1bb6cb45
--- /dev/null
+++ b/src/lib/_int.rs
@@ -0,0 +1,20 @@
+fn add(int x, int y) -> int { ret x + y; }
+fn sub(int x, int y) -> int { ret x - y; }
+fn mul(int x, int y) -> int { ret x * y; }
+fn div(int x, int y) -> int { ret x / y; }
+fn rem(int x, int y) -> int { ret x % y; }
+
+fn lt(int x, int y) -> bool { ret x < y; }
+fn le(int x, int y) -> bool { ret x <= y; }
+fn eq(int x, int y) -> bool { ret x == y; }
+fn ne(int x, int y) -> bool { ret x != y; }
+fn ge(int x, int y) -> bool { ret x >= y; }
+fn gt(int x, int y) -> bool { ret x > y; }
+
+iter range(mutable int lo, int hi) -> int {
+ while (lo < hi) {
+ put lo;
+ lo += 1;
+ }
+}
+
diff --git a/src/lib/_io.rs b/src/lib/_io.rs
new file mode 100644
index 00000000..1f01c3b3
--- /dev/null
+++ b/src/lib/_io.rs
@@ -0,0 +1,36 @@
+type buf_reader = obj {
+ fn read(vec[u8] buf) -> uint;
+};
+
+type buf_writer = obj {
+ fn write(vec[u8] buf) -> uint;
+};
+
+fn mk_buf_reader(str s) -> buf_reader {
+
+ obj fd_reader(int fd) {
+ fn read(vec[u8] v) -> uint {
+ auto len = _vec.len[u8](v);
+ auto buf = _vec.buf[u8](v);
+ auto count = os.libc.read(fd, buf, len);
+ if (count < 0) {
+ log "error filling buffer";
+ log sys.rustrt.last_os_error();
+ fail;
+ } else {
+ ret uint(count);
+ }
+ }
+ drop {
+ os.libc.close(fd);
+ }
+ }
+
+ auto fd = os.libc.open(_str.buf(s), 0);
+ if (fd < 0) {
+ log "error opening file";
+ log sys.rustrt.last_os_error();
+ fail;
+ }
+ ret fd_reader(fd);
+}
diff --git a/src/lib/_str.rs b/src/lib/_str.rs
new file mode 100644
index 00000000..ac27f294
--- /dev/null
+++ b/src/lib/_str.rs
@@ -0,0 +1,23 @@
+import rustrt.sbuf;
+
+native "rust" mod rustrt {
+ type sbuf;
+ fn str_buf(str s) -> sbuf;
+ fn str_len(str s) -> uint;
+ fn str_alloc(int n_bytes) -> str;
+}
+
+fn is_utf8(vec[u8] v) -> bool {
+}
+
+fn alloc(int n_bytes) -> str {
+ ret rustrt.str_alloc(n_bytes);
+}
+
+fn len(str s) -> uint {
+ ret rustrt.str_len(s);
+}
+
+fn buf(str s) -> sbuf {
+ ret rustrt.str_buf(s);
+}
diff --git a/src/lib/_u8.rs b/src/lib/_u8.rs
new file mode 100644
index 00000000..e1f671e7
--- /dev/null
+++ b/src/lib/_u8.rs
@@ -0,0 +1,20 @@
+fn add(u8 x, u8 y) -> u8 { ret x + y; }
+fn sub(u8 x, u8 y) -> u8 { ret x - y; }
+fn mul(u8 x, u8 y) -> u8 { ret x * y; }
+fn div(u8 x, u8 y) -> u8 { ret x / y; }
+fn rem(u8 x, u8 y) -> u8 { ret x % y; }
+
+fn lt(u8 x, u8 y) -> bool { ret x < y; }
+fn le(u8 x, u8 y) -> bool { ret x <= y; }
+fn eq(u8 x, u8 y) -> bool { ret x == y; }
+fn ne(u8 x, u8 y) -> bool { ret x != y; }
+fn ge(u8 x, u8 y) -> bool { ret x >= y; }
+fn gt(u8 x, u8 y) -> bool { ret x > y; }
+
+iter range(mutable u8 lo, u8 hi) -> u8 {
+ while (lo < hi) {
+ put lo;
+ lo += u8(1);
+ }
+}
+
diff --git a/src/lib/_vec.rs b/src/lib/_vec.rs
new file mode 100644
index 00000000..c938e6fb
--- /dev/null
+++ b/src/lib/_vec.rs
@@ -0,0 +1,30 @@
+import vbuf = rustrt.vbuf;
+
+native "rust" mod rustrt {
+ type vbuf;
+ fn vec_buf[T](vec[T] v) -> vbuf;
+ fn vec_len[T](vec[T] v) -> uint;
+ fn vec_alloc[T](int n_elts) -> vec[T];
+}
+
+fn alloc[T](int n_elts) -> vec[T] {
+ ret rustrt.vec_alloc[T](n_elts);
+}
+
+fn init[T](&T t, int n_elts) -> vec[T] {
+ let vec[T] v = alloc[T](n_elts);
+ let int i = n_elts;
+ while (i > 0) {
+ i -= 1;
+ v += vec(t);
+ }
+ ret v;
+}
+
+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);
+}
diff --git a/src/lib/linux_os.rs b/src/lib/linux_os.rs
new file mode 100644
index 00000000..a775a97a
--- /dev/null
+++ b/src/lib/linux_os.rs
@@ -0,0 +1,19 @@
+import _str.sbuf;
+import _vec.vbuf;
+
+native mod libc = "libc.so.6" {
+
+ fn open(sbuf s, int flags) -> int;
+ fn read(int fd, vbuf buf, uint count) -> int;
+ fn write(int fd, vbuf buf, uint count) -> int;
+ fn close(int fd) -> int;
+
+ type dir;
+ // readdir is a mess; handle via wrapper function in rustrt.
+ fn opendir(sbuf d) -> dir;
+ fn closedir(dir d) -> int;
+
+ fn getenv(sbuf n) -> sbuf;
+ fn setenv(sbuf n, sbuf v, int overwrite) -> int;
+ fn unsetenv(sbuf n) -> int;
+}
diff --git a/src/lib/macos_os.rs b/src/lib/macos_os.rs
new file mode 100644
index 00000000..8b30c8bc
--- /dev/null
+++ b/src/lib/macos_os.rs
@@ -0,0 +1,19 @@
+import _str.sbuf;
+import _vec.vbuf;
+
+native mod libc = "libc.dylib" {
+
+ fn open(sbuf s, int flags) -> int;
+ fn read(int fd, vbuf buf, uint count) -> int;
+ fn write(int fd, vbuf buf, uint count) -> int;
+ fn close(int fd) -> int;
+
+ type dir;
+ // readdir is a mess; handle via wrapper function in rustrt.
+ fn opendir(sbuf d) -> dir;
+ fn closedir(dir d) -> int;
+
+ fn getenv(sbuf n) -> sbuf;
+ fn setenv(sbuf n, sbuf v, int overwrite) -> int;
+ fn unsetenv(sbuf n) -> int;
+}
diff --git a/src/lib/std.rc b/src/lib/std.rc
new file mode 100644
index 00000000..3ddfc04c
--- /dev/null
+++ b/src/lib/std.rc
@@ -0,0 +1,35 @@
+meta (name = "std",
+ desc = "Rust standard library",
+ uuid = "122bed0b-c19b-4b82-b0b7-7ae8aead7297",
+ url = "http://rust-lang.org/src/std",
+ ver = "0.0.1");
+
+// Built-in types support modules.
+
+mod _int;
+mod _u8;
+mod _vec;
+mod _str;
+
+// General IO and system-services modules.
+
+mod _io;
+mod sys;
+
+// Authorize various rule-bendings.
+
+auth _io = unsafe;
+auth _str = unsafe;
+auth _vec = unsafe;
+
+// Target-OS module.
+
+alt (target_os) {
+ case ("win32") {
+ mod os = "win32_os.rs";
+ } case ("macos") {
+ mod os = "macos_os.rs";
+ } else {
+ mod os = "linux_os.rs";
+ }
+}
diff --git a/src/lib/sys.rs b/src/lib/sys.rs
new file mode 100644
index 00000000..84da28f7
--- /dev/null
+++ b/src/lib/sys.rs
@@ -0,0 +1,7 @@
+native "rust" mod rustrt {
+ fn last_os_error() -> str;
+ fn size_of[T]() -> uint;
+ fn align_of[T]() -> uint;
+ fn refcount[T](@T t) -> uint;
+}
+
diff --git a/src/lib/win32_os.rs b/src/lib/win32_os.rs
new file mode 100644
index 00000000..f770a5de
--- /dev/null
+++ b/src/lib/win32_os.rs
@@ -0,0 +1,9 @@
+import _str.sbuf;
+import _vec.vbuf;
+
+native mod libc = "msvcrt.dll" {
+ fn open(sbuf s, int flags) -> int = "_open";
+ 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";
+}
diff --git a/src/rt/bigint/bigint.h b/src/rt/bigint/bigint.h
new file mode 100644
index 00000000..b4c48f03
--- /dev/null
+++ b/src/rt/bigint/bigint.h
@@ -0,0 +1,294 @@
+/* bigint.h - include file for bigint package
+**
+** This library lets you do math on arbitrarily large integers. It's
+** pretty fast - compared with the multi-precision routines in the "bc"
+** calculator program, these routines are between two and twelve times faster,
+** except for division which is maybe half as fast.
+**
+** The calling convention is a little unusual. There's a basic problem
+** with writing a math library in a language that doesn't do automatic
+** garbage collection - what do you do about intermediate results?
+** You'd like to be able to write code like this:
+**
+** d = bi_sqrt( bi_add( bi_multiply( x, x ), bi_multiply( y, y ) ) );
+**
+** That works fine when the numbers being passed back and forth are
+** actual values - ints, floats, or even fixed-size structs. However,
+** when the numbers can be any size, as in this package, then you have
+** to pass them around as pointers to dynamically-allocated objects.
+** Those objects have to get de-allocated after you are done with them.
+** But how do you de-allocate the intermediate results in a complicated
+** multiple-call expression like the above?
+**
+** There are two common solutions to this problem. One, switch all your
+** code to a language that provides automatic garbage collection, for
+** example Java. This is a fine idea and I recommend you do it wherever
+** it's feasible. Two, change your routines to use a calling convention
+** that prevents people from writing multiple-call expressions like that.
+** The resulting code will be somewhat clumsy-looking, but it will work
+** just fine.
+**
+** This package uses a third method, which I haven't seen used anywhere
+** before. It's simple: each number can be used precisely once, after
+** which it is automatically de-allocated. This handles the anonymous
+** intermediate values perfectly. Named values still need to be copied
+** and freed explicitly. Here's the above example using this convention:
+**
+** d = bi_sqrt( bi_add(
+** bi_multiply( bi_copy( x ), bi_copy( x ) ),
+** bi_multiply( bi_copy( y ), bi_copy( y ) ) ) );
+** bi_free( x );
+** bi_free( y );
+**
+** Or, since the package contains a square routine, you could just write:
+**
+** d = bi_sqrt( bi_add( bi_square( x ), bi_square( y ) ) );
+**
+** This time the named values are only being used once, so you don't
+** have to copy and free them.
+**
+** This really works, however you do have to be very careful when writing
+** your code. If you leave out a bi_copy() and use a value more than once,
+** you'll get a runtime error about "zero refs" and a SIGFPE. Run your
+** code in a debugger, get a backtrace to see where the call was, and then
+** eyeball the code there to see where you need to add the bi_copy().
+**
+**
+** Copyright � 2000 by Jef Poskanzer <[email protected]>.
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions
+** are met:
+** 1. Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** 2. Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+**
+** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+** SUCH DAMAGE.
+*/
+
+
+/* Type definition for bigints - it's an opaque type, the real definition
+** is in bigint.c.
+*/
+typedef void* bigint;
+
+
+/* Some convenient pre-initialized numbers. These are all permanent,
+** so you can use them as many times as you want without calling bi_copy().
+*/
+extern bigint bi_0, bi_1, bi_2, bi_10, bi_m1, bi_maxint, bi_minint;
+
+
+/* Initialize the bigint package. You must call this when your program
+** starts up.
+*/
+void bi_initialize( void );
+
+/* Shut down the bigint package. You should call this when your program
+** exits. It's not actually required, but it does do some consistency
+** checks which help keep your program bug-free, so you really ought
+** to call it.
+*/
+void bi_terminate( void );
+
+/* Run in unsafe mode, skipping most runtime checks. Slightly faster.
+** Once your code is debugged you can add this call after bi_initialize().
+*/
+void bi_no_check( void );
+
+/* Make a copy of a bigint. You must call this if you want to use a
+** bigint more than once. (Or you can make the bigint permanent.)
+** Note that this routine is very cheap - all it actually does is
+** increment a reference counter.
+*/
+bigint bi_copy( bigint bi );
+
+/* Make a bigint permanent, so it doesn't get automatically freed when
+** used as an operand.
+*/
+void bi_permanent( bigint bi );
+
+/* Undo bi_permanent(). The next use will free the bigint. */
+void bi_depermanent( bigint bi );
+
+/* Explicitly free a bigint. Normally bigints get freed automatically
+** when they are used as an operand. This routine lets you free one
+** without using it. If the bigint is permanent, this doesn't do
+** anything, you have to depermanent it first.
+*/
+void bi_free( bigint bi );
+
+/* Compare two bigints. Returns -1, 0, or 1. */
+int bi_compare( bigint bia, bigint bib );
+
+/* Convert an int to a bigint. */
+bigint int_to_bi( int i );
+
+/* Convert a string to a bigint. */
+bigint str_to_bi( char* str );
+
+/* Convert a bigint to an int. SIGFPE on overflow. */
+int bi_to_int( bigint bi );
+
+/* Write a bigint to a file. */
+void bi_print( FILE* f, bigint bi );
+
+/* Read a bigint from a file. */
+bigint bi_scan( FILE* f );
+
+
+/* Operations on a bigint and a regular int. */
+
+/* Add an int to a bigint. */
+bigint bi_int_add( bigint bi, int i );
+
+/* Subtract an int from a bigint. */
+bigint bi_int_subtract( bigint bi, int i );
+
+/* Multiply a bigint by an int. */
+bigint bi_int_multiply( bigint bi, int i );
+
+/* Divide a bigint by an int. SIGFPE on divide-by-zero. */
+bigint bi_int_divide( bigint binumer, int denom );
+
+/* Take the remainder of a bigint by an int, with an int result.
+** SIGFPE if m is zero.
+*/
+int bi_int_rem( bigint bi, int m );
+
+/* Take the modulus of a bigint by an int, with an int result.
+** Note that mod is not rem: mod is always within [0..m), while
+** rem can be negative. SIGFPE if m is zero or negative.
+*/
+int bi_int_mod( bigint bi, int m );
+
+
+/* Basic operations on two bigints. */
+
+/* Add two bigints. */
+bigint bi_add( bigint bia, bigint bib );
+
+/* Subtract bib from bia. */
+bigint bi_subtract( bigint bia, bigint bib );
+
+/* Multiply two bigints. */
+bigint bi_multiply( bigint bia, bigint bib );
+
+/* Divide one bigint by another. SIGFPE on divide-by-zero. */
+bigint bi_divide( bigint binumer, bigint bidenom );
+
+/* Binary division of one bigint by another. SIGFPE on divide-by-zero.
+** This is here just for testing. It's about five times slower than
+** regular division.
+*/
+bigint bi_binary_divide( bigint binumer, bigint bidenom );
+
+/* Take the remainder of one bigint by another. SIGFPE if bim is zero. */
+bigint bi_rem( bigint bia, bigint bim );
+
+/* Take the modulus of one bigint by another. Note that mod is not rem:
+** mod is always within [0..bim), while rem can be negative. SIGFPE if
+** bim is zero or negative.
+*/
+bigint bi_mod( bigint bia, bigint bim );
+
+
+/* Some less common operations. */
+
+/* Negate a bigint. */
+bigint bi_negate( bigint bi );
+
+/* Absolute value of a bigint. */
+bigint bi_abs( bigint bi );
+
+/* Divide a bigint in half. */
+bigint bi_half( bigint bi );
+
+/* Multiply a bigint by two. */
+bigint bi_double( bigint bi );
+
+/* Square a bigint. */
+bigint bi_square( bigint bi );
+
+/* Raise bi to the power of biexp. SIGFPE if biexp is negative. */
+bigint bi_power( bigint bi, bigint biexp );
+
+/* Integer square root. */
+bigint bi_sqrt( bigint bi );
+
+/* Factorial. */
+bigint bi_factorial( bigint bi );
+
+
+/* Some predicates. */
+
+/* 1 if the bigint is odd, 0 if it's even. */
+int bi_is_odd( bigint bi );
+
+/* 1 if the bigint is even, 0 if it's odd. */
+int bi_is_even( bigint bi );
+
+/* 1 if the bigint equals zero, 0 if it's nonzero. */
+int bi_is_zero( bigint bi );
+
+/* 1 if the bigint equals one, 0 otherwise. */
+int bi_is_one( bigint bi );
+
+/* 1 if the bigint is less than zero, 0 if it's zero or greater. */
+int bi_is_negative( bigint bi );
+
+
+/* Now we get into the esoteric number-theory stuff used for cryptography. */
+
+/* Modular exponentiation. Much faster than bi_mod(bi_power(bi,biexp),bim).
+** Also, biexp can be negative.
+*/
+bigint bi_mod_power( bigint bi, bigint biexp, bigint bim );
+
+/* Modular inverse. mod( bi * modinv(bi), bim ) == 1. SIGFPE if bi is not
+** relatively prime to bim.
+*/
+bigint bi_mod_inverse( bigint bi, bigint bim );
+
+/* Produce a random number in the half-open interval [0..bi). You need
+** to have called srandom() before using this.
+*/
+bigint bi_random( bigint bi );
+
+/* Greatest common divisor of two bigints. Euclid's algorithm. */
+bigint bi_gcd( bigint bim, bigint bin );
+
+/* Greatest common divisor of two bigints, plus the corresponding multipliers.
+** Extended Euclid's algorithm.
+*/
+bigint bi_egcd( bigint bim, bigint bin, bigint* bim_mul, bigint* bin_mul );
+
+/* Least common multiple of two bigints. */
+bigint bi_lcm( bigint bia, bigint bib );
+
+/* The Jacobi symbol. SIGFPE if bib is even. */
+bigint bi_jacobi( bigint bia, bigint bib );
+
+/* Probabalistic prime checking. A non-zero return means the probability
+** that bi is prime is at least 1 - 1/2 ^ certainty.
+*/
+int bi_is_probable_prime( bigint bi, int certainty );
+
+/* Random probabilistic prime with the specified number of bits. */
+bigint bi_generate_prime( int bits, int certainty );
+
+/* Number of bits in the number. The log base 2, approximately. */
+int bi_bits( bigint bi );
diff --git a/src/rt/bigint/bigint_ext.cpp b/src/rt/bigint/bigint_ext.cpp
new file mode 100644
index 00000000..66d79106
--- /dev/null
+++ b/src/rt/bigint/bigint_ext.cpp
@@ -0,0 +1,553 @@
+/* bigint_ext - external portion of large integer package
+**
+** Copyright � 2000 by Jef Poskanzer <[email protected]>.
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions
+** are met:
+** 1. Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** 2. Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+**
+** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+** SUCH DAMAGE.
+*/
+
+#include <sys/types.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+
+#include "bigint.h"
+#include "low_primes.h"
+
+
+bigint bi_0, bi_1, bi_2, bi_10, bi_m1, bi_maxint, bi_minint;
+
+
+/* Forwards. */
+static void print_pos( FILE* f, bigint bi );
+
+
+bigint
+str_to_bi( char* str )
+ {
+ int sign;
+ bigint biR;
+
+ sign = 1;
+ if ( *str == '-' )
+ {
+ sign = -1;
+ ++str;
+ }
+ for ( biR = bi_0; *str >= '0' && *str <= '9'; ++str )
+ biR = bi_int_add( bi_int_multiply( biR, 10 ), *str - '0' );
+ if ( sign == -1 )
+ biR = bi_negate( biR );
+ return biR;
+ }
+
+
+void
+bi_print( FILE* f, bigint bi )
+ {
+ if ( bi_is_negative( bi_copy( bi ) ) )
+ {
+ putc( '-', f );
+ bi = bi_negate( bi );
+ }
+ print_pos( f, bi );
+ }
+
+
+bigint
+bi_scan( FILE* f )
+ {
+ int sign;
+ int c;
+ bigint biR;
+
+ sign = 1;
+ c = getc( f );
+ if ( c == '-' )
+ sign = -1;
+ else
+ ungetc( c, f );
+
+ biR = bi_0;
+ for (;;)
+ {
+ c = getc( f );
+ if ( c < '0' || c > '9' )
+ break;
+ biR = bi_int_add( bi_int_multiply( biR, 10 ), c - '0' );
+ }
+
+ if ( sign == -1 )
+ biR = bi_negate( biR );
+ return biR;
+ }
+
+
+static void
+print_pos( FILE* f, bigint bi )
+ {
+ if ( bi_compare( bi_copy( bi ), bi_10 ) >= 0 )
+ print_pos( f, bi_int_divide( bi_copy( bi ), 10 ) );
+ putc( bi_int_mod( bi, 10 ) + '0', f );
+ }
+
+
+int
+bi_int_mod( bigint bi, int m )
+ {
+ int r;
+
+ if ( m <= 0 )
+ {
+ (void) fprintf( stderr, "bi_int_mod: zero or negative modulus\n" );
+ (void) kill( getpid(), SIGFPE );
+ }
+ r = bi_int_rem( bi, m );
+ if ( r < 0 )
+ r += m;
+ return r;
+ }
+
+
+bigint
+bi_rem( bigint bia, bigint bim )
+ {
+ return bi_subtract(
+ bia, bi_multiply( bi_divide( bi_copy( bia ), bi_copy( bim ) ), bim ) );
+ }
+
+
+bigint
+bi_mod( bigint bia, bigint bim )
+ {
+ bigint biR;
+
+ if ( bi_compare( bi_copy( bim ), bi_0 ) <= 0 )
+ {
+ (void) fprintf( stderr, "bi_mod: zero or negative modulus\n" );
+ (void) kill( getpid(), SIGFPE );
+ }
+ biR = bi_rem( bia, bi_copy( bim ) );
+ if ( bi_is_negative( bi_copy( biR ) ) )
+ biR = bi_add( biR, bim );
+ else
+ bi_free( bim );
+ return biR;
+ }
+
+
+bigint
+bi_square( bigint bi )
+ {
+ bigint biR;
+
+ biR = bi_multiply( bi_copy( bi ), bi_copy( bi ) );
+ bi_free( bi );
+ return biR;
+ }
+
+
+bigint
+bi_power( bigint bi, bigint biexp )
+ {
+ bigint biR;
+
+ if ( bi_is_negative( bi_copy( biexp ) ) )
+ {
+ (void) fprintf( stderr, "bi_power: negative exponent\n" );
+ (void) kill( getpid(), SIGFPE );
+ }
+ biR = bi_1;
+ for (;;)
+ {
+ if ( bi_is_odd( bi_copy( biexp ) ) )
+ biR = bi_multiply( biR, bi_copy( bi ) );
+ biexp = bi_half( biexp );
+ if ( bi_compare( bi_copy( biexp ), bi_0 ) <= 0 )
+ break;
+ bi = bi_multiply( bi_copy( bi ), bi );
+ }
+ bi_free( bi );
+ bi_free( biexp );
+ return biR;
+ }
+
+
+bigint
+bi_factorial( bigint bi )
+ {
+ bigint biR;
+
+ biR = bi_1;
+ while ( bi_compare( bi_copy( bi ), bi_1 ) > 0 )
+ {
+ biR = bi_multiply( biR, bi_copy( bi ) );
+ bi = bi_int_subtract( bi, 1 );
+ }
+ bi_free( bi );
+ return biR;
+ }
+
+
+int
+bi_is_even( bigint bi )
+ {
+ return ! bi_is_odd( bi );
+ }
+
+
+bigint
+bi_mod_power( bigint bi, bigint biexp, bigint bim )
+ {
+ int invert;
+ bigint biR;
+
+ invert = 0;
+ if ( bi_is_negative( bi_copy( biexp ) ) )
+ {
+ biexp = bi_negate( biexp );
+ invert = 1;
+ }
+
+ biR = bi_1;
+ for (;;)
+ {
+ if ( bi_is_odd( bi_copy( biexp ) ) )
+ biR = bi_mod( bi_multiply( biR, bi_copy( bi ) ), bi_copy( bim ) );
+ biexp = bi_half( biexp );
+ if ( bi_compare( bi_copy( biexp ), bi_0 ) <= 0 )
+ break;
+ bi = bi_mod( bi_multiply( bi_copy( bi ), bi ), bi_copy( bim ) );
+ }
+ bi_free( bi );
+ bi_free( biexp );
+
+ if ( invert )
+ biR = bi_mod_inverse( biR, bim );
+ else
+ bi_free( bim );
+ return biR;
+ }
+
+
+bigint
+bi_mod_inverse( bigint bi, bigint bim )
+ {
+ bigint gcd, mul0, mul1;
+
+ gcd = bi_egcd( bi_copy( bim ), bi, &mul0, &mul1 );
+
+ /* Did we get gcd == 1? */
+ if ( ! bi_is_one( gcd ) )
+ {
+ (void) fprintf( stderr, "bi_mod_inverse: not relatively prime\n" );
+ (void) kill( getpid(), SIGFPE );
+ }
+
+ bi_free( mul0 );
+ return bi_mod( mul1, bim );
+ }
+
+
+/* Euclid's algorithm. */
+bigint
+bi_gcd( bigint bim, bigint bin )
+ {
+ bigint bit;
+
+ bim = bi_abs( bim );
+ bin = bi_abs( bin );
+ while ( ! bi_is_zero( bi_copy( bin ) ) )
+ {
+ bit = bi_mod( bim, bi_copy( bin ) );
+ bim = bin;
+ bin = bit;
+ }
+ bi_free( bin );
+ return bim;
+ }
+
+
+/* Extended Euclidean algorithm. */
+bigint
+bi_egcd( bigint bim, bigint bin, bigint* bim_mul, bigint* bin_mul )
+ {
+ bigint a0, b0, c0, a1, b1, c1, q, t;
+
+ if ( bi_is_negative( bi_copy( bim ) ) )
+ {
+ bigint biR;
+
+ biR = bi_egcd( bi_negate( bim ), bin, &t, bin_mul );
+ *bim_mul = bi_negate( t );
+ return biR;
+ }
+ if ( bi_is_negative( bi_copy( bin ) ) )
+ {
+ bigint biR;
+
+ biR = bi_egcd( bim, bi_negate( bin ), bim_mul, &t );
+ *bin_mul = bi_negate( t );
+ return biR;
+ }
+
+ a0 = bi_1; b0 = bi_0; c0 = bim;
+ a1 = bi_0; b1 = bi_1; c1 = bin;
+
+ while ( ! bi_is_zero( bi_copy( c1 ) ) )
+ {
+ q = bi_divide( bi_copy( c0 ), bi_copy( c1 ) );
+ t = a0;
+ a0 = bi_copy( a1 );
+ a1 = bi_subtract( t, bi_multiply( bi_copy( q ), a1 ) );
+ t = b0;
+ b0 = bi_copy( b1 );
+ b1 = bi_subtract( t, bi_multiply( bi_copy( q ), b1 ) );
+ t = c0;
+ c0 = bi_copy( c1 );
+ c1 = bi_subtract( t, bi_multiply( bi_copy( q ), c1 ) );
+ bi_free( q );
+ }
+
+ bi_free( a1 );
+ bi_free( b1 );
+ bi_free( c1 );
+ *bim_mul = a0;
+ *bin_mul = b0;
+ return c0;
+ }
+
+
+bigint
+bi_lcm( bigint bia, bigint bib )
+ {
+ bigint biR;
+
+ biR = bi_divide(
+ bi_multiply( bi_copy( bia ), bi_copy( bib ) ),
+ bi_gcd( bi_copy( bia ), bi_copy( bib ) ) );
+ bi_free( bia );
+ bi_free( bib );
+ return biR;
+ }
+
+
+/* The Jacobi symbol. */
+bigint
+bi_jacobi( bigint bia, bigint bib )
+ {
+ bigint biR;
+
+ if ( bi_is_even( bi_copy( bib ) ) )
+ {
+ (void) fprintf( stderr, "bi_jacobi: don't know how to compute Jacobi(n, even)\n" );
+ (void) kill( getpid(), SIGFPE );
+ }
+
+ if ( bi_compare( bi_copy( bia ), bi_copy( bib ) ) >= 0 )
+ return bi_jacobi( bi_mod( bia, bi_copy( bib ) ), bib );
+
+ if ( bi_is_zero( bi_copy( bia ) ) || bi_is_one( bi_copy( bia ) ) )
+ {
+ bi_free( bib );
+ return bia;
+ }
+
+ if ( bi_compare( bi_copy( bia ), bi_2 ) == 0 )
+ {
+ bi_free( bia );
+ switch ( bi_int_mod( bib, 8 ) )
+ {
+ case 1: case 7:
+ return bi_1;
+ case 3: case 5:
+ return bi_m1;
+ }
+ }
+
+ if ( bi_is_even( bi_copy( bia ) ) )
+ {
+ biR = bi_multiply(
+ bi_jacobi( bi_2, bi_copy( bib ) ),
+ bi_jacobi( bi_half( bia ), bi_copy( bib ) ) );
+ bi_free( bib );
+ return biR;
+ }
+
+ if ( bi_int_mod( bi_copy( bia ), 4 ) == 3 &&
+ bi_int_mod( bi_copy( bib ), 4 ) == 3 )
+ return bi_negate( bi_jacobi( bib, bia ) );
+ else
+ return bi_jacobi( bib, bia );
+ }
+
+
+/* Probabalistic prime checking. */
+int
+bi_is_probable_prime( bigint bi, int certainty )
+ {
+ int i, p;
+ bigint bim1;
+
+ /* First do trial division by a list of small primes. This eliminates
+ ** many candidates.
+ */
+ for ( i = 0; i < sizeof(low_primes)/sizeof(*low_primes); ++i )
+ {
+ p = low_primes[i];
+ switch ( bi_compare( int_to_bi( p ), bi_copy( bi ) ) )
+ {
+ case 0:
+ bi_free( bi );
+ return 1;
+ case 1:
+ bi_free( bi );
+ return 0;
+ }
+ if ( bi_int_mod( bi_copy( bi ), p ) == 0 )
+ {
+ bi_free( bi );
+ return 0;
+ }
+ }
+
+ /* Now do the probabilistic tests. */
+ bim1 = bi_int_subtract( bi_copy( bi ), 1 );
+ for ( i = 0; i < certainty; ++i )
+ {
+ bigint a, j, jac;
+
+ /* Pick random test number. */
+ a = bi_random( bi_copy( bi ) );
+
+ /* Decide whether to run the Fermat test or the Solovay-Strassen
+ ** test. The Fermat test is fast but lets some composite numbers
+ ** through. Solovay-Strassen runs slower but is more certain.
+ ** So the compromise here is we run the Fermat test a couple of
+ ** times to quickly reject most composite numbers, and then do
+ ** the rest of the iterations with Solovay-Strassen so nothing
+ ** slips through.
+ */
+ if ( i < 2 && certainty >= 5 )
+ {
+ /* Fermat test. Note that this is not state of the art. There's a
+ ** class of numbers called Carmichael numbers which are composite
+ ** but look prime to this test - it lets them slip through no
+ ** matter how many reps you run. However, it's nice and fast so
+ ** we run it anyway to help quickly reject most of the composites.
+ */
+ if ( ! bi_is_one( bi_mod_power( bi_copy( a ), bi_copy( bim1 ), bi_copy( bi ) ) ) )
+ {
+ bi_free( bi );
+ bi_free( bim1 );
+ bi_free( a );
+ return 0;
+ }
+ }
+ else
+ {
+ /* GCD test. This rarely hits, but we need it for Solovay-Strassen. */
+ if ( ! bi_is_one( bi_gcd( bi_copy( bi ), bi_copy( a ) ) ) )
+ {
+ bi_free( bi );
+ bi_free( bim1 );
+ bi_free( a );
+ return 0;
+ }
+
+ /* Solovay-Strassen test. First compute pseudo Jacobi. */
+ j = bi_mod_power(
+ bi_copy( a ), bi_half( bi_copy( bim1 ) ), bi_copy( bi ) );
+ if ( bi_compare( bi_copy( j ), bi_copy( bim1 ) ) == 0 )
+ {
+ bi_free( j );
+ j = bi_m1;
+ }
+
+ /* Now compute real Jacobi. */
+ jac = bi_jacobi( bi_copy( a ), bi_copy( bi ) );
+
+ /* If they're not equal, the number is definitely composite. */
+ if ( bi_compare( j, jac ) != 0 )
+ {
+ bi_free( bi );
+ bi_free( bim1 );
+ bi_free( a );
+ return 0;
+ }
+ }
+
+ bi_free( a );
+ }
+
+ bi_free( bim1 );
+
+ bi_free( bi );
+ return 1;
+ }
+
+
+bigint
+bi_generate_prime( int bits, int certainty )
+ {
+ bigint bimo2, bip;
+ int i, inc = 0;
+
+ bimo2 = bi_power( bi_2, int_to_bi( bits - 1 ) );
+ for (;;)
+ {
+ bip = bi_add( bi_random( bi_copy( bimo2 ) ), bi_copy( bimo2 ) );
+ /* By shoving the candidate numbers up to the next highest multiple
+ ** of six plus or minus one, we pre-eliminate all multiples of
+ ** two and/or three.
+ */
+ switch ( bi_int_mod( bi_copy( bip ), 6 ) )
+ {
+ case 0: inc = 4; bip = bi_int_add( bip, 1 ); break;
+ case 1: inc = 4; break;
+ case 2: inc = 2; bip = bi_int_add( bip, 3 ); break;
+ case 3: inc = 2; bip = bi_int_add( bip, 2 ); break;
+ case 4: inc = 2; bip = bi_int_add( bip, 1 ); break;
+ case 5: inc = 2; break;
+ }
+ /* Starting from the generated random number, check a bunch of
+ ** numbers in sequence. This is just to avoid calls to bi_random(),
+ ** which is more expensive than a simple add.
+ */
+ for ( i = 0; i < 1000; ++i ) /* arbitrary */
+ {
+ if ( bi_is_probable_prime( bi_copy( bip ), certainty ) )
+ {
+ bi_free( bimo2 );
+ return bip;
+ }
+ bip = bi_int_add( bip, inc );
+ inc = 6 - inc;
+ }
+ /* We ran through the whole sequence and didn't find a prime.
+ ** Shrug, just try a different random starting point.
+ */
+ bi_free( bip );
+ }
+ }
diff --git a/src/rt/bigint/bigint_int.cpp b/src/rt/bigint/bigint_int.cpp
new file mode 100644
index 00000000..194ddcb5
--- /dev/null
+++ b/src/rt/bigint/bigint_int.cpp
@@ -0,0 +1,1428 @@
+/* bigint - internal portion of large integer package
+**
+** Copyright � 2000 by Jef Poskanzer <[email protected]>.
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions
+** are met:
+** 1. Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** 2. Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+**
+** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+** SUCH DAMAGE.
+*/
+
+#include <sys/types.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+
+#include "bigint.h"
+
+#define max(a,b) ((a)>(b)?(a):(b))
+#define min(a,b) ((a)<(b)?(a):(b))
+
+/* MAXINT and MININT extracted from <values.h>, which gives a warning
+** message if included.
+*/
+#define BITSPERBYTE 8
+#define BITS(type) (BITSPERBYTE * (int)sizeof(type))
+#define INTBITS BITS(int)
+#define MININT (1 << (INTBITS - 1))
+#define MAXINT (~MININT)
+
+
+/* The package represents arbitrary-precision integers as a sign and a sum
+** of components multiplied by successive powers of the basic radix, i.e.:
+**
+** sign * ( comp0 + comp1 * radix + comp2 * radix^2 + comp3 * radix^3 )
+**
+** To make good use of the computer's word size, the radix is chosen
+** to be a power of two. It could be chosen to be the full word size,
+** however this would require a lot of finagling in the middle of the
+** algorithms to get the inter-word overflows right. That would slow things
+** down. Instead, the radix is chosen to be *half* the actual word size.
+** With just a little care, this means the words can hold all intermediate
+** values, and the overflows can be handled all at once at the end, in a
+** normalization step. This simplifies the coding enormously, and is probably
+** somewhat faster to run. The cost is that numbers use twice as much
+** storage as they would with the most efficient representation, but storage
+** is cheap.
+**
+** A few more notes on the representation:
+**
+** - The sign is always 1 or -1, never 0. The number 0 is represented
+** with a sign of 1.
+** - The components are signed numbers, to allow for negative intermediate
+** values. After normalization, all components are >= 0 and the sign is
+** updated.
+*/
+
+/* Type definition for bigints. */
+typedef int64_t comp; /* should be the largest signed int type you have */
+struct _real_bigint {
+ int refs;
+ struct _real_bigint* next;
+ int num_comps, max_comps;
+ int sign;
+ comp* comps;
+ };
+typedef struct _real_bigint* real_bigint;
+
+
+#undef DUMP
+
+
+#define PERMANENT 123456789
+
+static comp bi_radix, bi_radix_o2;
+static int bi_radix_sqrt, bi_comp_bits;
+
+static real_bigint active_list, free_list;
+static int active_count, free_count;
+static int check_level;
+
+
+/* Forwards. */
+static bigint regular_multiply( real_bigint bia, real_bigint bib );
+static bigint multi_divide( bigint binumer, real_bigint bidenom );
+static bigint multi_divide2( bigint binumer, real_bigint bidenom );
+static void more_comps( real_bigint bi, int n );
+static real_bigint alloc( int num_comps );
+static real_bigint clone( real_bigint bi );
+static void normalize( real_bigint bi );
+static void check( real_bigint bi );
+static void double_check( void );
+static void triple_check( void );
+#ifdef DUMP
+static void dump( char* str, bigint bi );
+#endif /* DUMP */
+static int csqrt( comp c );
+static int cbits( comp c );
+
+
+void
+bi_initialize( void )
+ {
+ /* Set the radix. This does not actually have to be a power of
+ ** two, that's just the most efficient value. It does have to
+ ** be even for bi_half() to work.
+ */
+ bi_radix = 1;
+ bi_radix <<= BITS(comp) / 2 - 1;
+
+ /* Halve the radix. Only used by bi_half(). */
+ bi_radix_o2 = bi_radix >> 1;
+
+ /* Take the square root of the radix. Only used by bi_divide(). */
+ bi_radix_sqrt = csqrt( bi_radix );
+
+ /* Figure out how many bits in a component. Only used by bi_bits(). */
+ bi_comp_bits = cbits( bi_radix - 1 );
+
+ /* Init various globals. */
+ active_list = (real_bigint) 0;
+ active_count = 0;
+ free_list = (real_bigint) 0;
+ free_count = 0;
+
+ /* This can be 0 through 3. */
+ check_level = 3;
+
+ /* Set up some convenient bigints. */
+ bi_0 = int_to_bi( 0 ); bi_permanent( bi_0 );
+ bi_1 = int_to_bi( 1 ); bi_permanent( bi_1 );
+ bi_2 = int_to_bi( 2 ); bi_permanent( bi_2 );
+ bi_10 = int_to_bi( 10 ); bi_permanent( bi_10 );
+ bi_m1 = int_to_bi( -1 ); bi_permanent( bi_m1 );
+ bi_maxint = int_to_bi( MAXINT ); bi_permanent( bi_maxint );
+ bi_minint = int_to_bi( MININT ); bi_permanent( bi_minint );
+ }
+
+
+void
+bi_terminate( void )
+ {
+ real_bigint p, pn;
+
+ bi_depermanent( bi_0 ); bi_free( bi_0 );
+ bi_depermanent( bi_1 ); bi_free( bi_1 );
+ bi_depermanent( bi_2 ); bi_free( bi_2 );
+ bi_depermanent( bi_10 ); bi_free( bi_10 );
+ bi_depermanent( bi_m1 ); bi_free( bi_m1 );
+ bi_depermanent( bi_maxint ); bi_free( bi_maxint );
+ bi_depermanent( bi_minint ); bi_free( bi_minint );
+
+ if ( active_count != 0 )
+ (void) fprintf(
+ stderr, "bi_terminate: there were %d un-freed bigints\n",
+ active_count );
+ if ( check_level >= 2 )
+ double_check();
+ if ( check_level >= 3 )
+ {
+ triple_check();
+ for ( p = active_list; p != (bigint) 0; p = pn )
+ {
+ pn = p->next;
+ free( p->comps );
+ free( p );
+ }
+ }
+ for ( p = free_list; p != (bigint) 0; p = pn )
+ {
+ pn = p->next;
+ free( p->comps );
+ free( p );
+ }
+ }
+
+
+void
+bi_no_check( void )
+ {
+ check_level = 0;
+ }
+
+
+bigint
+bi_copy( bigint obi )
+ {
+ real_bigint bi = (real_bigint) obi;
+
+ check( bi );
+ if ( bi->refs != PERMANENT )
+ ++bi->refs;
+ return bi;
+ }
+
+
+void
+bi_permanent( bigint obi )
+ {
+ real_bigint bi = (real_bigint) obi;
+
+ check( bi );
+ if ( check_level >= 1 && bi->refs != 1 )
+ {
+ (void) fprintf( stderr, "bi_permanent: refs was not 1\n" );
+ (void) kill( getpid(), SIGFPE );
+ }
+ bi->refs = PERMANENT;
+ }
+
+
+void
+bi_depermanent( bigint obi )
+ {
+ real_bigint bi = (real_bigint) obi;
+
+ check( bi );
+ if ( check_level >= 1 && bi->refs != PERMANENT )
+ {
+ (void) fprintf( stderr, "bi_depermanent: bigint was not permanent\n" );
+ (void) kill( getpid(), SIGFPE );
+ }
+ bi->refs = 1;
+ }
+
+
+void
+bi_free( bigint obi )
+ {
+ real_bigint bi = (real_bigint) obi;
+
+ check( bi );
+ if ( bi->refs == PERMANENT )
+ return;
+ --bi->refs;
+ if ( bi->refs > 0 )
+ return;
+ if ( check_level >= 3 )
+ {
+ /* The active list only gets maintained at check levels 3 or higher. */
+ real_bigint* nextP;
+ for ( nextP = &active_list; *nextP != (real_bigint) 0; nextP = &((*nextP)->next) )
+ if ( *nextP == bi )
+ {
+ *nextP = bi->next;
+ break;
+ }
+ }
+ --active_count;
+ bi->next = free_list;
+ free_list = bi;
+ ++free_count;
+ if ( check_level >= 1 && active_count < 0 )
+ {
+ (void) fprintf( stderr,
+ "bi_free: active_count went negative - double-freed bigint?\n" );
+ (void) kill( getpid(), SIGFPE );
+ }
+ }
+
+
+int
+bi_compare( bigint obia, bigint obib )
+ {
+ real_bigint bia = (real_bigint) obia;
+ real_bigint bib = (real_bigint) obib;
+ int r, c;
+
+ check( bia );
+ check( bib );
+
+ /* First check for pointer equality. */
+ if ( bia == bib )
+ r = 0;
+ else
+ {
+ /* Compare signs. */
+ if ( bia->sign > bib->sign )
+ r = 1;
+ else if ( bia->sign < bib->sign )
+ r = -1;
+ /* Signs are the same. Check the number of components. */
+ else if ( bia->num_comps > bib->num_comps )
+ r = bia->sign;
+ else if ( bia->num_comps < bib->num_comps )
+ r = -bia->sign;
+ else
+ {
+ /* Same number of components. Compare starting from the high end
+ ** and working down.
+ */
+ r = 0; /* if we complete the loop, the numbers are equal */
+ for ( c = bia->num_comps - 1; c >= 0; --c )
+ {
+ if ( bia->comps[c] > bib->comps[c] )
+ { r = bia->sign; break; }
+ else if ( bia->comps[c] < bib->comps[c] )
+ { r = -bia->sign; break; }
+ }
+ }
+ }
+
+ bi_free( bia );
+ bi_free( bib );
+ return r;
+ }
+
+
+bigint
+int_to_bi( int i )
+ {
+ real_bigint biR;
+
+ biR = alloc( 1 );
+ biR->sign = 1;
+ biR->comps[0] = i;
+ normalize( biR );
+ check( biR );
+ return biR;
+ }
+
+
+int
+bi_to_int( bigint obi )
+ {
+ real_bigint bi = (real_bigint) obi;
+ comp v, m;
+ int c, r;
+
+ check( bi );
+ if ( bi_compare( bi_copy( bi ), bi_maxint ) > 0 ||
+ bi_compare( bi_copy( bi ), bi_minint ) < 0 )
+ {
+ (void) fprintf( stderr, "bi_to_int: overflow\n" );
+ (void) kill( getpid(), SIGFPE );
+ }
+ v = 0;
+ m = 1;
+ for ( c = 0; c < bi->num_comps; ++c )
+ {
+ v += bi->comps[c] * m;
+ m *= bi_radix;
+ }
+ r = (int) ( bi->sign * v );
+ bi_free( bi );
+ return r;
+ }
+
+
+bigint
+bi_int_add( bigint obi, int i )
+ {
+ real_bigint bi = (real_bigint) obi;
+ real_bigint biR;
+
+ check( bi );
+ biR = clone( bi );
+ if ( biR->sign == 1 )
+ biR->comps[0] += i;
+ else
+ biR->comps[0] -= i;
+ normalize( biR );
+ check( biR );
+ return biR;
+ }
+
+
+bigint
+bi_int_subtract( bigint obi, int i )
+ {
+ real_bigint bi = (real_bigint) obi;
+ real_bigint biR;
+
+ check( bi );
+ biR = clone( bi );
+ if ( biR->sign == 1 )
+ biR->comps[0] -= i;
+ else
+ biR->comps[0] += i;
+ normalize( biR );
+ check( biR );
+ return biR;
+ }
+
+
+bigint
+bi_int_multiply( bigint obi, int i )
+ {
+ real_bigint bi = (real_bigint) obi;
+ real_bigint biR;
+ int c;
+
+ check( bi );
+ biR = clone( bi );
+ if ( i < 0 )
+ {
+ i = -i;
+ biR->sign = -biR->sign;
+ }
+ for ( c = 0; c < biR->num_comps; ++c )
+ biR->comps[c] *= i;
+ normalize( biR );
+ check( biR );
+ return biR;
+ }
+
+
+bigint
+bi_int_divide( bigint obinumer, int denom )
+ {
+ real_bigint binumer = (real_bigint) obinumer;
+ real_bigint biR;
+ int c;
+ comp r;
+
+ check( binumer );
+ if ( denom == 0 )
+ {
+ (void) fprintf( stderr, "bi_int_divide: divide by zero\n" );
+ (void) kill( getpid(), SIGFPE );
+ }
+ biR = clone( binumer );
+ if ( denom < 0 )
+ {
+ denom = -denom;
+ biR->sign = -biR->sign;
+ }
+ r = 0;
+ for ( c = biR->num_comps - 1; c >= 0; --c )
+ {
+ r = r * bi_radix + biR->comps[c];
+ biR->comps[c] = r / denom;
+ r = r % denom;
+ }
+ normalize( biR );
+ check( biR );
+ return biR;
+ }
+
+
+int
+bi_int_rem( bigint obi, int m )
+ {
+ real_bigint bi = (real_bigint) obi;
+ comp rad_r, r;
+ int c;
+
+ check( bi );
+ if ( m == 0 )
+ {
+ (void) fprintf( stderr, "bi_int_rem: divide by zero\n" );
+ (void) kill( getpid(), SIGFPE );
+ }
+ if ( m < 0 )
+ m = -m;
+ rad_r = 1;
+ r = 0;
+ for ( c = 0; c < bi->num_comps; ++c )
+ {
+ r = ( r + bi->comps[c] * rad_r ) % m;
+ rad_r = ( rad_r * bi_radix ) % m;
+ }
+ if ( bi->sign < 1 )
+ r = -r;
+ bi_free( bi );
+ return (int) r;
+ }
+
+
+bigint
+bi_add( bigint obia, bigint obib )
+ {
+ real_bigint bia = (real_bigint) obia;
+ real_bigint bib = (real_bigint) obib;
+ real_bigint biR;
+ int c;
+
+ check( bia );
+ check( bib );
+ biR = clone( bia );
+ more_comps( biR, max( biR->num_comps, bib->num_comps ) );
+ for ( c = 0; c < bib->num_comps; ++c )
+ if ( biR->sign == bib->sign )
+ biR->comps[c] += bib->comps[c];
+ else
+ biR->comps[c] -= bib->comps[c];
+ bi_free( bib );
+ normalize( biR );
+ check( biR );
+ return biR;
+ }
+
+
+bigint
+bi_subtract( bigint obia, bigint obib )
+ {
+ real_bigint bia = (real_bigint) obia;
+ real_bigint bib = (real_bigint) obib;
+ real_bigint biR;
+ int c;
+
+ check( bia );
+ check( bib );
+ biR = clone( bia );
+ more_comps( biR, max( biR->num_comps, bib->num_comps ) );
+ for ( c = 0; c < bib->num_comps; ++c )
+ if ( biR->sign == bib->sign )
+ biR->comps[c] -= bib->comps[c];
+ else
+ biR->comps[c] += bib->comps[c];
+ bi_free( bib );
+ normalize( biR );
+ check( biR );
+ return biR;
+ }
+
+
+/* Karatsuba multiplication. This is supposedly O(n^1.59), better than
+** regular multiplication for large n. The define below sets the crossover
+** point - below that we use regular multiplication, above it we
+** use Karatsuba. Note that Karatsuba is a recursive algorithm, so
+** all Karatsuba calls involve regular multiplications as the base
+** steps.
+*/
+#define KARATSUBA_THRESH 12
+bigint
+bi_multiply( bigint obia, bigint obib )
+ {
+ real_bigint bia = (real_bigint) obia;
+ real_bigint bib = (real_bigint) obib;
+
+ check( bia );
+ check( bib );
+ if ( min( bia->num_comps, bib->num_comps ) < KARATSUBA_THRESH )
+ return regular_multiply( bia, bib );
+ else
+ {
+ /* The factors are large enough that Karatsuba multiplication
+ ** is a win. The basic idea here is you break each factor up
+ ** into two parts, like so:
+ ** i * r^n + j k * r^n + l
+ ** r is the radix we're representing numbers with, so this
+ ** breaking up just means shuffling components around, no
+ ** math required. With regular multiplication the product
+ ** would be:
+ ** ik * r^(n*2) + ( il + jk ) * r^n + jl
+ ** That's four sub-multiplies and one addition, not counting the
+ ** radix-shifting. With Karatsuba, you instead do:
+ ** ik * r^(n*2) + ( (i+j)(k+l) - ik - jl ) * r^n + jl
+ ** This is only three sub-multiplies. The number of adds
+ ** (and subtracts) increases to four, but those run in linear time
+ ** so they are cheap. The sub-multiplies are accomplished by
+ ** recursive calls, eventually reducing to regular multiplication.
+ */
+ int n, c;
+ real_bigint bi_i, bi_j, bi_k, bi_l;
+ real_bigint bi_ik, bi_mid, bi_jl;
+
+ n = ( max( bia->num_comps, bib->num_comps ) + 1 ) / 2;
+ bi_i = alloc( n );
+ bi_j = alloc( n );
+ bi_k = alloc( n );
+ bi_l = alloc( n );
+ for ( c = 0; c < n; ++c )
+ {
+ if ( c + n < bia->num_comps )
+ bi_i->comps[c] = bia->comps[c + n];
+ else
+ bi_i->comps[c] = 0;
+ if ( c < bia->num_comps )
+ bi_j->comps[c] = bia->comps[c];
+ else
+ bi_j->comps[c] = 0;
+ if ( c + n < bib->num_comps )
+ bi_k->comps[c] = bib->comps[c + n];
+ else
+ bi_k->comps[c] = 0;
+ if ( c < bib->num_comps )
+ bi_l->comps[c] = bib->comps[c];
+ else
+ bi_l->comps[c] = 0;
+ }
+ bi_i->sign = bi_j->sign = bi_k->sign = bi_l->sign = 1;
+ normalize( bi_i );
+ normalize( bi_j );
+ normalize( bi_k );
+ normalize( bi_l );
+ bi_ik = bi_multiply( bi_copy( bi_i ), bi_copy( bi_k ) );
+ bi_jl = bi_multiply( bi_copy( bi_j ), bi_copy( bi_l ) );
+ bi_mid = bi_subtract(
+ bi_subtract(
+ bi_multiply( bi_add( bi_i, bi_j ), bi_add( bi_k, bi_l ) ),
+ bi_copy( bi_ik ) ),
+ bi_copy( bi_jl ) );
+ more_comps(
+ bi_jl, max( bi_mid->num_comps + n, bi_ik->num_comps + n * 2 ) );
+ for ( c = 0; c < bi_mid->num_comps; ++c )
+ bi_jl->comps[c + n] += bi_mid->comps[c];
+ for ( c = 0; c < bi_ik->num_comps; ++c )
+ bi_jl->comps[c + n * 2] += bi_ik->comps[c];
+ bi_free( bi_ik );
+ bi_free( bi_mid );
+ bi_jl->sign = bia->sign * bib->sign;
+ bi_free( bia );
+ bi_free( bib );
+ normalize( bi_jl );
+ check( bi_jl );
+ return bi_jl;
+ }
+ }
+
+
+/* Regular O(n^2) multiplication. */
+static bigint
+regular_multiply( real_bigint bia, real_bigint bib )
+ {
+ real_bigint biR;
+ int new_comps, c1, c2;
+
+ check( bia );
+ check( bib );
+ biR = clone( bi_0 );
+ new_comps = bia->num_comps + bib->num_comps;
+ more_comps( biR, new_comps );
+ for ( c1 = 0; c1 < bia->num_comps; ++c1 )
+ {
+ for ( c2 = 0; c2 < bib->num_comps; ++c2 )
+ biR->comps[c1 + c2] += bia->comps[c1] * bib->comps[c2];
+ /* Normalize after each inner loop to avoid overflowing any
+ ** components. But be sure to reset biR's components count,
+ ** in case a previous normalization lowered it.
+ */
+ biR->num_comps = new_comps;
+ normalize( biR );
+ }
+ check( biR );
+ if ( ! bi_is_zero( bi_copy( biR ) ) )
+ biR->sign = bia->sign * bib->sign;
+ bi_free( bia );
+ bi_free( bib );
+ return biR;
+ }
+
+
+/* The following three routines implement a multi-precision divide method
+** that I haven't seen used anywhere else. It is not quite as fast as
+** the standard divide method, but it is a lot simpler. In fact it's
+** about as simple as the binary shift-and-subtract method, which goes
+** about five times slower than this.
+**
+** The method assumes you already have multi-precision multiply and subtract
+** routines, and also a multi-by-single precision divide routine. The latter
+** is used to generate approximations, which are then checked and corrected
+** using the former. The result converges to the correct value by about
+** 16 bits per loop.
+*/
+
+/* Public routine to divide two arbitrary numbers. */
+bigint
+bi_divide( bigint binumer, bigint obidenom )
+ {
+ real_bigint bidenom = (real_bigint) obidenom;
+ int sign;
+ bigint biquotient;
+
+ /* Check signs and trivial cases. */
+ sign = 1;
+ switch ( bi_compare( bi_copy( bidenom ), bi_0 ) )
+ {
+ case 0:
+ (void) fprintf( stderr, "bi_divide: divide by zero\n" );
+ (void) kill( getpid(), SIGFPE );
+ case -1:
+ sign *= -1;
+ bidenom = bi_negate( bidenom );
+ break;
+ }
+ switch ( bi_compare( bi_copy( binumer ), bi_0 ) )
+ {
+ case 0:
+ bi_free( binumer );
+ bi_free( bidenom );
+ return bi_0;
+ case -1:
+ sign *= -1;
+ binumer = bi_negate( binumer );
+ break;
+ }
+ switch ( bi_compare( bi_copy( binumer ), bi_copy( bidenom ) ) )
+ {
+ case -1:
+ bi_free( binumer );
+ bi_free( bidenom );
+ return bi_0;
+ case 0:
+ bi_free( binumer );
+ bi_free( bidenom );
+ if ( sign == 1 )
+ return bi_1;
+ else
+ return bi_m1;
+ }
+
+ /* Is the denominator small enough to do an int divide? */
+ if ( bidenom->num_comps == 1 )
+ {
+ /* Win! */
+ biquotient = bi_int_divide( binumer, bidenom->comps[0] );
+ bi_free( bidenom );
+ }
+ else
+ {
+ /* No, we have to do a full multi-by-multi divide. */
+ biquotient = multi_divide( binumer, bidenom );
+ }
+
+ if ( sign == -1 )
+ biquotient = bi_negate( biquotient );
+ return biquotient;
+ }
+
+
+/* Divide two multi-precision positive numbers. */
+static bigint
+multi_divide( bigint binumer, real_bigint bidenom )
+ {
+ /* We use a successive approximation method that is kind of like a
+ ** continued fraction. The basic approximation is to do an int divide
+ ** by the high-order component of the denominator. Then we correct
+ ** based on the remainder from that.
+ **
+ ** However, if the high-order component is too small, this doesn't
+ ** work well. In particular, if the high-order component is 1 it
+ ** doesn't work at all. Easily fixed, though - if the component
+ ** is too small, increase it!
+ */
+ if ( bidenom->comps[bidenom->num_comps-1] < bi_radix_sqrt )
+ {
+ /* We use the square root of the radix as the threshhold here
+ ** because that's the largest value guaranteed to not make the
+ ** high-order component overflow and become too small again.
+ **
+ ** We increase binumer along with bidenom to keep the end result
+ ** the same.
+ */
+ binumer = bi_int_multiply( binumer, bi_radix_sqrt );
+ bidenom = bi_int_multiply( bidenom, bi_radix_sqrt );
+ }
+
+ /* Now start the recursion. */
+ return multi_divide2( binumer, bidenom );
+ }
+
+
+/* Divide two multi-precision positive conditioned numbers. */
+static bigint
+multi_divide2( bigint binumer, real_bigint bidenom )
+ {
+ real_bigint biapprox;
+ bigint birem, biquotient;
+ int c, o;
+
+ /* Figure out the approximate quotient. Since we're dividing by only
+ ** the top component of the denominator, which is less than or equal to
+ ** the full denominator, the result is guaranteed to be greater than or
+ ** equal to the correct quotient.
+ */
+ o = bidenom->num_comps - 1;
+ biapprox = bi_int_divide( bi_copy( binumer ), bidenom->comps[o] );
+ /* And downshift the result to get the approximate quotient. */
+ for ( c = o; c < biapprox->num_comps; ++c )
+ biapprox->comps[c - o] = biapprox->comps[c];
+ biapprox->num_comps -= o;
+
+ /* Find the remainder from the approximate quotient. */
+ birem = bi_subtract(
+ bi_multiply( bi_copy( biapprox ), bi_copy( bidenom ) ), binumer );
+
+ /* If the remainder is negative, zero, or in fact any value less
+ ** than bidenom, then we have the correct quotient and we're done.
+ */
+ if ( bi_compare( bi_copy( birem ), bi_copy( bidenom ) ) < 0 )
+ {
+ biquotient = biapprox;
+ bi_free( birem );
+ bi_free( bidenom );
+ }
+ else
+ {
+ /* The real quotient is now biapprox - birem / bidenom. We still
+ ** have to do a divide. However, birem is smaller than binumer,
+ ** so the next divide will go faster. We do the divide by
+ ** recursion. Since this is tail-recursion or close to it, we
+ ** could probably re-arrange things and make it a non-recursive
+ ** loop, but the overhead of recursion is small and the bookkeeping
+ ** is simpler this way.
+ **
+ ** Note that since the sub-divide uses the same denominator, it
+ ** doesn't have to adjust the values again - the high-order component
+ ** will still be good.
+ */
+ biquotient = bi_subtract( biapprox, multi_divide2( birem, bidenom ) );
+ }
+
+ return biquotient;
+ }
+
+
+/* Binary division - about five times slower than the above. */
+bigint
+bi_binary_divide( bigint binumer, bigint obidenom )
+ {
+ real_bigint bidenom = (real_bigint) obidenom;
+ int sign;
+ bigint biquotient;
+
+ /* Check signs and trivial cases. */
+ sign = 1;
+ switch ( bi_compare( bi_copy( bidenom ), bi_0 ) )
+ {
+ case 0:
+ (void) fprintf( stderr, "bi_divide: divide by zero\n" );
+ (void) kill( getpid(), SIGFPE );
+ case -1:
+ sign *= -1;
+ bidenom = bi_negate( bidenom );
+ break;
+ }
+ switch ( bi_compare( bi_copy( binumer ), bi_0 ) )
+ {
+ case 0:
+ bi_free( binumer );
+ bi_free( bidenom );
+ return bi_0;
+ case -1:
+ sign *= -1;
+ binumer = bi_negate( binumer );
+ break;
+ }
+ switch ( bi_compare( bi_copy( binumer ), bi_copy( bidenom ) ) )
+ {
+ case -1:
+ bi_free( binumer );
+ bi_free( bidenom );
+ return bi_0;
+ case 0:
+ bi_free( binumer );
+ bi_free( bidenom );
+ if ( sign == 1 )
+ return bi_1;
+ else
+ return bi_m1;
+ }
+
+ /* Is the denominator small enough to do an int divide? */
+ if ( bidenom->num_comps == 1 )
+ {
+ /* Win! */
+ biquotient = bi_int_divide( binumer, bidenom->comps[0] );
+ bi_free( bidenom );
+ }
+ else
+ {
+ /* No, we have to do a full multi-by-multi divide. */
+ int num_bits, den_bits, i;
+
+ num_bits = bi_bits( bi_copy( binumer ) );
+ den_bits = bi_bits( bi_copy( bidenom ) );
+ bidenom = bi_multiply( bidenom, bi_power( bi_2, int_to_bi( num_bits - den_bits ) ) );
+ biquotient = bi_0;
+ for ( i = den_bits; i <= num_bits; ++i )
+ {
+ biquotient = bi_double( biquotient );
+ if ( bi_compare( bi_copy( binumer ), bi_copy( bidenom ) ) >= 0 )
+ {
+ biquotient = bi_int_add( biquotient, 1 );
+ binumer = bi_subtract( binumer, bi_copy( bidenom ) );
+ }
+ bidenom = bi_half( bidenom );
+ }
+ bi_free( binumer );
+ bi_free( bidenom );
+ }
+
+ if ( sign == -1 )
+ biquotient = bi_negate( biquotient );
+ return biquotient;
+ }
+
+
+bigint
+bi_negate( bigint obi )
+ {
+ real_bigint bi = (real_bigint) obi;
+ real_bigint biR;
+
+ check( bi );
+ biR = clone( bi );
+ biR->sign = -biR->sign;
+ check( biR );
+ return biR;
+ }
+
+
+bigint
+bi_abs( bigint obi )
+ {
+ real_bigint bi = (real_bigint) obi;
+ real_bigint biR;
+
+ check( bi );
+ biR = clone( bi );
+ biR->sign = 1;
+ check( biR );
+ return biR;
+ }
+
+
+bigint
+bi_half( bigint obi )
+ {
+ real_bigint bi = (real_bigint) obi;
+ real_bigint biR;
+ int c;
+
+ check( bi );
+ /* This depends on the radix being even. */
+ biR = clone( bi );
+ for ( c = 0; c < biR->num_comps; ++c )
+ {
+ if ( biR->comps[c] & 1 )
+ if ( c > 0 )
+ biR->comps[c - 1] += bi_radix_o2;
+ biR->comps[c] = biR->comps[c] >> 1;
+ }
+ /* Avoid normalization. */
+ if ( biR->num_comps > 1 && biR->comps[biR->num_comps-1] == 0 )
+ --biR->num_comps;
+ check( biR );
+ return biR;
+ }
+
+
+bigint
+bi_double( bigint obi )
+ {
+ real_bigint bi = (real_bigint) obi;
+ real_bigint biR;
+ int c;
+
+ check( bi );
+ biR = clone( bi );
+ for ( c = biR->num_comps - 1; c >= 0; --c )
+ {
+ biR->comps[c] = biR->comps[c] << 1;
+ if ( biR->comps[c] >= bi_radix )
+ {
+ if ( c + 1 >= biR->num_comps )
+ more_comps( biR, biR->num_comps + 1 );
+ biR->comps[c] -= bi_radix;
+ biR->comps[c + 1] += 1;
+ }
+ }
+ check( biR );
+ return biR;
+ }
+
+
+/* Find integer square root by Newton's method. */
+bigint
+bi_sqrt( bigint obi )
+ {
+ real_bigint bi = (real_bigint) obi;
+ bigint biR, biR2, bidiff;
+
+ switch ( bi_compare( bi_copy( bi ), bi_0 ) )
+ {
+ case -1:
+ (void) fprintf( stderr, "bi_sqrt: imaginary result\n" );
+ (void) kill( getpid(), SIGFPE );
+ case 0:
+ return bi;
+ }
+ if ( bi_is_one( bi_copy( bi ) ) )
+ return bi;
+
+ /* Newton's method converges reasonably fast, but it helps to have
+ ** a good initial guess. We can make a *very* good initial guess
+ ** by taking the square root of the top component times the square
+ ** root of the radix part. Both of those are easy to compute.
+ */
+ biR = bi_int_multiply(
+ bi_power( int_to_bi( bi_radix_sqrt ), int_to_bi( bi->num_comps - 1 ) ),
+ csqrt( bi->comps[bi->num_comps - 1] ) );
+
+ /* Now do the Newton loop until we have the answer. */
+ for (;;)
+ {
+ biR2 = bi_divide( bi_copy( bi ), bi_copy( biR ) );
+ bidiff = bi_subtract( bi_copy( biR ), bi_copy( biR2 ) );
+ if ( bi_is_zero( bi_copy( bidiff ) ) ||
+ bi_compare( bi_copy( bidiff ), bi_m1 ) == 0 )
+ {
+ bi_free( bi );
+ bi_free( bidiff );
+ bi_free( biR2 );
+ return biR;
+ }
+ if ( bi_is_one( bi_copy( bidiff ) ) )
+ {
+ bi_free( bi );
+ bi_free( bidiff );
+ bi_free( biR );
+ return biR2;
+ }
+ bi_free( bidiff );
+ biR = bi_half( bi_add( biR, biR2 ) );
+ }
+ }
+
+
+int
+bi_is_odd( bigint obi )
+ {
+ real_bigint bi = (real_bigint) obi;
+ int r;
+
+ check( bi );
+ r = bi->comps[0] & 1;
+ bi_free( bi );
+ return r;
+ }
+
+
+int
+bi_is_zero( bigint obi )
+ {
+ real_bigint bi = (real_bigint) obi;
+ int r;
+
+ check( bi );
+ r = ( bi->sign == 1 && bi->num_comps == 1 && bi->comps[0] == 0 );
+ bi_free( bi );
+ return r;
+ }
+
+
+int
+bi_is_one( bigint obi )
+ {
+ real_bigint bi = (real_bigint) obi;
+ int r;
+
+ check( bi );
+ r = ( bi->sign == 1 && bi->num_comps == 1 && bi->comps[0] == 1 );
+ bi_free( bi );
+ return r;
+ }
+
+
+int
+bi_is_negative( bigint obi )
+ {
+ real_bigint bi = (real_bigint) obi;
+ int r;
+
+ check( bi );
+ r = ( bi->sign == -1 );
+ bi_free( bi );
+ return r;
+ }
+
+
+bigint
+bi_random( bigint bi )
+ {
+ real_bigint biR;
+ int c;
+
+ biR = bi_multiply( bi_copy( bi ), bi_copy( bi ) );
+ for ( c = 0; c < biR->num_comps; ++c )
+ biR->comps[c] = random();
+ normalize( biR );
+ biR = bi_mod( biR, bi );
+ return biR;
+ }
+
+
+int
+bi_bits( bigint obi )
+ {
+ real_bigint bi = (real_bigint) obi;
+ int bits;
+
+ bits =
+ bi_comp_bits * ( bi->num_comps - 1 ) +
+ cbits( bi->comps[bi->num_comps - 1] );
+ bi_free( bi );
+ return bits;
+ }
+
+
+/* Allocate and zero more components. Does not consume bi, of course. */
+static void
+more_comps( real_bigint bi, int n )
+ {
+ if ( n > bi->max_comps )
+ {
+ bi->max_comps = max( bi->max_comps * 2, n );
+ bi->comps = (comp*) realloc(
+ (void*) bi->comps, bi->max_comps * sizeof(comp) );
+ if ( bi->comps == (comp*) 0 )
+ {
+ (void) fprintf( stderr, "out of memory\n" );
+ exit( 1 );
+ }
+ }
+ for ( ; bi->num_comps < n; ++bi->num_comps )
+ bi->comps[bi->num_comps] = 0;
+ }
+
+
+/* Make a new empty bigint. Fills in everything except sign and the
+** components.
+*/
+static real_bigint
+alloc( int num_comps )
+ {
+ real_bigint biR;
+
+ /* Can we recycle an old bigint? */
+ if ( free_list != (real_bigint) 0 )
+ {
+ biR = free_list;
+ free_list = biR->next;
+ --free_count;
+ if ( check_level >= 1 && biR->refs != 0 )
+ {
+ (void) fprintf( stderr, "alloc: refs was not 0\n" );
+ (void) kill( getpid(), SIGFPE );
+ }
+ more_comps( biR, num_comps );
+ }
+ else
+ {
+ /* No free bigints available - create a new one. */
+ biR = (real_bigint) malloc( sizeof(struct _real_bigint) );
+ if ( biR == (real_bigint) 0 )
+ {
+ (void) fprintf( stderr, "out of memory\n" );
+ exit( 1 );
+ }
+ biR->comps = (comp*) malloc( num_comps * sizeof(comp) );
+ if ( biR->comps == (comp*) 0 )
+ {
+ (void) fprintf( stderr, "out of memory\n" );
+ exit( 1 );
+ }
+ biR->max_comps = num_comps;
+ }
+ biR->num_comps = num_comps;
+ biR->refs = 1;
+ if ( check_level >= 3 )
+ {
+ /* The active list only gets maintained at check levels 3 or higher. */
+ biR->next = active_list;
+ active_list = biR;
+ }
+ else
+ biR->next = (real_bigint) 0;
+ ++active_count;
+ return biR;
+ }
+
+
+/* Make a modifiable copy of bi. DOES consume bi. */
+static real_bigint
+clone( real_bigint bi )
+ {
+ real_bigint biR;
+ int c;
+
+ /* Very clever optimization. */
+ if ( bi->refs != PERMANENT && bi->refs == 1 )
+ return bi;
+
+ biR = alloc( bi->num_comps );
+ biR->sign = bi->sign;
+ for ( c = 0; c < bi->num_comps; ++c )
+ biR->comps[c] = bi->comps[c];
+ bi_free( bi );
+ return biR;
+ }
+
+
+/* Put bi into normal form. Does not consume bi, of course.
+**
+** Normal form is:
+** - All components >= 0 and < bi_radix.
+** - Leading 0 components removed.
+** - Sign either 1 or -1.
+** - The number zero represented by a single 0 component and a sign of 1.
+*/
+static void
+normalize( real_bigint bi )
+ {
+ int c;
+
+ /* Borrow for negative components. Got to be careful with the math here:
+ ** -9 / 10 == 0 -9 % 10 == -9
+ ** -10 / 10 == -1 -10 % 10 == 0
+ ** -11 / 10 == -1 -11 % 10 == -1
+ */
+ for ( c = 0; c < bi->num_comps - 1; ++c )
+ if ( bi->comps[c] < 0 )
+ {
+ bi->comps[c+1] += bi->comps[c] / bi_radix - 1;
+ bi->comps[c] = bi->comps[c] % bi_radix;
+ if ( bi->comps[c] != 0 )
+ bi->comps[c] += bi_radix;
+ else
+ bi->comps[c+1] += 1;
+ }
+ /* Is the top component negative? */
+ if ( bi->comps[bi->num_comps - 1] < 0 )
+ {
+ /* Switch the sign of the number, and fix up the components. */
+ bi->sign = -bi->sign;
+ for ( c = 0; c < bi->num_comps - 1; ++c )
+ {
+ bi->comps[c] = bi_radix - bi->comps[c];
+ bi->comps[c + 1] += 1;
+ }
+ bi->comps[bi->num_comps - 1] = -bi->comps[bi->num_comps - 1];
+ }
+
+ /* Carry for components larger than the radix. */
+ for ( c = 0; c < bi->num_comps; ++c )
+ if ( bi->comps[c] >= bi_radix )
+ {
+ if ( c + 1 >= bi->num_comps )
+ more_comps( bi, bi->num_comps + 1 );
+ bi->comps[c+1] += bi->comps[c] / bi_radix;
+ bi->comps[c] = bi->comps[c] % bi_radix;
+ }
+
+ /* Trim off any leading zero components. */
+ for ( ; bi->num_comps > 1 && bi->comps[bi->num_comps-1] == 0; --bi->num_comps )
+ ;
+
+ /* Check for -0. */
+ if ( bi->num_comps == 1 && bi->comps[0] == 0 && bi->sign == -1 )
+ bi->sign = 1;
+ }
+
+
+static void
+check( real_bigint bi )
+ {
+ if ( check_level == 0 )
+ return;
+ if ( bi->refs == 0 )
+ {
+ (void) fprintf( stderr, "check: zero refs in bigint\n" );
+ (void) kill( getpid(), SIGFPE );
+ }
+ if ( bi->refs < 0 )
+ {
+ (void) fprintf( stderr, "check: negative refs in bigint\n" );
+ (void) kill( getpid(), SIGFPE );
+ }
+ if ( check_level < 3 )
+ {
+ /* At check levels less than 3, active bigints have a zero next. */
+ if ( bi->next != (real_bigint) 0 )
+ {
+ (void) fprintf(
+ stderr, "check: attempt to use a bigint from the free list\n" );
+ (void) kill( getpid(), SIGFPE );
+ }
+ }
+ else
+ {
+ /* At check levels 3 or higher, active bigints must be on the active
+ ** list.
+ */
+ real_bigint p;
+
+ for ( p = active_list; p != (real_bigint) 0; p = p->next )
+ if ( p == bi )
+ break;
+ if ( p == (real_bigint) 0 )
+ {
+ (void) fprintf( stderr,
+ "check: attempt to use a bigint not on the active list\n" );
+ (void) kill( getpid(), SIGFPE );
+ }
+ }
+ if ( check_level >= 2 )
+ double_check();
+ if ( check_level >= 3 )
+ triple_check();
+ }
+
+
+static void
+double_check( void )
+ {
+ real_bigint p;
+ int c;
+
+ for ( p = free_list, c = 0; p != (real_bigint) 0; p = p->next, ++c )
+ if ( p->refs != 0 )
+ {
+ (void) fprintf( stderr,
+ "double_check: found a non-zero ref on the free list\n" );
+ (void) kill( getpid(), SIGFPE );
+ }
+ if ( c != free_count )
+ {
+ (void) fprintf( stderr,
+ "double_check: free_count is %d but the free list has %d items\n",
+ free_count, c );
+ (void) kill( getpid(), SIGFPE );
+ }
+ }
+
+
+static void
+triple_check( void )
+ {
+ real_bigint p;
+ int c;
+
+ for ( p = active_list, c = 0; p != (real_bigint) 0; p = p->next, ++c )
+ if ( p->refs == 0 )
+ {
+ (void) fprintf( stderr,
+ "triple_check: found a zero ref on the active list\n" );
+ (void) kill( getpid(), SIGFPE );
+ }
+ if ( c != active_count )
+ {
+ (void) fprintf( stderr,
+ "triple_check: active_count is %d but active_list has %d items\n",
+ free_count, c );
+ (void) kill( getpid(), SIGFPE );
+ }
+ }
+
+
+#ifdef DUMP
+/* Debug routine to dump out a complete bigint. Does not consume bi. */
+static void
+dump( char* str, bigint obi )
+ {
+ int c;
+ real_bigint bi = (real_bigint) obi;
+
+ (void) fprintf( stdout, "dump %s at 0x%08x:\n", str, (unsigned int) bi );
+ (void) fprintf( stdout, " refs: %d\n", bi->refs );
+ (void) fprintf( stdout, " next: 0x%08x\n", (unsigned int) bi->next );
+ (void) fprintf( stdout, " num_comps: %d\n", bi->num_comps );
+ (void) fprintf( stdout, " max_comps: %d\n", bi->max_comps );
+ (void) fprintf( stdout, " sign: %d\n", bi->sign );
+ for ( c = bi->num_comps - 1; c >= 0; --c )
+ (void) fprintf( stdout, " comps[%d]: %11lld (0x%016llx)\n", c, (long long) bi->comps[c], (long long) bi->comps[c] );
+ (void) fprintf( stdout, " print: " );
+ bi_print( stdout, bi_copy( bi ) );
+ (void) fprintf( stdout, "\n" );
+ }
+#endif /* DUMP */
+
+
+/* Trivial square-root routine so that we don't have to link in the math lib. */
+static int
+csqrt( comp c )
+ {
+ comp r, r2, diff;
+
+ if ( c < 0 )
+ {
+ (void) fprintf( stderr, "csqrt: imaginary result\n" );
+ (void) kill( getpid(), SIGFPE );
+ }
+
+ r = c / 2;
+ for (;;)
+ {
+ r2 = c / r;
+ diff = r - r2;
+ if ( diff == 0 || diff == -1 )
+ return (int) r;
+ if ( diff == 1 )
+ return (int) r2;
+ r = ( r + r2 ) / 2;
+ }
+ }
+
+
+/* Figure out how many bits are in a number. */
+static int
+cbits( comp c )
+ {
+ int b;
+
+ for ( b = 0; c != 0; ++b )
+ c >>= 1;
+ return b;
+ }
diff --git a/src/rt/bigint/low_primes.h b/src/rt/bigint/low_primes.h
new file mode 100644
index 00000000..c9d3df0b
--- /dev/null
+++ b/src/rt/bigint/low_primes.h
@@ -0,0 +1,1069 @@
+/* Primes up to 100000. */
+static long low_primes[] = {
+ 2, 3, 5, 7, 11, 13, 17, 19, 23,
+ 29, 31, 37, 41, 43, 47, 53, 59, 61,
+ 67, 71, 73, 79, 83, 89, 97, 101, 103,
+ 107, 109, 113, 127, 131, 137, 139, 149, 151,
+ 157, 163, 167, 173, 179, 181, 191, 193, 197,
+ 199, 211, 223, 227, 229, 233, 239, 241, 251,
+ 257, 263, 269, 271, 277, 281, 283, 293, 307,
+ 311, 313, 317, 331, 337, 347, 349, 353, 359,
+ 367, 373, 379, 383, 389, 397, 401, 409, 419,
+ 421, 431, 433, 439, 443, 449, 457, 461, 463,
+ 467, 479, 487, 491, 499, 503, 509, 521, 523,
+ 541, 547, 557, 563, 569, 571, 577, 587, 593,
+ 599, 601, 607, 613, 617, 619, 631, 641, 643,
+ 647, 653, 659, 661, 673, 677, 683, 691, 701,
+ 709, 719, 727, 733, 739, 743, 751, 757, 761,
+ 769, 773, 787, 797, 809, 811, 821, 823, 827,
+ 829, 839, 853, 857, 859, 863, 877, 881, 883,
+ 887, 907, 911, 919, 929, 937, 941, 947, 953,
+ 967, 971, 977, 983, 991, 997, 1009, 1013, 1019,
+ 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069,
+ 1087, 1091, 1093, 1097, 1103, 1109, 1117, 1123, 1129,
+ 1151, 1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213,
+ 1217, 1223, 1229, 1231, 1237, 1249, 1259, 1277, 1279,
+ 1283, 1289, 1291, 1297, 1301, 1303, 1307, 1319, 1321,
+ 1327, 1361, 1367, 1373, 1381, 1399, 1409, 1423, 1427,
+ 1429, 1433, 1439, 1447, 1451, 1453, 1459, 1471, 1481,
+ 1483, 1487, 1489, 1493, 1499, 1511, 1523, 1531, 1543,
+ 1549, 1553, 1559, 1567, 1571, 1579, 1583, 1597, 1601,
+ 1607, 1609, 1613, 1619, 1621, 1627, 1637, 1657, 1663,
+ 1667, 1669, 1693, 1697, 1699, 1709, 1721, 1723, 1733,
+ 1741, 1747, 1753, 1759, 1777, 1783, 1787, 1789, 1801,
+ 1811, 1823, 1831, 1847, 1861, 1867, 1871, 1873, 1877,
+ 1879, 1889, 1901, 1907, 1913, 1931, 1933, 1949, 1951,
+ 1973, 1979, 1987, 1993, 1997, 1999, 2003, 2011, 2017,
+ 2027, 2029, 2039, 2053, 2063, 2069, 2081, 2083, 2087,
+ 2089, 2099, 2111, 2113, 2129, 2131, 2137, 2141, 2143,
+ 2153, 2161, 2179, 2203, 2207, 2213, 2221, 2237, 2239,
+ 2243, 2251, 2267, 2269, 2273, 2281, 2287, 2293, 2297,
+ 2309, 2311, 2333, 2339, 2341, 2347, 2351, 2357, 2371,
+ 2377, 2381, 2383, 2389, 2393, 2399, 2411, 2417, 2423,
+ 2437, 2441, 2447, 2459, 2467, 2473, 2477, 2503, 2521,
+ 2531, 2539, 2543, 2549, 2551, 2557, 2579, 2591, 2593,
+ 2609, 2617, 2621, 2633, 2647, 2657, 2659, 2663, 2671,
+ 2677, 2683, 2687, 2689, 2693, 2699, 2707, 2711, 2713,
+ 2719, 2729, 2731, 2741, 2749, 2753, 2767, 2777, 2789,
+ 2791, 2797, 2801, 2803, 2819, 2833, 2837, 2843, 2851,
+ 2857, 2861, 2879, 2887, 2897, 2903, 2909, 2917, 2927,
+ 2939, 2953, 2957, 2963, 2969, 2971, 2999, 3001, 3011,
+ 3019, 3023, 3037, 3041, 3049, 3061, 3067, 3079, 3083,
+ 3089, 3109, 3119, 3121, 3137, 3163, 3167, 3169, 3181,
+ 3187, 3191, 3203, 3209, 3217, 3221, 3229, 3251, 3253,
+ 3257, 3259, 3271, 3299, 3301, 3307, 3313, 3319, 3323,
+ 3329, 3331, 3343, 3347, 3359, 3361, 3371, 3373, 3389,
+ 3391, 3407, 3413, 3433, 3449, 3457, 3461, 3463, 3467,
+ 3469, 3491, 3499, 3511, 3517, 3527, 3529, 3533, 3539,
+ 3541, 3547, 3557, 3559, 3571, 3581, 3583, 3593, 3607,
+ 3613, 3617, 3623, 3631, 3637, 3643, 3659, 3671, 3673,
+ 3677, 3691, 3697, 3701, 3709, 3719, 3727, 3733, 3739,
+ 3761, 3767, 3769, 3779, 3793, 3797, 3803, 3821, 3823,
+ 3833, 3847, 3851, 3853, 3863, 3877, 3881, 3889, 3907,
+ 3911, 3917, 3919, 3923, 3929, 3931, 3943, 3947, 3967,
+ 3989, 4001, 4003, 4007, 4013, 4019, 4021, 4027, 4049,
+ 4051, 4057, 4073, 4079, 4091, 4093, 4099, 4111, 4127,
+ 4129, 4133, 4139, 4153, 4157, 4159, 4177, 4201, 4211,
+ 4217, 4219, 4229, 4231, 4241, 4243, 4253, 4259, 4261,
+ 4271, 4273, 4283, 4289, 4297, 4327, 4337, 4339, 4349,
+ 4357, 4363, 4373, 4391, 4397, 4409, 4421, 4423, 4441,
+ 4447, 4451, 4457, 4463, 4481, 4483, 4493, 4507, 4513,
+ 4517, 4519, 4523, 4547, 4549, 4561, 4567, 4583, 4591,
+ 4597, 4603, 4621, 4637, 4639, 4643, 4649, 4651, 4657,
+ 4663, 4673, 4679, 4691, 4703, 4721, 4723, 4729, 4733,
+ 4751, 4759, 4783, 4787, 4789, 4793, 4799, 4801, 4813,
+ 4817, 4831, 4861, 4871, 4877, 4889, 4903, 4909, 4919,
+ 4931, 4933, 4937, 4943, 4951, 4957, 4967, 4969, 4973,
+ 4987, 4993, 4999, 5003, 5009, 5011, 5021, 5023, 5039,
+ 5051, 5059, 5077, 5081, 5087, 5099, 5101, 5107, 5113,
+ 5119, 5147, 5153, 5167, 5171, 5179, 5189, 5197, 5209,
+ 5227, 5231, 5233, 5237, 5261, 5273, 5279, 5281, 5297,
+ 5303, 5309, 5323, 5333, 5347, 5351, 5381, 5387, 5393,
+ 5399, 5407, 5413, 5417, 5419, 5431, 5437, 5441, 5443,
+ 5449, 5471, 5477, 5479, 5483, 5501, 5503, 5507, 5519,
+ 5521, 5527, 5531, 5557, 5563, 5569, 5573, 5581, 5591,
+ 5623, 5639, 5641, 5647, 5651, 5653, 5657, 5659, 5669,
+ 5683, 5689, 5693, 5701, 5711, 5717, 5737, 5741, 5743,
+ 5749, 5779, 5783, 5791, 5801, 5807, 5813, 5821, 5827,
+ 5839, 5843, 5849, 5851, 5857, 5861, 5867, 5869, 5879,
+ 5881, 5897, 5903, 5923, 5927, 5939, 5953, 5981, 5987,
+ 6007, 6011, 6029, 6037, 6043, 6047, 6053, 6067, 6073,
+ 6079, 6089, 6091, 6101, 6113, 6121, 6131, 6133, 6143,
+ 6151, 6163, 6173, 6197, 6199, 6203, 6211, 6217, 6221,
+ 6229, 6247, 6257, 6263, 6269, 6271, 6277, 6287, 6299,
+ 6301, 6311, 6317, 6323, 6329, 6337, 6343, 6353, 6359,
+ 6361, 6367, 6373, 6379, 6389, 6397, 6421, 6427, 6449,
+ 6451, 6469, 6473, 6481, 6491, 6521, 6529, 6547, 6551,
+ 6553, 6563, 6569, 6571, 6577, 6581, 6599, 6607, 6619,
+ 6637, 6653, 6659, 6661, 6673, 6679, 6689, 6691, 6701,
+ 6703, 6709, 6719, 6733, 6737, 6761, 6763, 6779, 6781,
+ 6791, 6793, 6803, 6823, 6827, 6829, 6833, 6841, 6857,
+ 6863, 6869, 6871, 6883, 6899, 6907, 6911, 6917, 6947,
+ 6949, 6959, 6961, 6967, 6971, 6977, 6983, 6991, 6997,
+ 7001, 7013, 7019, 7027, 7039, 7043, 7057, 7069, 7079,
+ 7103, 7109, 7121, 7127, 7129, 7151, 7159, 7177, 7187,
+ 7193, 7207, 7211, 7213, 7219, 7229, 7237, 7243, 7247,
+ 7253, 7283, 7297, 7307, 7309, 7321, 7331, 7333, 7349,
+ 7351, 7369, 7393, 7411, 7417, 7433, 7451, 7457, 7459,
+ 7477, 7481, 7487, 7489, 7499, 7507, 7517, 7523, 7529,
+ 7537, 7541, 7547, 7549, 7559, 7561, 7573, 7577, 7583,
+ 7589, 7591, 7603, 7607, 7621, 7639, 7643, 7649, 7669,
+ 7673, 7681, 7687, 7691, 7699, 7703, 7717, 7723, 7727,
+ 7741, 7753, 7757, 7759, 7789, 7793, 7817, 7823, 7829,
+ 7841, 7853, 7867, 7873, 7877, 7879, 7883, 7901, 7907,
+ 7919, 7927, 7933, 7937, 7949, 7951, 7963, 7993, 8009,
+ 8011, 8017, 8039, 8053, 8059, 8069, 8081, 8087, 8089,
+ 8093, 8101, 8111, 8117, 8123, 8147, 8161, 8167, 8171,
+ 8179, 8191, 8209, 8219, 8221, 8231, 8233, 8237, 8243,
+ 8263, 8269, 8273, 8287, 8291, 8293, 8297, 8311, 8317,
+ 8329, 8353, 8363, 8369, 8377, 8387, 8389, 8419, 8423,
+ 8429, 8431, 8443, 8447, 8461, 8467, 8501, 8513, 8521,
+ 8527, 8537, 8539, 8543, 8563, 8573, 8581, 8597, 8599,
+ 8609, 8623, 8627, 8629, 8641, 8647, 8663, 8669, 8677,
+ 8681, 8689, 8693, 8699, 8707, 8713, 8719, 8731, 8737,
+ 8741, 8747, 8753, 8761, 8779, 8783, 8803, 8807, 8819,
+ 8821, 8831, 8837, 8839, 8849, 8861, 8863, 8867, 8887,
+ 8893, 8923, 8929, 8933, 8941, 8951, 8963, 8969, 8971,
+ 8999, 9001, 9007, 9011, 9013, 9029, 9041, 9043, 9049,
+ 9059, 9067, 9091, 9103, 9109, 9127, 9133, 9137, 9151,
+ 9157, 9161, 9173, 9181, 9187, 9199, 9203, 9209, 9221,
+ 9227, 9239, 9241, 9257, 9277, 9281, 9283, 9293, 9311,
+ 9319, 9323, 9337, 9341, 9343, 9349, 9371, 9377, 9391,
+ 9397, 9403, 9413, 9419, 9421, 9431, 9433, 9437, 9439,
+ 9461, 9463, 9467, 9473, 9479, 9491, 9497, 9511, 9521,
+ 9533, 9539, 9547, 9551, 9587, 9601, 9613, 9619, 9623,
+ 9629, 9631, 9643, 9649, 9661, 9677, 9679, 9689, 9697,
+ 9719, 9721, 9733, 9739, 9743, 9749, 9767, 9769, 9781,
+ 9787, 9791, 9803, 9811, 9817, 9829, 9833, 9839, 9851,
+ 9857, 9859, 9871, 9883, 9887, 9901, 9907, 9923, 9929,
+ 9931, 9941, 9949, 9967, 9973, 10007, 10009, 10037, 10039,
+ 10061, 10067, 10069, 10079, 10091, 10093, 10099, 10103, 10111,
+ 10133, 10139, 10141, 10151, 10159, 10163, 10169, 10177, 10181,
+ 10193, 10211, 10223, 10243, 10247, 10253, 10259, 10267, 10271,
+ 10273, 10289, 10301, 10303, 10313, 10321, 10331, 10333, 10337,
+ 10343, 10357, 10369, 10391, 10399, 10427, 10429, 10433, 10453,
+ 10457, 10459, 10463, 10477, 10487, 10499, 10501, 10513, 10529,
+ 10531, 10559, 10567, 10589, 10597, 10601, 10607, 10613, 10627,
+ 10631, 10639, 10651, 10657, 10663, 10667, 10687, 10691, 10709,
+ 10711, 10723, 10729, 10733, 10739, 10753, 10771, 10781, 10789,
+ 10799, 10831, 10837, 10847, 10853, 10859, 10861, 10867, 10883,
+ 10889, 10891, 10903, 10909, 10937, 10939, 10949, 10957, 10973,
+ 10979, 10987, 10993, 11003, 11027, 11047, 11057, 11059, 11069,
+ 11071, 11083, 11087, 11093, 11113, 11117, 11119, 11131, 11149,
+ 11159, 11161, 11171, 11173, 11177, 11197, 11213, 11239, 11243,
+ 11251, 11257, 11261, 11273, 11279, 11287, 11299, 11311, 11317,
+ 11321, 11329, 11351, 11353, 11369, 11383, 11393, 11399, 11411,
+ 11423, 11437, 11443, 11447, 11467, 11471, 11483, 11489, 11491,
+ 11497, 11503, 11519, 11527, 11549, 11551, 11579, 11587, 11593,
+ 11597, 11617, 11621, 11633, 11657, 11677, 11681, 11689, 11699,
+ 11701, 11717, 11719, 11731, 11743, 11777, 11779, 11783, 11789,
+ 11801, 11807, 11813, 11821, 11827, 11831, 11833, 11839, 11863,
+ 11867, 11887, 11897, 11903, 11909, 11923, 11927, 11933, 11939,
+ 11941, 11953, 11959, 11969, 11971, 11981, 11987, 12007, 12011,
+ 12037, 12041, 12043, 12049, 12071, 12073, 12097, 12101, 12107,
+ 12109, 12113, 12119, 12143, 12149, 12157, 12161, 12163, 12197,
+ 12203, 12211, 12227, 12239, 12241, 12251, 12253, 12263, 12269,
+ 12277, 12281, 12289, 12301, 12323, 12329, 12343, 12347, 12373,
+ 12377, 12379, 12391, 12401, 12409, 12413, 12421, 12433, 12437,
+ 12451, 12457, 12473, 12479, 12487, 12491, 12497, 12503, 12511,
+ 12517, 12527, 12539, 12541, 12547, 12553, 12569, 12577, 12583,
+ 12589, 12601, 12611, 12613, 12619, 12637, 12641, 12647, 12653,
+ 12659, 12671, 12689, 12697, 12703, 12713, 12721, 12739, 12743,
+ 12757, 12763, 12781, 12791, 12799, 12809, 12821, 12823, 12829,
+ 12841, 12853, 12889, 12893, 12899, 12907, 12911, 12917, 12919,
+ 12923, 12941, 12953, 12959, 12967, 12973, 12979, 12983, 13001,
+ 13003, 13007, 13009, 13033, 13037, 13043, 13049, 13063, 13093,
+ 13099, 13103, 13109, 13121, 13127, 13147, 13151, 13159, 13163,
+ 13171, 13177, 13183, 13187, 13217, 13219, 13229, 13241, 13249,
+ 13259, 13267, 13291, 13297, 13309, 13313, 13327, 13331, 13337,
+ 13339, 13367, 13381, 13397, 13399, 13411, 13417, 13421, 13441,
+ 13451, 13457, 13463, 13469, 13477, 13487, 13499, 13513, 13523,
+ 13537, 13553, 13567, 13577, 13591, 13597, 13613, 13619, 13627,
+ 13633, 13649, 13669, 13679, 13681, 13687, 13691, 13693, 13697,
+ 13709, 13711, 13721, 13723, 13729, 13751, 13757, 13759, 13763,
+ 13781, 13789, 13799, 13807, 13829, 13831, 13841, 13859, 13873,
+ 13877, 13879, 13883, 13901, 13903, 13907, 13913, 13921, 13931,
+ 13933, 13963, 13967, 13997, 13999, 14009, 14011, 14029, 14033,
+ 14051, 14057, 14071, 14081, 14083, 14087, 14107, 14143, 14149,
+ 14153, 14159, 14173, 14177, 14197, 14207, 14221, 14243, 14249,
+ 14251, 14281, 14293, 14303, 14321, 14323, 14327, 14341, 14347,
+ 14369, 14387, 14389, 14401, 14407, 14411, 14419, 14423, 14431,
+ 14437, 14447, 14449, 14461, 14479, 14489, 14503, 14519, 14533,
+ 14537, 14543, 14549, 14551, 14557, 14561, 14563, 14591, 14593,
+ 14621, 14627, 14629, 14633, 14639, 14653, 14657, 14669, 14683,
+ 14699, 14713, 14717, 14723, 14731, 14737, 14741, 14747, 14753,
+ 14759, 14767, 14771, 14779, 14783, 14797, 14813, 14821, 14827,
+ 14831, 14843, 14851, 14867, 14869, 14879, 14887, 14891, 14897,
+ 14923, 14929, 14939, 14947, 14951, 14957, 14969, 14983, 15013,
+ 15017, 15031, 15053, 15061, 15073, 15077, 15083, 15091, 15101,
+ 15107, 15121, 15131, 15137, 15139, 15149, 15161, 15173, 15187,
+ 15193, 15199, 15217, 15227, 15233, 15241, 15259, 15263, 15269,
+ 15271, 15277, 15287, 15289, 15299, 15307, 15313, 15319, 15329,
+ 15331, 15349, 15359, 15361, 15373, 15377, 15383, 15391, 15401,
+ 15413, 15427, 15439, 15443, 15451, 15461, 15467, 15473, 15493,
+ 15497, 15511, 15527, 15541, 15551, 15559, 15569, 15581, 15583,
+ 15601, 15607, 15619, 15629, 15641, 15643, 15647, 15649, 15661,
+ 15667, 15671, 15679, 15683, 15727, 15731, 15733, 15737, 15739,
+ 15749, 15761, 15767, 15773, 15787, 15791, 15797, 15803, 15809,
+ 15817, 15823, 15859, 15877, 15881, 15887, 15889, 15901, 15907,
+ 15913, 15919, 15923, 15937, 15959, 15971, 15973, 15991, 16001,
+ 16007, 16033, 16057, 16061, 16063, 16067, 16069, 16073, 16087,
+ 16091, 16097, 16103, 16111, 16127, 16139, 16141, 16183, 16187,
+ 16189, 16193, 16217, 16223, 16229, 16231, 16249, 16253, 16267,
+ 16273, 16301, 16319, 16333, 16339, 16349, 16361, 16363, 16369,
+ 16381, 16411, 16417, 16421, 16427, 16433, 16447, 16451, 16453,
+ 16477, 16481, 16487, 16493, 16519, 16529, 16547, 16553, 16561,
+ 16567, 16573, 16603, 16607, 16619, 16631, 16633, 16649, 16651,
+ 16657, 16661, 16673, 16691, 16693, 16699, 16703, 16729, 16741,
+ 16747, 16759, 16763, 16787, 16811, 16823, 16829, 16831, 16843,
+ 16871, 16879, 16883, 16889, 16901, 16903, 16921, 16927, 16931,
+ 16937, 16943, 16963, 16979, 16981, 16987, 16993, 17011, 17021,
+ 17027, 17029, 17033, 17041, 17047, 17053, 17077, 17093, 17099,
+ 17107, 17117, 17123, 17137, 17159, 17167, 17183, 17189, 17191,
+ 17203, 17207, 17209, 17231, 17239, 17257, 17291, 17293, 17299,
+ 17317, 17321, 17327, 17333, 17341, 17351, 17359, 17377, 17383,
+ 17387, 17389, 17393, 17401, 17417, 17419, 17431, 17443, 17449,
+ 17467, 17471, 17477, 17483, 17489, 17491, 17497, 17509, 17519,
+ 17539, 17551, 17569, 17573, 17579, 17581, 17597, 17599, 17609,
+ 17623, 17627, 17657, 17659, 17669, 17681, 17683, 17707, 17713,
+ 17729, 17737, 17747, 17749, 17761, 17783, 17789, 17791, 17807,
+ 17827, 17837, 17839, 17851, 17863, 17881, 17891, 17903, 17909,
+ 17911, 17921, 17923, 17929, 17939, 17957, 17959, 17971, 17977,
+ 17981, 17987, 17989, 18013, 18041, 18043, 18047, 18049, 18059,
+ 18061, 18077, 18089, 18097, 18119, 18121, 18127, 18131, 18133,
+ 18143, 18149, 18169, 18181, 18191, 18199, 18211, 18217, 18223,
+ 18229, 18233, 18251, 18253, 18257, 18269, 18287, 18289, 18301,
+ 18307, 18311, 18313, 18329, 18341, 18353, 18367, 18371, 18379,
+ 18397, 18401, 18413, 18427, 18433, 18439, 18443, 18451, 18457,
+ 18461, 18481, 18493, 18503, 18517, 18521, 18523, 18539, 18541,
+ 18553, 18583, 18587, 18593, 18617, 18637, 18661, 18671, 18679,
+ 18691, 18701, 18713, 18719, 18731, 18743, 18749, 18757, 18773,
+ 18787, 18793, 18797, 18803, 18839, 18859, 18869, 18899, 18911,
+ 18913, 18917, 18919, 18947, 18959, 18973, 18979, 19001, 19009,
+ 19013, 19031, 19037, 19051, 19069, 19073, 19079, 19081, 19087,
+ 19121, 19139, 19141, 19157, 19163, 19181, 19183, 19207, 19211,
+ 19213, 19219, 19231, 19237, 19249, 19259, 19267, 19273, 19289,
+ 19301, 19309, 19319, 19333, 19373, 19379, 19381, 19387, 19391,
+ 19403, 19417, 19421, 19423, 19427, 19429, 19433, 19441, 19447,
+ 19457, 19463, 19469, 19471, 19477, 19483, 19489, 19501, 19507,
+ 19531, 19541, 19543, 19553, 19559, 19571, 19577, 19583, 19597,
+ 19603, 19609, 19661, 19681, 19687, 19697, 19699, 19709, 19717,
+ 19727, 19739, 19751, 19753, 19759, 19763, 19777, 19793, 19801,
+ 19813, 19819, 19841, 19843, 19853, 19861, 19867, 19889, 19891,
+ 19913, 19919, 19927, 19937, 19949, 19961, 19963, 19973, 19979,
+ 19991, 19993, 19997, 20011, 20021, 20023, 20029, 20047, 20051,
+ 20063, 20071, 20089, 20101, 20107, 20113, 20117, 20123, 20129,
+ 20143, 20147, 20149, 20161, 20173, 20177, 20183, 20201, 20219,
+ 20231, 20233, 20249, 20261, 20269, 20287, 20297, 20323, 20327,
+ 20333, 20341, 20347, 20353, 20357, 20359, 20369, 20389, 20393,
+ 20399, 20407, 20411, 20431, 20441, 20443, 20477, 20479, 20483,
+ 20507, 20509, 20521, 20533, 20543, 20549, 20551, 20563, 20593,
+ 20599, 20611, 20627, 20639, 20641, 20663, 20681, 20693, 20707,
+ 20717, 20719, 20731, 20743, 20747, 20749, 20753, 20759, 20771,
+ 20773, 20789, 20807, 20809, 20849, 20857, 20873, 20879, 20887,
+ 20897, 20899, 20903, 20921, 20929, 20939, 20947, 20959, 20963,
+ 20981, 20983, 21001, 21011, 21013, 21017, 21019, 21023, 21031,
+ 21059, 21061, 21067, 21089, 21101, 21107, 21121, 21139, 21143,
+ 21149, 21157, 21163, 21169, 21179, 21187, 21191, 21193, 21211,
+ 21221, 21227, 21247, 21269, 21277, 21283, 21313, 21317, 21319,
+ 21323, 21341, 21347, 21377, 21379, 21383, 21391, 21397, 21401,
+ 21407, 21419, 21433, 21467, 21481, 21487, 21491, 21493, 21499,
+ 21503, 21517, 21521, 21523, 21529, 21557, 21559, 21563, 21569,
+ 21577, 21587, 21589, 21599, 21601, 21611, 21613, 21617, 21647,
+ 21649, 21661, 21673, 21683, 21701, 21713, 21727, 21737, 21739,
+ 21751, 21757, 21767, 21773, 21787, 21799, 21803, 21817, 21821,
+ 21839, 21841, 21851, 21859, 21863, 21871, 21881, 21893, 21911,
+ 21929, 21937, 21943, 21961, 21977, 21991, 21997, 22003, 22013,
+ 22027, 22031, 22037, 22039, 22051, 22063, 22067, 22073, 22079,
+ 22091, 22093, 22109, 22111, 22123, 22129, 22133, 22147, 22153,
+ 22157, 22159, 22171, 22189, 22193, 22229, 22247, 22259, 22271,
+ 22273, 22277, 22279, 22283, 22291, 22303, 22307, 22343, 22349,
+ 22367, 22369, 22381, 22391, 22397, 22409, 22433, 22441, 22447,
+ 22453, 22469, 22481, 22483, 22501, 22511, 22531, 22541, 22543,
+ 22549, 22567, 22571, 22573, 22613, 22619, 22621, 22637, 22639,
+ 22643, 22651, 22669, 22679, 22691, 22697, 22699, 22709, 22717,
+ 22721, 22727, 22739, 22741, 22751, 22769, 22777, 22783, 22787,
+ 22807, 22811, 22817, 22853, 22859, 22861, 22871, 22877, 22901,
+ 22907, 22921, 22937, 22943, 22961, 22963, 22973, 22993, 23003,
+ 23011, 23017, 23021, 23027, 23029, 23039, 23041, 23053, 23057,
+ 23059, 23063, 23071, 23081, 23087, 23099, 23117, 23131, 23143,
+ 23159, 23167, 23173, 23189, 23197, 23201, 23203, 23209, 23227,
+ 23251, 23269, 23279, 23291, 23293, 23297, 23311, 23321, 23327,
+ 23333, 23339, 23357, 23369, 23371, 23399, 23417, 23431, 23447,
+ 23459, 23473, 23497, 23509, 23531, 23537, 23539, 23549, 23557,
+ 23561, 23563, 23567, 23581, 23593, 23599, 23603, 23609, 23623,
+ 23627, 23629, 23633, 23663, 23669, 23671, 23677, 23687, 23689,
+ 23719, 23741, 23743, 23747, 23753, 23761, 23767, 23773, 23789,
+ 23801, 23813, 23819, 23827, 23831, 23833, 23857, 23869, 23873,
+ 23879, 23887, 23893, 23899, 23909, 23911, 23917, 23929, 23957,
+ 23971, 23977, 23981, 23993, 24001, 24007, 24019, 24023, 24029,
+ 24043, 24049, 24061, 24071, 24077, 24083, 24091, 24097, 24103,
+ 24107, 24109, 24113, 24121, 24133, 24137, 24151, 24169, 24179,
+ 24181, 24197, 24203, 24223, 24229, 24239, 24247, 24251, 24281,
+ 24317, 24329, 24337, 24359, 24371, 24373, 24379, 24391, 24407,
+ 24413, 24419, 24421, 24439, 24443, 24469, 24473, 24481, 24499,
+ 24509, 24517, 24527, 24533, 24547, 24551, 24571, 24593, 24611,
+ 24623, 24631, 24659, 24671, 24677, 24683, 24691, 24697, 24709,
+ 24733, 24749, 24763, 24767, 24781, 24793, 24799, 24809, 24821,
+ 24841, 24847, 24851, 24859, 24877, 24889, 24907, 24917, 24919,
+ 24923, 24943, 24953, 24967, 24971, 24977, 24979, 24989, 25013,
+ 25031, 25033, 25037, 25057, 25073, 25087, 25097, 25111, 25117,
+ 25121, 25127, 25147, 25153, 25163, 25169, 25171, 25183, 25189,
+ 25219, 25229, 25237, 25243, 25247, 25253, 25261, 25301, 25303,
+ 25307, 25309, 25321, 25339, 25343, 25349, 25357, 25367, 25373,
+ 25391, 25409, 25411, 25423, 25439, 25447, 25453, 25457, 25463,
+ 25469, 25471, 25523, 25537, 25541, 25561, 25577, 25579, 25583,
+ 25589, 25601, 25603, 25609, 25621, 25633, 25639, 25643, 25657,
+ 25667, 25673, 25679, 25693, 25703, 25717, 25733, 25741, 25747,
+ 25759, 25763, 25771, 25793, 25799, 25801, 25819, 25841, 25847,
+ 25849, 25867, 25873, 25889, 25903, 25913, 25919, 25931, 25933,
+ 25939, 25943, 25951, 25969, 25981, 25997, 25999, 26003, 26017,
+ 26021, 26029, 26041, 26053, 26083, 26099, 26107, 26111, 26113,
+ 26119, 26141, 26153, 26161, 26171, 26177, 26183, 26189, 26203,
+ 26209, 26227, 26237, 26249, 26251, 26261, 26263, 26267, 26293,
+ 26297, 26309, 26317, 26321, 26339, 26347, 26357, 26371, 26387,
+ 26393, 26399, 26407, 26417, 26423, 26431, 26437, 26449, 26459,
+ 26479, 26489, 26497, 26501, 26513, 26539, 26557, 26561, 26573,
+ 26591, 26597, 26627, 26633, 26641, 26647, 26669, 26681, 26683,
+ 26687, 26693, 26699, 26701, 26711, 26713, 26717, 26723, 26729,
+ 26731, 26737, 26759, 26777, 26783, 26801, 26813, 26821, 26833,
+ 26839, 26849, 26861, 26863, 26879, 26881, 26891, 26893, 26903,
+ 26921, 26927, 26947, 26951, 26953, 26959, 26981, 26987, 26993,
+ 27011, 27017, 27031, 27043, 27059, 27061, 27067, 27073, 27077,
+ 27091, 27103, 27107, 27109, 27127, 27143, 27179, 27191, 27197,
+ 27211, 27239, 27241, 27253, 27259, 27271, 27277, 27281, 27283,
+ 27299, 27329, 27337, 27361, 27367, 27397, 27407, 27409, 27427,
+ 27431, 27437, 27449, 27457, 27479, 27481, 27487, 27509, 27527,
+ 27529, 27539, 27541, 27551, 27581, 27583, 27611, 27617, 27631,
+ 27647, 27653, 27673, 27689, 27691, 27697, 27701, 27733, 27737,
+ 27739, 27743, 27749, 27751, 27763, 27767, 27773, 27779, 27791,
+ 27793, 27799, 27803, 27809, 27817, 27823, 27827, 27847, 27851,
+ 27883, 27893, 27901, 27917, 27919, 27941, 27943, 27947, 27953,
+ 27961, 27967, 27983, 27997, 28001, 28019, 28027, 28031, 28051,
+ 28057, 28069, 28081, 28087, 28097, 28099, 28109, 28111, 28123,
+ 28151, 28163, 28181, 28183, 28201, 28211, 28219, 28229, 28277,
+ 28279, 28283, 28289, 28297, 28307, 28309, 28319, 28349, 28351,
+ 28387, 28393, 28403, 28409, 28411, 28429, 28433, 28439, 28447,
+ 28463, 28477, 28493, 28499, 28513, 28517, 28537, 28541, 28547,
+ 28549, 28559, 28571, 28573, 28579, 28591, 28597, 28603, 28607,
+ 28619, 28621, 28627, 28631, 28643, 28649, 28657, 28661, 28663,
+ 28669, 28687, 28697, 28703, 28711, 28723, 28729, 28751, 28753,
+ 28759, 28771, 28789, 28793, 28807, 28813, 28817, 28837, 28843,
+ 28859, 28867, 28871, 28879, 28901, 28909, 28921, 28927, 28933,
+ 28949, 28961, 28979, 29009, 29017, 29021, 29023, 29027, 29033,
+ 29059, 29063, 29077, 29101, 29123, 29129, 29131, 29137, 29147,
+ 29153, 29167, 29173, 29179, 29191, 29201, 29207, 29209, 29221,
+ 29231, 29243, 29251, 29269, 29287, 29297, 29303, 29311, 29327,
+ 29333, 29339, 29347, 29363, 29383, 29387, 29389, 29399, 29401,
+ 29411, 29423, 29429, 29437, 29443, 29453, 29473, 29483, 29501,
+ 29527, 29531, 29537, 29567, 29569, 29573, 29581, 29587, 29599,
+ 29611, 29629, 29633, 29641, 29663, 29669, 29671, 29683, 29717,
+ 29723, 29741, 29753, 29759, 29761, 29789, 29803, 29819, 29833,
+ 29837, 29851, 29863, 29867, 29873, 29879, 29881, 29917, 29921,
+ 29927, 29947, 29959, 29983, 29989, 30011, 30013, 30029, 30047,
+ 30059, 30071, 30089, 30091, 30097, 30103, 30109, 30113, 30119,
+ 30133, 30137, 30139, 30161, 30169, 30181, 30187, 30197, 30203,
+ 30211, 30223, 30241, 30253, 30259, 30269, 30271, 30293, 30307,
+ 30313, 30319, 30323, 30341, 30347, 30367, 30389, 30391, 30403,
+ 30427, 30431, 30449, 30467, 30469, 30491, 30493, 30497, 30509,
+ 30517, 30529, 30539, 30553, 30557, 30559, 30577, 30593, 30631,
+ 30637, 30643, 30649, 30661, 30671, 30677, 30689, 30697, 30703,
+ 30707, 30713, 30727, 30757, 30763, 30773, 30781, 30803, 30809,
+ 30817, 30829, 30839, 30841, 30851, 30853, 30859, 30869, 30871,
+ 30881, 30893, 30911, 30931, 30937, 30941, 30949, 30971, 30977,
+ 30983, 31013, 31019, 31033, 31039, 31051, 31063, 31069, 31079,
+ 31081, 31091, 31121, 31123, 31139, 31147, 31151, 31153, 31159,
+ 31177, 31181, 31183, 31189, 31193, 31219, 31223, 31231, 31237,
+ 31247, 31249, 31253, 31259, 31267, 31271, 31277, 31307, 31319,
+ 31321, 31327, 31333, 31337, 31357, 31379, 31387, 31391, 31393,
+ 31397, 31469, 31477, 31481, 31489, 31511, 31513, 31517, 31531,
+ 31541, 31543, 31547, 31567, 31573, 31583, 31601, 31607, 31627,
+ 31643, 31649, 31657, 31663, 31667, 31687, 31699, 31721, 31723,
+ 31727, 31729, 31741, 31751, 31769, 31771, 31793, 31799, 31817,
+ 31847, 31849, 31859, 31873, 31883, 31891, 31907, 31957, 31963,
+ 31973, 31981, 31991, 32003, 32009, 32027, 32029, 32051, 32057,
+ 32059, 32063, 32069, 32077, 32083, 32089, 32099, 32117, 32119,
+ 32141, 32143, 32159, 32173, 32183, 32189, 32191, 32203, 32213,
+ 32233, 32237, 32251, 32257, 32261, 32297, 32299, 32303, 32309,
+ 32321, 32323, 32327, 32341, 32353, 32359, 32363, 32369, 32371,
+ 32377, 32381, 32401, 32411, 32413, 32423, 32429, 32441, 32443,
+ 32467, 32479, 32491, 32497, 32503, 32507, 32531, 32533, 32537,
+ 32561, 32563, 32569, 32573, 32579, 32587, 32603, 32609, 32611,
+ 32621, 32633, 32647, 32653, 32687, 32693, 32707, 32713, 32717,
+ 32719, 32749, 32771, 32779, 32783, 32789, 32797, 32801, 32803,
+ 32831, 32833, 32839, 32843, 32869, 32887, 32909, 32911, 32917,
+ 32933, 32939, 32941, 32957, 32969, 32971, 32983, 32987, 32993,
+ 32999, 33013, 33023, 33029, 33037, 33049, 33053, 33071, 33073,
+ 33083, 33091, 33107, 33113, 33119, 33149, 33151, 33161, 33179,
+ 33181, 33191, 33199, 33203, 33211, 33223, 33247, 33287, 33289,
+ 33301, 33311, 33317, 33329, 33331, 33343, 33347, 33349, 33353,
+ 33359, 33377, 33391, 33403, 33409, 33413, 33427, 33457, 33461,
+ 33469, 33479, 33487, 33493, 33503, 33521, 33529, 33533, 33547,
+ 33563, 33569, 33577, 33581, 33587, 33589, 33599, 33601, 33613,
+ 33617, 33619, 33623, 33629, 33637, 33641, 33647, 33679, 33703,
+ 33713, 33721, 33739, 33749, 33751, 33757, 33767, 33769, 33773,
+ 33791, 33797, 33809, 33811, 33827, 33829, 33851, 33857, 33863,
+ 33871, 33889, 33893, 33911, 33923, 33931, 33937, 33941, 33961,
+ 33967, 33997, 34019, 34031, 34033, 34039, 34057, 34061, 34123,
+ 34127, 34129, 34141, 34147, 34157, 34159, 34171, 34183, 34211,
+ 34213, 34217, 34231, 34253, 34259, 34261, 34267, 34273, 34283,
+ 34297, 34301, 34303, 34313, 34319, 34327, 34337, 34351, 34361,
+ 34367, 34369, 34381, 34403, 34421, 34429, 34439, 34457, 34469,
+ 34471, 34483, 34487, 34499, 34501, 34511, 34513, 34519, 34537,
+ 34543, 34549, 34583, 34589, 34591, 34603, 34607, 34613, 34631,
+ 34649, 34651, 34667, 34673, 34679, 34687, 34693, 34703, 34721,
+ 34729, 34739, 34747, 34757, 34759, 34763, 34781, 34807, 34819,
+ 34841, 34843, 34847, 34849, 34871, 34877, 34883, 34897, 34913,
+ 34919, 34939, 34949, 34961, 34963, 34981, 35023, 35027, 35051,
+ 35053, 35059, 35069, 35081, 35083, 35089, 35099, 35107, 35111,
+ 35117, 35129, 35141, 35149, 35153, 35159, 35171, 35201, 35221,
+ 35227, 35251, 35257, 35267, 35279, 35281, 35291, 35311, 35317,
+ 35323, 35327, 35339, 35353, 35363, 35381, 35393, 35401, 35407,
+ 35419, 35423, 35437, 35447, 35449, 35461, 35491, 35507, 35509,
+ 35521, 35527, 35531, 35533, 35537, 35543, 35569, 35573, 35591,
+ 35593, 35597, 35603, 35617, 35671, 35677, 35729, 35731, 35747,
+ 35753, 35759, 35771, 35797, 35801, 35803, 35809, 35831, 35837,
+ 35839, 35851, 35863, 35869, 35879, 35897, 35899, 35911, 35923,
+ 35933, 35951, 35963, 35969, 35977, 35983, 35993, 35999, 36007,
+ 36011, 36013, 36017, 36037, 36061, 36067, 36073, 36083, 36097,
+ 36107, 36109, 36131, 36137, 36151, 36161, 36187, 36191, 36209,
+ 36217, 36229, 36241, 36251, 36263, 36269, 36277, 36293, 36299,
+ 36307, 36313, 36319, 36341, 36343, 36353, 36373, 36383, 36389,
+ 36433, 36451, 36457, 36467, 36469, 36473, 36479, 36493, 36497,
+ 36523, 36527, 36529, 36541, 36551, 36559, 36563, 36571, 36583,
+ 36587, 36599, 36607, 36629, 36637, 36643, 36653, 36671, 36677,
+ 36683, 36691, 36697, 36709, 36713, 36721, 36739, 36749, 36761,
+ 36767, 36779, 36781, 36787, 36791, 36793, 36809, 36821, 36833,
+ 36847, 36857, 36871, 36877, 36887, 36899, 36901, 36913, 36919,
+ 36923, 36929, 36931, 36943, 36947, 36973, 36979, 36997, 37003,
+ 37013, 37019, 37021, 37039, 37049, 37057, 37061, 37087, 37097,
+ 37117, 37123, 37139, 37159, 37171, 37181, 37189, 37199, 37201,
+ 37217, 37223, 37243, 37253, 37273, 37277, 37307, 37309, 37313,
+ 37321, 37337, 37339, 37357, 37361, 37363, 37369, 37379, 37397,
+ 37409, 37423, 37441, 37447, 37463, 37483, 37489, 37493, 37501,
+ 37507, 37511, 37517, 37529, 37537, 37547, 37549, 37561, 37567,
+ 37571, 37573, 37579, 37589, 37591, 37607, 37619, 37633, 37643,
+ 37649, 37657, 37663, 37691, 37693, 37699, 37717, 37747, 37781,
+ 37783, 37799, 37811, 37813, 37831, 37847, 37853, 37861, 37871,
+ 37879, 37889, 37897, 37907, 37951, 37957, 37963, 37967, 37987,
+ 37991, 37993, 37997, 38011, 38039, 38047, 38053, 38069, 38083,
+ 38113, 38119, 38149, 38153, 38167, 38177, 38183, 38189, 38197,
+ 38201, 38219, 38231, 38237, 38239, 38261, 38273, 38281, 38287,
+ 38299, 38303, 38317, 38321, 38327, 38329, 38333, 38351, 38371,
+ 38377, 38393, 38431, 38447, 38449, 38453, 38459, 38461, 38501,
+ 38543, 38557, 38561, 38567, 38569, 38593, 38603, 38609, 38611,
+ 38629, 38639, 38651, 38653, 38669, 38671, 38677, 38693, 38699,
+ 38707, 38711, 38713, 38723, 38729, 38737, 38747, 38749, 38767,
+ 38783, 38791, 38803, 38821, 38833, 38839, 38851, 38861, 38867,
+ 38873, 38891, 38903, 38917, 38921, 38923, 38933, 38953, 38959,
+ 38971, 38977, 38993, 39019, 39023, 39041, 39043, 39047, 39079,
+ 39089, 39097, 39103, 39107, 39113, 39119, 39133, 39139, 39157,
+ 39161, 39163, 39181, 39191, 39199, 39209, 39217, 39227, 39229,
+ 39233, 39239, 39241, 39251, 39293, 39301, 39313, 39317, 39323,
+ 39341, 39343, 39359, 39367, 39371, 39373, 39383, 39397, 39409,
+ 39419, 39439, 39443, 39451, 39461, 39499, 39503, 39509, 39511,
+ 39521, 39541, 39551, 39563, 39569, 39581, 39607, 39619, 39623,
+ 39631, 39659, 39667, 39671, 39679, 39703, 39709, 39719, 39727,
+ 39733, 39749, 39761, 39769, 39779, 39791, 39799, 39821, 39827,
+ 39829, 39839, 39841, 39847, 39857, 39863, 39869, 39877, 39883,
+ 39887, 39901, 39929, 39937, 39953, 39971, 39979, 39983, 39989,
+ 40009, 40013, 40031, 40037, 40039, 40063, 40087, 40093, 40099,
+ 40111, 40123, 40127, 40129, 40151, 40153, 40163, 40169, 40177,
+ 40189, 40193, 40213, 40231, 40237, 40241, 40253, 40277, 40283,
+ 40289, 40343, 40351, 40357, 40361, 40387, 40423, 40427, 40429,
+ 40433, 40459, 40471, 40483, 40487, 40493, 40499, 40507, 40519,
+ 40529, 40531, 40543, 40559, 40577, 40583, 40591, 40597, 40609,
+ 40627, 40637, 40639, 40693, 40697, 40699, 40709, 40739, 40751,
+ 40759, 40763, 40771, 40787, 40801, 40813, 40819, 40823, 40829,
+ 40841, 40847, 40849, 40853, 40867, 40879, 40883, 40897, 40903,
+ 40927, 40933, 40939, 40949, 40961, 40973, 40993, 41011, 41017,
+ 41023, 41039, 41047, 41051, 41057, 41077, 41081, 41113, 41117,
+ 41131, 41141, 41143, 41149, 41161, 41177, 41179, 41183, 41189,
+ 41201, 41203, 41213, 41221, 41227, 41231, 41233, 41243, 41257,
+ 41263, 41269, 41281, 41299, 41333, 41341, 41351, 41357, 41381,
+ 41387, 41389, 41399, 41411, 41413, 41443, 41453, 41467, 41479,
+ 41491, 41507, 41513, 41519, 41521, 41539, 41543, 41549, 41579,
+ 41593, 41597, 41603, 41609, 41611, 41617, 41621, 41627, 41641,
+ 41647, 41651, 41659, 41669, 41681, 41687, 41719, 41729, 41737,
+ 41759, 41761, 41771, 41777, 41801, 41809, 41813, 41843, 41849,
+ 41851, 41863, 41879, 41887, 41893, 41897, 41903, 41911, 41927,
+ 41941, 41947, 41953, 41957, 41959, 41969, 41981, 41983, 41999,
+ 42013, 42017, 42019, 42023, 42043, 42061, 42071, 42073, 42083,
+ 42089, 42101, 42131, 42139, 42157, 42169, 42179, 42181, 42187,
+ 42193, 42197, 42209, 42221, 42223, 42227, 42239, 42257, 42281,
+ 42283, 42293, 42299, 42307, 42323, 42331, 42337, 42349, 42359,
+ 42373, 42379, 42391, 42397, 42403, 42407, 42409, 42433, 42437,
+ 42443, 42451, 42457, 42461, 42463, 42467, 42473, 42487, 42491,
+ 42499, 42509, 42533, 42557, 42569, 42571, 42577, 42589, 42611,
+ 42641, 42643, 42649, 42667, 42677, 42683, 42689, 42697, 42701,
+ 42703, 42709, 42719, 42727, 42737, 42743, 42751, 42767, 42773,
+ 42787, 42793, 42797, 42821, 42829, 42839, 42841, 42853, 42859,
+ 42863, 42899, 42901, 42923, 42929, 42937, 42943, 42953, 42961,
+ 42967, 42979, 42989, 43003, 43013, 43019, 43037, 43049, 43051,
+ 43063, 43067, 43093, 43103, 43117, 43133, 43151, 43159, 43177,
+ 43189, 43201, 43207, 43223, 43237, 43261, 43271, 43283, 43291,
+ 43313, 43319, 43321, 43331, 43391, 43397, 43399, 43403, 43411,
+ 43427, 43441, 43451, 43457, 43481, 43487, 43499, 43517, 43541,
+ 43543, 43573, 43577, 43579, 43591, 43597, 43607, 43609, 43613,
+ 43627, 43633, 43649, 43651, 43661, 43669, 43691, 43711, 43717,
+ 43721, 43753, 43759, 43777, 43781, 43783, 43787, 43789, 43793,
+ 43801, 43853, 43867, 43889, 43891, 43913, 43933, 43943, 43951,
+ 43961, 43963, 43969, 43973, 43987, 43991, 43997, 44017, 44021,
+ 44027, 44029, 44041, 44053, 44059, 44071, 44087, 44089, 44101,
+ 44111, 44119, 44123, 44129, 44131, 44159, 44171, 44179, 44189,
+ 44201, 44203, 44207, 44221, 44249, 44257, 44263, 44267, 44269,
+ 44273, 44279, 44281, 44293, 44351, 44357, 44371, 44381, 44383,
+ 44389, 44417, 44449, 44453, 44483, 44491, 44497, 44501, 44507,
+ 44519, 44531, 44533, 44537, 44543, 44549, 44563, 44579, 44587,
+ 44617, 44621, 44623, 44633, 44641, 44647, 44651, 44657, 44683,
+ 44687, 44699, 44701, 44711, 44729, 44741, 44753, 44771, 44773,
+ 44777, 44789, 44797, 44809, 44819, 44839, 44843, 44851, 44867,
+ 44879, 44887, 44893, 44909, 44917, 44927, 44939, 44953, 44959,
+ 44963, 44971, 44983, 44987, 45007, 45013, 45053, 45061, 45077,
+ 45083, 45119, 45121, 45127, 45131, 45137, 45139, 45161, 45179,
+ 45181, 45191, 45197, 45233, 45247, 45259, 45263, 45281, 45289,
+ 45293, 45307, 45317, 45319, 45329, 45337, 45341, 45343, 45361,
+ 45377, 45389, 45403, 45413, 45427, 45433, 45439, 45481, 45491,
+ 45497, 45503, 45523, 45533, 45541, 45553, 45557, 45569, 45587,
+ 45589, 45599, 45613, 45631, 45641, 45659, 45667, 45673, 45677,
+ 45691, 45697, 45707, 45737, 45751, 45757, 45763, 45767, 45779,
+ 45817, 45821, 45823, 45827, 45833, 45841, 45853, 45863, 45869,
+ 45887, 45893, 45943, 45949, 45953, 45959, 45971, 45979, 45989,
+ 46021, 46027, 46049, 46051, 46061, 46073, 46091, 46093, 46099,
+ 46103, 46133, 46141, 46147, 46153, 46171, 46181, 46183, 46187,
+ 46199, 46219, 46229, 46237, 46261, 46271, 46273, 46279, 46301,
+ 46307, 46309, 46327, 46337, 46349, 46351, 46381, 46399, 46411,
+ 46439, 46441, 46447, 46451, 46457, 46471, 46477, 46489, 46499,
+ 46507, 46511, 46523, 46549, 46559, 46567, 46573, 46589, 46591,
+ 46601, 46619, 46633, 46639, 46643, 46649, 46663, 46679, 46681,
+ 46687, 46691, 46703, 46723, 46727, 46747, 46751, 46757, 46769,
+ 46771, 46807, 46811, 46817, 46819, 46829, 46831, 46853, 46861,
+ 46867, 46877, 46889, 46901, 46919, 46933, 46957, 46993, 46997,
+ 47017, 47041, 47051, 47057, 47059, 47087, 47093, 47111, 47119,
+ 47123, 47129, 47137, 47143, 47147, 47149, 47161, 47189, 47207,
+ 47221, 47237, 47251, 47269, 47279, 47287, 47293, 47297, 47303,
+ 47309, 47317, 47339, 47351, 47353, 47363, 47381, 47387, 47389,
+ 47407, 47417, 47419, 47431, 47441, 47459, 47491, 47497, 47501,
+ 47507, 47513, 47521, 47527, 47533, 47543, 47563, 47569, 47581,
+ 47591, 47599, 47609, 47623, 47629, 47639, 47653, 47657, 47659,
+ 47681, 47699, 47701, 47711, 47713, 47717, 47737, 47741, 47743,
+ 47777, 47779, 47791, 47797, 47807, 47809, 47819, 47837, 47843,
+ 47857, 47869, 47881, 47903, 47911, 47917, 47933, 47939, 47947,
+ 47951, 47963, 47969, 47977, 47981, 48017, 48023, 48029, 48049,
+ 48073, 48079, 48091, 48109, 48119, 48121, 48131, 48157, 48163,
+ 48179, 48187, 48193, 48197, 48221, 48239, 48247, 48259, 48271,
+ 48281, 48299, 48311, 48313, 48337, 48341, 48353, 48371, 48383,
+ 48397, 48407, 48409, 48413, 48437, 48449, 48463, 48473, 48479,
+ 48481, 48487, 48491, 48497, 48523, 48527, 48533, 48539, 48541,
+ 48563, 48571, 48589, 48593, 48611, 48619, 48623, 48647, 48649,
+ 48661, 48673, 48677, 48679, 48731, 48733, 48751, 48757, 48761,
+ 48767, 48779, 48781, 48787, 48799, 48809, 48817, 48821, 48823,
+ 48847, 48857, 48859, 48869, 48871, 48883, 48889, 48907, 48947,
+ 48953, 48973, 48989, 48991, 49003, 49009, 49019, 49031, 49033,
+ 49037, 49043, 49057, 49069, 49081, 49103, 49109, 49117, 49121,
+ 49123, 49139, 49157, 49169, 49171, 49177, 49193, 49199, 49201,
+ 49207, 49211, 49223, 49253, 49261, 49277, 49279, 49297, 49307,
+ 49331, 49333, 49339, 49363, 49367, 49369, 49391, 49393, 49409,
+ 49411, 49417, 49429, 49433, 49451, 49459, 49463, 49477, 49481,
+ 49499, 49523, 49529, 49531, 49537, 49547, 49549, 49559, 49597,
+ 49603, 49613, 49627, 49633, 49639, 49663, 49667, 49669, 49681,
+ 49697, 49711, 49727, 49739, 49741, 49747, 49757, 49783, 49787,
+ 49789, 49801, 49807, 49811, 49823, 49831, 49843, 49853, 49871,
+ 49877, 49891, 49919, 49921, 49927, 49937, 49939, 49943, 49957,
+ 49991, 49993, 49999, 50021, 50023, 50033, 50047, 50051, 50053,
+ 50069, 50077, 50087, 50093, 50101, 50111, 50119, 50123, 50129,
+ 50131, 50147, 50153, 50159, 50177, 50207, 50221, 50227, 50231,
+ 50261, 50263, 50273, 50287, 50291, 50311, 50321, 50329, 50333,
+ 50341, 50359, 50363, 50377, 50383, 50387, 50411, 50417, 50423,
+ 50441, 50459, 50461, 50497, 50503, 50513, 50527, 50539, 50543,
+ 50549, 50551, 50581, 50587, 50591, 50593, 50599, 50627, 50647,
+ 50651, 50671, 50683, 50707, 50723, 50741, 50753, 50767, 50773,
+ 50777, 50789, 50821, 50833, 50839, 50849, 50857, 50867, 50873,
+ 50891, 50893, 50909, 50923, 50929, 50951, 50957, 50969, 50971,
+ 50989, 50993, 51001, 51031, 51043, 51047, 51059, 51061, 51071,
+ 51109, 51131, 51133, 51137, 51151, 51157, 51169, 51193, 51197,
+ 51199, 51203, 51217, 51229, 51239, 51241, 51257, 51263, 51283,
+ 51287, 51307, 51329, 51341, 51343, 51347, 51349, 51361, 51383,
+ 51407, 51413, 51419, 51421, 51427, 51431, 51437, 51439, 51449,
+ 51461, 51473, 51479, 51481, 51487, 51503, 51511, 51517, 51521,
+ 51539, 51551, 51563, 51577, 51581, 51593, 51599, 51607, 51613,
+ 51631, 51637, 51647, 51659, 51673, 51679, 51683, 51691, 51713,
+ 51719, 51721, 51749, 51767, 51769, 51787, 51797, 51803, 51817,
+ 51827, 51829, 51839, 51853, 51859, 51869, 51871, 51893, 51899,
+ 51907, 51913, 51929, 51941, 51949, 51971, 51973, 51977, 51991,
+ 52009, 52021, 52027, 52051, 52057, 52067, 52069, 52081, 52103,
+ 52121, 52127, 52147, 52153, 52163, 52177, 52181, 52183, 52189,
+ 52201, 52223, 52237, 52249, 52253, 52259, 52267, 52289, 52291,
+ 52301, 52313, 52321, 52361, 52363, 52369, 52379, 52387, 52391,
+ 52433, 52453, 52457, 52489, 52501, 52511, 52517, 52529, 52541,
+ 52543, 52553, 52561, 52567, 52571, 52579, 52583, 52609, 52627,
+ 52631, 52639, 52667, 52673, 52691, 52697, 52709, 52711, 52721,
+ 52727, 52733, 52747, 52757, 52769, 52783, 52807, 52813, 52817,
+ 52837, 52859, 52861, 52879, 52883, 52889, 52901, 52903, 52919,
+ 52937, 52951, 52957, 52963, 52967, 52973, 52981, 52999, 53003,
+ 53017, 53047, 53051, 53069, 53077, 53087, 53089, 53093, 53101,
+ 53113, 53117, 53129, 53147, 53149, 53161, 53171, 53173, 53189,
+ 53197, 53201, 53231, 53233, 53239, 53267, 53269, 53279, 53281,
+ 53299, 53309, 53323, 53327, 53353, 53359, 53377, 53381, 53401,
+ 53407, 53411, 53419, 53437, 53441, 53453, 53479, 53503, 53507,
+ 53527, 53549, 53551, 53569, 53591, 53593, 53597, 53609, 53611,
+ 53617, 53623, 53629, 53633, 53639, 53653, 53657, 53681, 53693,
+ 53699, 53717, 53719, 53731, 53759, 53773, 53777, 53783, 53791,
+ 53813, 53819, 53831, 53849, 53857, 53861, 53881, 53887, 53891,
+ 53897, 53899, 53917, 53923, 53927, 53939, 53951, 53959, 53987,
+ 53993, 54001, 54011, 54013, 54037, 54049, 54059, 54083, 54091,
+ 54101, 54121, 54133, 54139, 54151, 54163, 54167, 54181, 54193,
+ 54217, 54251, 54269, 54277, 54287, 54293, 54311, 54319, 54323,
+ 54331, 54347, 54361, 54367, 54371, 54377, 54401, 54403, 54409,
+ 54413, 54419, 54421, 54437, 54443, 54449, 54469, 54493, 54497,
+ 54499, 54503, 54517, 54521, 54539, 54541, 54547, 54559, 54563,
+ 54577, 54581, 54583, 54601, 54617, 54623, 54629, 54631, 54647,
+ 54667, 54673, 54679, 54709, 54713, 54721, 54727, 54751, 54767,
+ 54773, 54779, 54787, 54799, 54829, 54833, 54851, 54869, 54877,
+ 54881, 54907, 54917, 54919, 54941, 54949, 54959, 54973, 54979,
+ 54983, 55001, 55009, 55021, 55049, 55051, 55057, 55061, 55073,
+ 55079, 55103, 55109, 55117, 55127, 55147, 55163, 55171, 55201,
+ 55207, 55213, 55217, 55219, 55229, 55243, 55249, 55259, 55291,
+ 55313, 55331, 55333, 55337, 55339, 55343, 55351, 55373, 55381,
+ 55399, 55411, 55439, 55441, 55457, 55469, 55487, 55501, 55511,
+ 55529, 55541, 55547, 55579, 55589, 55603, 55609, 55619, 55621,
+ 55631, 55633, 55639, 55661, 55663, 55667, 55673, 55681, 55691,
+ 55697, 55711, 55717, 55721, 55733, 55763, 55787, 55793, 55799,
+ 55807, 55813, 55817, 55819, 55823, 55829, 55837, 55843, 55849,
+ 55871, 55889, 55897, 55901, 55903, 55921, 55927, 55931, 55933,
+ 55949, 55967, 55987, 55997, 56003, 56009, 56039, 56041, 56053,
+ 56081, 56087, 56093, 56099, 56101, 56113, 56123, 56131, 56149,
+ 56167, 56171, 56179, 56197, 56207, 56209, 56237, 56239, 56249,
+ 56263, 56267, 56269, 56299, 56311, 56333, 56359, 56369, 56377,
+ 56383, 56393, 56401, 56417, 56431, 56437, 56443, 56453, 56467,
+ 56473, 56477, 56479, 56489, 56501, 56503, 56509, 56519, 56527,
+ 56531, 56533, 56543, 56569, 56591, 56597, 56599, 56611, 56629,
+ 56633, 56659, 56663, 56671, 56681, 56687, 56701, 56711, 56713,
+ 56731, 56737, 56747, 56767, 56773, 56779, 56783, 56807, 56809,
+ 56813, 56821, 56827, 56843, 56857, 56873, 56891, 56893, 56897,
+ 56909, 56911, 56921, 56923, 56929, 56941, 56951, 56957, 56963,
+ 56983, 56989, 56993, 56999, 57037, 57041, 57047, 57059, 57073,
+ 57077, 57089, 57097, 57107, 57119, 57131, 57139, 57143, 57149,
+ 57163, 57173, 57179, 57191, 57193, 57203, 57221, 57223, 57241,
+ 57251, 57259, 57269, 57271, 57283, 57287, 57301, 57329, 57331,
+ 57347, 57349, 57367, 57373, 57383, 57389, 57397, 57413, 57427,
+ 57457, 57467, 57487, 57493, 57503, 57527, 57529, 57557, 57559,
+ 57571, 57587, 57593, 57601, 57637, 57641, 57649, 57653, 57667,
+ 57679, 57689, 57697, 57709, 57713, 57719, 57727, 57731, 57737,
+ 57751, 57773, 57781, 57787, 57791, 57793, 57803, 57809, 57829,
+ 57839, 57847, 57853, 57859, 57881, 57899, 57901, 57917, 57923,
+ 57943, 57947, 57973, 57977, 57991, 58013, 58027, 58031, 58043,
+ 58049, 58057, 58061, 58067, 58073, 58099, 58109, 58111, 58129,
+ 58147, 58151, 58153, 58169, 58171, 58189, 58193, 58199, 58207,
+ 58211, 58217, 58229, 58231, 58237, 58243, 58271, 58309, 58313,
+ 58321, 58337, 58363, 58367, 58369, 58379, 58391, 58393, 58403,
+ 58411, 58417, 58427, 58439, 58441, 58451, 58453, 58477, 58481,
+ 58511, 58537, 58543, 58549, 58567, 58573, 58579, 58601, 58603,
+ 58613, 58631, 58657, 58661, 58679, 58687, 58693, 58699, 58711,
+ 58727, 58733, 58741, 58757, 58763, 58771, 58787, 58789, 58831,
+ 58889, 58897, 58901, 58907, 58909, 58913, 58921, 58937, 58943,
+ 58963, 58967, 58979, 58991, 58997, 59009, 59011, 59021, 59023,
+ 59029, 59051, 59053, 59063, 59069, 59077, 59083, 59093, 59107,
+ 59113, 59119, 59123, 59141, 59149, 59159, 59167, 59183, 59197,
+ 59207, 59209, 59219, 59221, 59233, 59239, 59243, 59263, 59273,
+ 59281, 59333, 59341, 59351, 59357, 59359, 59369, 59377, 59387,
+ 59393, 59399, 59407, 59417, 59419, 59441, 59443, 59447, 59453,
+ 59467, 59471, 59473, 59497, 59509, 59513, 59539, 59557, 59561,
+ 59567, 59581, 59611, 59617, 59621, 59627, 59629, 59651, 59659,
+ 59663, 59669, 59671, 59693, 59699, 59707, 59723, 59729, 59743,
+ 59747, 59753, 59771, 59779, 59791, 59797, 59809, 59833, 59863,
+ 59879, 59887, 59921, 59929, 59951, 59957, 59971, 59981, 59999,
+ 60013, 60017, 60029, 60037, 60041, 60077, 60083, 60089, 60091,
+ 60101, 60103, 60107, 60127, 60133, 60139, 60149, 60161, 60167,
+ 60169, 60209, 60217, 60223, 60251, 60257, 60259, 60271, 60289,
+ 60293, 60317, 60331, 60337, 60343, 60353, 60373, 60383, 60397,
+ 60413, 60427, 60443, 60449, 60457, 60493, 60497, 60509, 60521,
+ 60527, 60539, 60589, 60601, 60607, 60611, 60617, 60623, 60631,
+ 60637, 60647, 60649, 60659, 60661, 60679, 60689, 60703, 60719,
+ 60727, 60733, 60737, 60757, 60761, 60763, 60773, 60779, 60793,
+ 60811, 60821, 60859, 60869, 60887, 60889, 60899, 60901, 60913,
+ 60917, 60919, 60923, 60937, 60943, 60953, 60961, 61001, 61007,
+ 61027, 61031, 61043, 61051, 61057, 61091, 61099, 61121, 61129,
+ 61141, 61151, 61153, 61169, 61211, 61223, 61231, 61253, 61261,
+ 61283, 61291, 61297, 61331, 61333, 61339, 61343, 61357, 61363,
+ 61379, 61381, 61403, 61409, 61417, 61441, 61463, 61469, 61471,
+ 61483, 61487, 61493, 61507, 61511, 61519, 61543, 61547, 61553,
+ 61559, 61561, 61583, 61603, 61609, 61613, 61627, 61631, 61637,
+ 61643, 61651, 61657, 61667, 61673, 61681, 61687, 61703, 61717,
+ 61723, 61729, 61751, 61757, 61781, 61813, 61819, 61837, 61843,
+ 61861, 61871, 61879, 61909, 61927, 61933, 61949, 61961, 61967,
+ 61979, 61981, 61987, 61991, 62003, 62011, 62017, 62039, 62047,
+ 62053, 62057, 62071, 62081, 62099, 62119, 62129, 62131, 62137,
+ 62141, 62143, 62171, 62189, 62191, 62201, 62207, 62213, 62219,
+ 62233, 62273, 62297, 62299, 62303, 62311, 62323, 62327, 62347,
+ 62351, 62383, 62401, 62417, 62423, 62459, 62467, 62473, 62477,
+ 62483, 62497, 62501, 62507, 62533, 62539, 62549, 62563, 62581,
+ 62591, 62597, 62603, 62617, 62627, 62633, 62639, 62653, 62659,
+ 62683, 62687, 62701, 62723, 62731, 62743, 62753, 62761, 62773,
+ 62791, 62801, 62819, 62827, 62851, 62861, 62869, 62873, 62897,
+ 62903, 62921, 62927, 62929, 62939, 62969, 62971, 62981, 62983,
+ 62987, 62989, 63029, 63031, 63059, 63067, 63073, 63079, 63097,
+ 63103, 63113, 63127, 63131, 63149, 63179, 63197, 63199, 63211,
+ 63241, 63247, 63277, 63281, 63299, 63311, 63313, 63317, 63331,
+ 63337, 63347, 63353, 63361, 63367, 63377, 63389, 63391, 63397,
+ 63409, 63419, 63421, 63439, 63443, 63463, 63467, 63473, 63487,
+ 63493, 63499, 63521, 63527, 63533, 63541, 63559, 63577, 63587,
+ 63589, 63599, 63601, 63607, 63611, 63617, 63629, 63647, 63649,
+ 63659, 63667, 63671, 63689, 63691, 63697, 63703, 63709, 63719,
+ 63727, 63737, 63743, 63761, 63773, 63781, 63793, 63799, 63803,
+ 63809, 63823, 63839, 63841, 63853, 63857, 63863, 63901, 63907,
+ 63913, 63929, 63949, 63977, 63997, 64007, 64013, 64019, 64033,
+ 64037, 64063, 64067, 64081, 64091, 64109, 64123, 64151, 64153,
+ 64157, 64171, 64187, 64189, 64217, 64223, 64231, 64237, 64271,
+ 64279, 64283, 64301, 64303, 64319, 64327, 64333, 64373, 64381,
+ 64399, 64403, 64433, 64439, 64451, 64453, 64483, 64489, 64499,
+ 64513, 64553, 64567, 64577, 64579, 64591, 64601, 64609, 64613,
+ 64621, 64627, 64633, 64661, 64663, 64667, 64679, 64693, 64709,
+ 64717, 64747, 64763, 64781, 64783, 64793, 64811, 64817, 64849,
+ 64853, 64871, 64877, 64879, 64891, 64901, 64919, 64921, 64927,
+ 64937, 64951, 64969, 64997, 65003, 65011, 65027, 65029, 65033,
+ 65053, 65063, 65071, 65089, 65099, 65101, 65111, 65119, 65123,
+ 65129, 65141, 65147, 65167, 65171, 65173, 65179, 65183, 65203,
+ 65213, 65239, 65257, 65267, 65269, 65287, 65293, 65309, 65323,
+ 65327, 65353, 65357, 65371, 65381, 65393, 65407, 65413, 65419,
+ 65423, 65437, 65447, 65449, 65479, 65497, 65519, 65521, 65537,
+ 65539, 65543, 65551, 65557, 65563, 65579, 65581, 65587, 65599,
+ 65609, 65617, 65629, 65633, 65647, 65651, 65657, 65677, 65687,
+ 65699, 65701, 65707, 65713, 65717, 65719, 65729, 65731, 65761,
+ 65777, 65789, 65809, 65827, 65831, 65837, 65839, 65843, 65851,
+ 65867, 65881, 65899, 65921, 65927, 65929, 65951, 65957, 65963,
+ 65981, 65983, 65993, 66029, 66037, 66041, 66047, 66067, 66071,
+ 66083, 66089, 66103, 66107, 66109, 66137, 66161, 66169, 66173,
+ 66179, 66191, 66221, 66239, 66271, 66293, 66301, 66337, 66343,
+ 66347, 66359, 66361, 66373, 66377, 66383, 66403, 66413, 66431,
+ 66449, 66457, 66463, 66467, 66491, 66499, 66509, 66523, 66529,
+ 66533, 66541, 66553, 66569, 66571, 66587, 66593, 66601, 66617,
+ 66629, 66643, 66653, 66683, 66697, 66701, 66713, 66721, 66733,
+ 66739, 66749, 66751, 66763, 66791, 66797, 66809, 66821, 66841,
+ 66851, 66853, 66863, 66877, 66883, 66889, 66919, 66923, 66931,
+ 66943, 66947, 66949, 66959, 66973, 66977, 67003, 67021, 67033,
+ 67043, 67049, 67057, 67061, 67073, 67079, 67103, 67121, 67129,
+ 67139, 67141, 67153, 67157, 67169, 67181, 67187, 67189, 67211,
+ 67213, 67217, 67219, 67231, 67247, 67261, 67271, 67273, 67289,
+ 67307, 67339, 67343, 67349, 67369, 67391, 67399, 67409, 67411,
+ 67421, 67427, 67429, 67433, 67447, 67453, 67477, 67481, 67489,
+ 67493, 67499, 67511, 67523, 67531, 67537, 67547, 67559, 67567,
+ 67577, 67579, 67589, 67601, 67607, 67619, 67631, 67651, 67679,
+ 67699, 67709, 67723, 67733, 67741, 67751, 67757, 67759, 67763,
+ 67777, 67783, 67789, 67801, 67807, 67819, 67829, 67843, 67853,
+ 67867, 67883, 67891, 67901, 67927, 67931, 67933, 67939, 67943,
+ 67957, 67961, 67967, 67979, 67987, 67993, 68023, 68041, 68053,
+ 68059, 68071, 68087, 68099, 68111, 68113, 68141, 68147, 68161,
+ 68171, 68207, 68209, 68213, 68219, 68227, 68239, 68261, 68279,
+ 68281, 68311, 68329, 68351, 68371, 68389, 68399, 68437, 68443,
+ 68447, 68449, 68473, 68477, 68483, 68489, 68491, 68501, 68507,
+ 68521, 68531, 68539, 68543, 68567, 68581, 68597, 68611, 68633,
+ 68639, 68659, 68669, 68683, 68687, 68699, 68711, 68713, 68729,
+ 68737, 68743, 68749, 68767, 68771, 68777, 68791, 68813, 68819,
+ 68821, 68863, 68879, 68881, 68891, 68897, 68899, 68903, 68909,
+ 68917, 68927, 68947, 68963, 68993, 69001, 69011, 69019, 69029,
+ 69031, 69061, 69067, 69073, 69109, 69119, 69127, 69143, 69149,
+ 69151, 69163, 69191, 69193, 69197, 69203, 69221, 69233, 69239,
+ 69247, 69257, 69259, 69263, 69313, 69317, 69337, 69341, 69371,
+ 69379, 69383, 69389, 69401, 69403, 69427, 69431, 69439, 69457,
+ 69463, 69467, 69473, 69481, 69491, 69493, 69497, 69499, 69539,
+ 69557, 69593, 69623, 69653, 69661, 69677, 69691, 69697, 69709,
+ 69737, 69739, 69761, 69763, 69767, 69779, 69809, 69821, 69827,
+ 69829, 69833, 69847, 69857, 69859, 69877, 69899, 69911, 69929,
+ 69931, 69941, 69959, 69991, 69997, 70001, 70003, 70009, 70019,
+ 70039, 70051, 70061, 70067, 70079, 70099, 70111, 70117, 70121,
+ 70123, 70139, 70141, 70157, 70163, 70177, 70181, 70183, 70199,
+ 70201, 70207, 70223, 70229, 70237, 70241, 70249, 70271, 70289,
+ 70297, 70309, 70313, 70321, 70327, 70351, 70373, 70379, 70381,
+ 70393, 70423, 70429, 70439, 70451, 70457, 70459, 70481, 70487,
+ 70489, 70501, 70507, 70529, 70537, 70549, 70571, 70573, 70583,
+ 70589, 70607, 70619, 70621, 70627, 70639, 70657, 70663, 70667,
+ 70687, 70709, 70717, 70729, 70753, 70769, 70783, 70793, 70823,
+ 70841, 70843, 70849, 70853, 70867, 70877, 70879, 70891, 70901,
+ 70913, 70919, 70921, 70937, 70949, 70951, 70957, 70969, 70979,
+ 70981, 70991, 70997, 70999, 71011, 71023, 71039, 71059, 71069,
+ 71081, 71089, 71119, 71129, 71143, 71147, 71153, 71161, 71167,
+ 71171, 71191, 71209, 71233, 71237, 71249, 71257, 71261, 71263,
+ 71287, 71293, 71317, 71327, 71329, 71333, 71339, 71341, 71347,
+ 71353, 71359, 71363, 71387, 71389, 71399, 71411, 71413, 71419,
+ 71429, 71437, 71443, 71453, 71471, 71473, 71479, 71483, 71503,
+ 71527, 71537, 71549, 71551, 71563, 71569, 71593, 71597, 71633,
+ 71647, 71663, 71671, 71693, 71699, 71707, 71711, 71713, 71719,
+ 71741, 71761, 71777, 71789, 71807, 71809, 71821, 71837, 71843,
+ 71849, 71861, 71867, 71879, 71881, 71887, 71899, 71909, 71917,
+ 71933, 71941, 71947, 71963, 71971, 71983, 71987, 71993, 71999,
+ 72019, 72031, 72043, 72047, 72053, 72073, 72077, 72089, 72091,
+ 72101, 72103, 72109, 72139, 72161, 72167, 72169, 72173, 72211,
+ 72221, 72223, 72227, 72229, 72251, 72253, 72269, 72271, 72277,
+ 72287, 72307, 72313, 72337, 72341, 72353, 72367, 72379, 72383,
+ 72421, 72431, 72461, 72467, 72469, 72481, 72493, 72497, 72503,
+ 72533, 72547, 72551, 72559, 72577, 72613, 72617, 72623, 72643,
+ 72647, 72649, 72661, 72671, 72673, 72679, 72689, 72701, 72707,
+ 72719, 72727, 72733, 72739, 72763, 72767, 72797, 72817, 72823,
+ 72859, 72869, 72871, 72883, 72889, 72893, 72901, 72907, 72911,
+ 72923, 72931, 72937, 72949, 72953, 72959, 72973, 72977, 72997,
+ 73009, 73013, 73019, 73037, 73039, 73043, 73061, 73063, 73079,
+ 73091, 73121, 73127, 73133, 73141, 73181, 73189, 73237, 73243,
+ 73259, 73277, 73291, 73303, 73309, 73327, 73331, 73351, 73361,
+ 73363, 73369, 73379, 73387, 73417, 73421, 73433, 73453, 73459,
+ 73471, 73477, 73483, 73517, 73523, 73529, 73547, 73553, 73561,
+ 73571, 73583, 73589, 73597, 73607, 73609, 73613, 73637, 73643,
+ 73651, 73673, 73679, 73681, 73693, 73699, 73709, 73721, 73727,
+ 73751, 73757, 73771, 73783, 73819, 73823, 73847, 73849, 73859,
+ 73867, 73877, 73883, 73897, 73907, 73939, 73943, 73951, 73961,
+ 73973, 73999, 74017, 74021, 74027, 74047, 74051, 74071, 74077,
+ 74093, 74099, 74101, 74131, 74143, 74149, 74159, 74161, 74167,
+ 74177, 74189, 74197, 74201, 74203, 74209, 74219, 74231, 74257,
+ 74279, 74287, 74293, 74297, 74311, 74317, 74323, 74353, 74357,
+ 74363, 74377, 74381, 74383, 74411, 74413, 74419, 74441, 74449,
+ 74453, 74471, 74489, 74507, 74509, 74521, 74527, 74531, 74551,
+ 74561, 74567, 74573, 74587, 74597, 74609, 74611, 74623, 74653,
+ 74687, 74699, 74707, 74713, 74717, 74719, 74729, 74731, 74747,
+ 74759, 74761, 74771, 74779, 74797, 74821, 74827, 74831, 74843,
+ 74857, 74861, 74869, 74873, 74887, 74891, 74897, 74903, 74923,
+ 74929, 74933, 74941, 74959, 75011, 75013, 75017, 75029, 75037,
+ 75041, 75079, 75083, 75109, 75133, 75149, 75161, 75167, 75169,
+ 75181, 75193, 75209, 75211, 75217, 75223, 75227, 75239, 75253,
+ 75269, 75277, 75289, 75307, 75323, 75329, 75337, 75347, 75353,
+ 75367, 75377, 75389, 75391, 75401, 75403, 75407, 75431, 75437,
+ 75479, 75503, 75511, 75521, 75527, 75533, 75539, 75541, 75553,
+ 75557, 75571, 75577, 75583, 75611, 75617, 75619, 75629, 75641,
+ 75653, 75659, 75679, 75683, 75689, 75703, 75707, 75709, 75721,
+ 75731, 75743, 75767, 75773, 75781, 75787, 75793, 75797, 75821,
+ 75833, 75853, 75869, 75883, 75913, 75931, 75937, 75941, 75967,
+ 75979, 75983, 75989, 75991, 75997, 76001, 76003, 76031, 76039,
+ 76079, 76081, 76091, 76099, 76103, 76123, 76129, 76147, 76157,
+ 76159, 76163, 76207, 76213, 76231, 76243, 76249, 76253, 76259,
+ 76261, 76283, 76289, 76303, 76333, 76343, 76367, 76369, 76379,
+ 76387, 76403, 76421, 76423, 76441, 76463, 76471, 76481, 76487,
+ 76493, 76507, 76511, 76519, 76537, 76541, 76543, 76561, 76579,
+ 76597, 76603, 76607, 76631, 76649, 76651, 76667, 76673, 76679,
+ 76697, 76717, 76733, 76753, 76757, 76771, 76777, 76781, 76801,
+ 76819, 76829, 76831, 76837, 76847, 76871, 76873, 76883, 76907,
+ 76913, 76919, 76943, 76949, 76961, 76963, 76991, 77003, 77017,
+ 77023, 77029, 77041, 77047, 77069, 77081, 77093, 77101, 77137,
+ 77141, 77153, 77167, 77171, 77191, 77201, 77213, 77237, 77239,
+ 77243, 77249, 77261, 77263, 77267, 77269, 77279, 77291, 77317,
+ 77323, 77339, 77347, 77351, 77359, 77369, 77377, 77383, 77417,
+ 77419, 77431, 77447, 77471, 77477, 77479, 77489, 77491, 77509,
+ 77513, 77521, 77527, 77543, 77549, 77551, 77557, 77563, 77569,
+ 77573, 77587, 77591, 77611, 77617, 77621, 77641, 77647, 77659,
+ 77681, 77687, 77689, 77699, 77711, 77713, 77719, 77723, 77731,
+ 77743, 77747, 77761, 77773, 77783, 77797, 77801, 77813, 77839,
+ 77849, 77863, 77867, 77893, 77899, 77929, 77933, 77951, 77969,
+ 77977, 77983, 77999, 78007, 78017, 78031, 78041, 78049, 78059,
+ 78079, 78101, 78121, 78137, 78139, 78157, 78163, 78167, 78173,
+ 78179, 78191, 78193, 78203, 78229, 78233, 78241, 78259, 78277,
+ 78283, 78301, 78307, 78311, 78317, 78341, 78347, 78367, 78401,
+ 78427, 78437, 78439, 78467, 78479, 78487, 78497, 78509, 78511,
+ 78517, 78539, 78541, 78553, 78569, 78571, 78577, 78583, 78593,
+ 78607, 78623, 78643, 78649, 78653, 78691, 78697, 78707, 78713,
+ 78721, 78737, 78779, 78781, 78787, 78791, 78797, 78803, 78809,
+ 78823, 78839, 78853, 78857, 78877, 78887, 78889, 78893, 78901,
+ 78919, 78929, 78941, 78977, 78979, 78989, 79031, 79039, 79043,
+ 79063, 79087, 79103, 79111, 79133, 79139, 79147, 79151, 79153,
+ 79159, 79181, 79187, 79193, 79201, 79229, 79231, 79241, 79259,
+ 79273, 79279, 79283, 79301, 79309, 79319, 79333, 79337, 79349,
+ 79357, 79367, 79379, 79393, 79397, 79399, 79411, 79423, 79427,
+ 79433, 79451, 79481, 79493, 79531, 79537, 79549, 79559, 79561,
+ 79579, 79589, 79601, 79609, 79613, 79621, 79627, 79631, 79633,
+ 79657, 79669, 79687, 79691, 79693, 79697, 79699, 79757, 79769,
+ 79777, 79801, 79811, 79813, 79817, 79823, 79829, 79841, 79843,
+ 79847, 79861, 79867, 79873, 79889, 79901, 79903, 79907, 79939,
+ 79943, 79967, 79973, 79979, 79987, 79997, 79999, 80021, 80039,
+ 80051, 80071, 80077, 80107, 80111, 80141, 80147, 80149, 80153,
+ 80167, 80173, 80177, 80191, 80207, 80209, 80221, 80231, 80233,
+ 80239, 80251, 80263, 80273, 80279, 80287, 80309, 80317, 80329,
+ 80341, 80347, 80363, 80369, 80387, 80407, 80429, 80447, 80449,
+ 80471, 80473, 80489, 80491, 80513, 80527, 80537, 80557, 80567,
+ 80599, 80603, 80611, 80621, 80627, 80629, 80651, 80657, 80669,
+ 80671, 80677, 80681, 80683, 80687, 80701, 80713, 80737, 80747,
+ 80749, 80761, 80777, 80779, 80783, 80789, 80803, 80809, 80819,
+ 80831, 80833, 80849, 80863, 80897, 80909, 80911, 80917, 80923,
+ 80929, 80933, 80953, 80963, 80989, 81001, 81013, 81017, 81019,
+ 81023, 81031, 81041, 81043, 81047, 81049, 81071, 81077, 81083,
+ 81097, 81101, 81119, 81131, 81157, 81163, 81173, 81181, 81197,
+ 81199, 81203, 81223, 81233, 81239, 81281, 81283, 81293, 81299,
+ 81307, 81331, 81343, 81349, 81353, 81359, 81371, 81373, 81401,
+ 81409, 81421, 81439, 81457, 81463, 81509, 81517, 81527, 81533,
+ 81547, 81551, 81553, 81559, 81563, 81569, 81611, 81619, 81629,
+ 81637, 81647, 81649, 81667, 81671, 81677, 81689, 81701, 81703,
+ 81707, 81727, 81737, 81749, 81761, 81769, 81773, 81799, 81817,
+ 81839, 81847, 81853, 81869, 81883, 81899, 81901, 81919, 81929,
+ 81931, 81937, 81943, 81953, 81967, 81971, 81973, 82003, 82007,
+ 82009, 82013, 82021, 82031, 82037, 82039, 82051, 82067, 82073,
+ 82129, 82139, 82141, 82153, 82163, 82171, 82183, 82189, 82193,
+ 82207, 82217, 82219, 82223, 82231, 82237, 82241, 82261, 82267,
+ 82279, 82301, 82307, 82339, 82349, 82351, 82361, 82373, 82387,
+ 82393, 82421, 82457, 82463, 82469, 82471, 82483, 82487, 82493,
+ 82499, 82507, 82529, 82531, 82549, 82559, 82561, 82567, 82571,
+ 82591, 82601, 82609, 82613, 82619, 82633, 82651, 82657, 82699,
+ 82721, 82723, 82727, 82729, 82757, 82759, 82763, 82781, 82787,
+ 82793, 82799, 82811, 82813, 82837, 82847, 82883, 82889, 82891,
+ 82903, 82913, 82939, 82963, 82981, 82997, 83003, 83009, 83023,
+ 83047, 83059, 83063, 83071, 83077, 83089, 83093, 83101, 83117,
+ 83137, 83177, 83203, 83207, 83219, 83221, 83227, 83231, 83233,
+ 83243, 83257, 83267, 83269, 83273, 83299, 83311, 83339, 83341,
+ 83357, 83383, 83389, 83399, 83401, 83407, 83417, 83423, 83431,
+ 83437, 83443, 83449, 83459, 83471, 83477, 83497, 83537, 83557,
+ 83561, 83563, 83579, 83591, 83597, 83609, 83617, 83621, 83639,
+ 83641, 83653, 83663, 83689, 83701, 83717, 83719, 83737, 83761,
+ 83773, 83777, 83791, 83813, 83833, 83843, 83857, 83869, 83873,
+ 83891, 83903, 83911, 83921, 83933, 83939, 83969, 83983, 83987,
+ 84011, 84017, 84047, 84053, 84059, 84061, 84067, 84089, 84121,
+ 84127, 84131, 84137, 84143, 84163, 84179, 84181, 84191, 84199,
+ 84211, 84221, 84223, 84229, 84239, 84247, 84263, 84299, 84307,
+ 84313, 84317, 84319, 84347, 84349, 84377, 84389, 84391, 84401,
+ 84407, 84421, 84431, 84437, 84443, 84449, 84457, 84463, 84467,
+ 84481, 84499, 84503, 84509, 84521, 84523, 84533, 84551, 84559,
+ 84589, 84629, 84631, 84649, 84653, 84659, 84673, 84691, 84697,
+ 84701, 84713, 84719, 84731, 84737, 84751, 84761, 84787, 84793,
+ 84809, 84811, 84827, 84857, 84859, 84869, 84871, 84913, 84919,
+ 84947, 84961, 84967, 84977, 84979, 84991, 85009, 85021, 85027,
+ 85037, 85049, 85061, 85081, 85087, 85091, 85093, 85103, 85109,
+ 85121, 85133, 85147, 85159, 85193, 85199, 85201, 85213, 85223,
+ 85229, 85237, 85243, 85247, 85259, 85297, 85303, 85313, 85331,
+ 85333, 85361, 85363, 85369, 85381, 85411, 85427, 85429, 85439,
+ 85447, 85451, 85453, 85469, 85487, 85513, 85517, 85523, 85531,
+ 85549, 85571, 85577, 85597, 85601, 85607, 85619, 85621, 85627,
+ 85639, 85643, 85661, 85667, 85669, 85691, 85703, 85711, 85717,
+ 85733, 85751, 85781, 85793, 85817, 85819, 85829, 85831, 85837,
+ 85843, 85847, 85853, 85889, 85903, 85909, 85931, 85933, 85991,
+ 85999, 86011, 86017, 86027, 86029, 86069, 86077, 86083, 86111,
+ 86113, 86117, 86131, 86137, 86143, 86161, 86171, 86179, 86183,
+ 86197, 86201, 86209, 86239, 86243, 86249, 86257, 86263, 86269,
+ 86287, 86291, 86293, 86297, 86311, 86323, 86341, 86351, 86353,
+ 86357, 86369, 86371, 86381, 86389, 86399, 86413, 86423, 86441,
+ 86453, 86461, 86467, 86477, 86491, 86501, 86509, 86531, 86533,
+ 86539, 86561, 86573, 86579, 86587, 86599, 86627, 86629, 86677,
+ 86689, 86693, 86711, 86719, 86729, 86743, 86753, 86767, 86771,
+ 86783, 86813, 86837, 86843, 86851, 86857, 86861, 86869, 86923,
+ 86927, 86929, 86939, 86951, 86959, 86969, 86981, 86993, 87011,
+ 87013, 87037, 87041, 87049, 87071, 87083, 87103, 87107, 87119,
+ 87121, 87133, 87149, 87151, 87179, 87181, 87187, 87211, 87221,
+ 87223, 87251, 87253, 87257, 87277, 87281, 87293, 87299, 87313,
+ 87317, 87323, 87337, 87359, 87383, 87403, 87407, 87421, 87427,
+ 87433, 87443, 87473, 87481, 87491, 87509, 87511, 87517, 87523,
+ 87539, 87541, 87547, 87553, 87557, 87559, 87583, 87587, 87589,
+ 87613, 87623, 87629, 87631, 87641, 87643, 87649, 87671, 87679,
+ 87683, 87691, 87697, 87701, 87719, 87721, 87739, 87743, 87751,
+ 87767, 87793, 87797, 87803, 87811, 87833, 87853, 87869, 87877,
+ 87881, 87887, 87911, 87917, 87931, 87943, 87959, 87961, 87973,
+ 87977, 87991, 88001, 88003, 88007, 88019, 88037, 88069, 88079,
+ 88093, 88117, 88129, 88169, 88177, 88211, 88223, 88237, 88241,
+ 88259, 88261, 88289, 88301, 88321, 88327, 88337, 88339, 88379,
+ 88397, 88411, 88423, 88427, 88463, 88469, 88471, 88493, 88499,
+ 88513, 88523, 88547, 88589, 88591, 88607, 88609, 88643, 88651,
+ 88657, 88661, 88663, 88667, 88681, 88721, 88729, 88741, 88747,
+ 88771, 88789, 88793, 88799, 88801, 88807, 88811, 88813, 88817,
+ 88819, 88843, 88853, 88861, 88867, 88873, 88883, 88897, 88903,
+ 88919, 88937, 88951, 88969, 88993, 88997, 89003, 89009, 89017,
+ 89021, 89041, 89051, 89057, 89069, 89071, 89083, 89087, 89101,
+ 89107, 89113, 89119, 89123, 89137, 89153, 89189, 89203, 89209,
+ 89213, 89227, 89231, 89237, 89261, 89269, 89273, 89293, 89303,
+ 89317, 89329, 89363, 89371, 89381, 89387, 89393, 89399, 89413,
+ 89417, 89431, 89443, 89449, 89459, 89477, 89491, 89501, 89513,
+ 89519, 89521, 89527, 89533, 89561, 89563, 89567, 89591, 89597,
+ 89599, 89603, 89611, 89627, 89633, 89653, 89657, 89659, 89669,
+ 89671, 89681, 89689, 89753, 89759, 89767, 89779, 89783, 89797,
+ 89809, 89819, 89821, 89833, 89839, 89849, 89867, 89891, 89897,
+ 89899, 89909, 89917, 89923, 89939, 89959, 89963, 89977, 89983,
+ 89989, 90001, 90007, 90011, 90017, 90019, 90023, 90031, 90053,
+ 90059, 90067, 90071, 90073, 90089, 90107, 90121, 90127, 90149,
+ 90163, 90173, 90187, 90191, 90197, 90199, 90203, 90217, 90227,
+ 90239, 90247, 90263, 90271, 90281, 90289, 90313, 90353, 90359,
+ 90371, 90373, 90379, 90397, 90401, 90403, 90407, 90437, 90439,
+ 90469, 90473, 90481, 90499, 90511, 90523, 90527, 90529, 90533,
+ 90547, 90583, 90599, 90617, 90619, 90631, 90641, 90647, 90659,
+ 90677, 90679, 90697, 90703, 90709, 90731, 90749, 90787, 90793,
+ 90803, 90821, 90823, 90833, 90841, 90847, 90863, 90887, 90901,
+ 90907, 90911, 90917, 90931, 90947, 90971, 90977, 90989, 90997,
+ 91009, 91019, 91033, 91079, 91081, 91097, 91099, 91121, 91127,
+ 91129, 91139, 91141, 91151, 91153, 91159, 91163, 91183, 91193,
+ 91199, 91229, 91237, 91243, 91249, 91253, 91283, 91291, 91297,
+ 91303, 91309, 91331, 91367, 91369, 91373, 91381, 91387, 91393,
+ 91397, 91411, 91423, 91433, 91453, 91457, 91459, 91463, 91493,
+ 91499, 91513, 91529, 91541, 91571, 91573, 91577, 91583, 91591,
+ 91621, 91631, 91639, 91673, 91691, 91703, 91711, 91733, 91753,
+ 91757, 91771, 91781, 91801, 91807, 91811, 91813, 91823, 91837,
+ 91841, 91867, 91873, 91909, 91921, 91939, 91943, 91951, 91957,
+ 91961, 91967, 91969, 91997, 92003, 92009, 92033, 92041, 92051,
+ 92077, 92083, 92107, 92111, 92119, 92143, 92153, 92173, 92177,
+ 92179, 92189, 92203, 92219, 92221, 92227, 92233, 92237, 92243,
+ 92251, 92269, 92297, 92311, 92317, 92333, 92347, 92353, 92357,
+ 92363, 92369, 92377, 92381, 92383, 92387, 92399, 92401, 92413,
+ 92419, 92431, 92459, 92461, 92467, 92479, 92489, 92503, 92507,
+ 92551, 92557, 92567, 92569, 92581, 92593, 92623, 92627, 92639,
+ 92641, 92647, 92657, 92669, 92671, 92681, 92683, 92693, 92699,
+ 92707, 92717, 92723, 92737, 92753, 92761, 92767, 92779, 92789,
+ 92791, 92801, 92809, 92821, 92831, 92849, 92857, 92861, 92863,
+ 92867, 92893, 92899, 92921, 92927, 92941, 92951, 92957, 92959,
+ 92987, 92993, 93001, 93047, 93053, 93059, 93077, 93083, 93089,
+ 93097, 93103, 93113, 93131, 93133, 93139, 93151, 93169, 93179,
+ 93187, 93199, 93229, 93239, 93241, 93251, 93253, 93257, 93263,
+ 93281, 93283, 93287, 93307, 93319, 93323, 93329, 93337, 93371,
+ 93377, 93383, 93407, 93419, 93427, 93463, 93479, 93481, 93487,
+ 93491, 93493, 93497, 93503, 93523, 93529, 93553, 93557, 93559,
+ 93563, 93581, 93601, 93607, 93629, 93637, 93683, 93701, 93703,
+ 93719, 93739, 93761, 93763, 93787, 93809, 93811, 93827, 93851,
+ 93871, 93887, 93889, 93893, 93901, 93911, 93913, 93923, 93937,
+ 93941, 93949, 93967, 93971, 93979, 93983, 93997, 94007, 94009,
+ 94033, 94049, 94057, 94063, 94079, 94099, 94109, 94111, 94117,
+ 94121, 94151, 94153, 94169, 94201, 94207, 94219, 94229, 94253,
+ 94261, 94273, 94291, 94307, 94309, 94321, 94327, 94331, 94343,
+ 94349, 94351, 94379, 94397, 94399, 94421, 94427, 94433, 94439,
+ 94441, 94447, 94463, 94477, 94483, 94513, 94529, 94531, 94541,
+ 94543, 94547, 94559, 94561, 94573, 94583, 94597, 94603, 94613,
+ 94621, 94649, 94651, 94687, 94693, 94709, 94723, 94727, 94747,
+ 94771, 94777, 94781, 94789, 94793, 94811, 94819, 94823, 94837,
+ 94841, 94847, 94849, 94873, 94889, 94903, 94907, 94933, 94949,
+ 94951, 94961, 94993, 94999, 95003, 95009, 95021, 95027, 95063,
+ 95071, 95083, 95087, 95089, 95093, 95101, 95107, 95111, 95131,
+ 95143, 95153, 95177, 95189, 95191, 95203, 95213, 95219, 95231,
+ 95233, 95239, 95257, 95261, 95267, 95273, 95279, 95287, 95311,
+ 95317, 95327, 95339, 95369, 95383, 95393, 95401, 95413, 95419,
+ 95429, 95441, 95443, 95461, 95467, 95471, 95479, 95483, 95507,
+ 95527, 95531, 95539, 95549, 95561, 95569, 95581, 95597, 95603,
+ 95617, 95621, 95629, 95633, 95651, 95701, 95707, 95713, 95717,
+ 95723, 95731, 95737, 95747, 95773, 95783, 95789, 95791, 95801,
+ 95803, 95813, 95819, 95857, 95869, 95873, 95881, 95891, 95911,
+ 95917, 95923, 95929, 95947, 95957, 95959, 95971, 95987, 95989,
+ 96001, 96013, 96017, 96043, 96053, 96059, 96079, 96097, 96137,
+ 96149, 96157, 96167, 96179, 96181, 96199, 96211, 96221, 96223,
+ 96233, 96259, 96263, 96269, 96281, 96289, 96293, 96323, 96329,
+ 96331, 96337, 96353, 96377, 96401, 96419, 96431, 96443, 96451,
+ 96457, 96461, 96469, 96479, 96487, 96493, 96497, 96517, 96527,
+ 96553, 96557, 96581, 96587, 96589, 96601, 96643, 96661, 96667,
+ 96671, 96697, 96703, 96731, 96737, 96739, 96749, 96757, 96763,
+ 96769, 96779, 96787, 96797, 96799, 96821, 96823, 96827, 96847,
+ 96851, 96857, 96893, 96907, 96911, 96931, 96953, 96959, 96973,
+ 96979, 96989, 96997, 97001, 97003, 97007, 97021, 97039, 97073,
+ 97081, 97103, 97117, 97127, 97151, 97157, 97159, 97169, 97171,
+ 97177, 97187, 97213, 97231, 97241, 97259, 97283, 97301, 97303,
+ 97327, 97367, 97369, 97373, 97379, 97381, 97387, 97397, 97423,
+ 97429, 97441, 97453, 97459, 97463, 97499, 97501, 97511, 97523,
+ 97547, 97549, 97553, 97561, 97571, 97577, 97579, 97583, 97607,
+ 97609, 97613, 97649, 97651, 97673, 97687, 97711, 97729, 97771,
+ 97777, 97787, 97789, 97813, 97829, 97841, 97843, 97847, 97849,
+ 97859, 97861, 97871, 97879, 97883, 97919, 97927, 97931, 97943,
+ 97961, 97967, 97973, 97987, 98009, 98011, 98017, 98041, 98047,
+ 98057, 98081, 98101, 98123, 98129, 98143, 98179, 98207, 98213,
+ 98221, 98227, 98251, 98257, 98269, 98297, 98299, 98317, 98321,
+ 98323, 98327, 98347, 98369, 98377, 98387, 98389, 98407, 98411,
+ 98419, 98429, 98443, 98453, 98459, 98467, 98473, 98479, 98491,
+ 98507, 98519, 98533, 98543, 98561, 98563, 98573, 98597, 98621,
+ 98627, 98639, 98641, 98663, 98669, 98689, 98711, 98713, 98717,
+ 98729, 98731, 98737, 98773, 98779, 98801, 98807, 98809, 98837,
+ 98849, 98867, 98869, 98873, 98887, 98893, 98897, 98899, 98909,
+ 98911, 98927, 98929, 98939, 98947, 98953, 98963, 98981, 98993,
+ 98999, 99013, 99017, 99023, 99041, 99053, 99079, 99083, 99089,
+ 99103, 99109, 99119, 99131, 99133, 99137, 99139, 99149, 99173,
+ 99181, 99191, 99223, 99233, 99241, 99251, 99257, 99259, 99277,
+ 99289, 99317, 99347, 99349, 99367, 99371, 99377, 99391, 99397,
+ 99401, 99409, 99431, 99439, 99469, 99487, 99497, 99523, 99527,
+ 99529, 99551, 99559, 99563, 99571, 99577, 99581, 99607, 99611,
+ 99623, 99643, 99661, 99667, 99679, 99689, 99707, 99709, 99713,
+ 99719, 99721, 99733, 99761, 99767, 99787, 99793, 99809, 99817,
+ 99823, 99829, 99833, 99839, 99859, 99871, 99877, 99881, 99901,
+ 99907, 99923, 99929, 99961, 99971, 99989, 99991,
+ };
diff --git a/src/rt/isaac/rand.h b/src/rt/isaac/rand.h
new file mode 100644
index 00000000..018496f6
--- /dev/null
+++ b/src/rt/isaac/rand.h
@@ -0,0 +1,56 @@
+/*
+------------------------------------------------------------------------------
+rand.h: definitions for a random number generator
+By Bob Jenkins, 1996, Public Domain
+MODIFIED:
+ 960327: Creation (addition of randinit, really)
+ 970719: use context, not global variables, for internal state
+ 980324: renamed seed to flag
+ 980605: recommend RANDSIZL=4 for noncryptography.
+ 010626: note this is public domain
+------------------------------------------------------------------------------
+*/
+#ifndef STANDARD
+#include "standard.h"
+#endif
+
+#ifndef RAND
+#define RAND
+#define RANDSIZL (8) /* I recommend 8 for crypto, 4 for simulations */
+#define RANDSIZ (1<<RANDSIZL)
+
+/* context of random number generator */
+struct randctx
+{
+ ub4 randcnt;
+ ub4 randrsl[RANDSIZ];
+ ub4 randmem[RANDSIZ];
+ ub4 randa;
+ ub4 randb;
+ ub4 randc;
+};
+typedef struct randctx randctx;
+
+/*
+------------------------------------------------------------------------------
+ If (flag==TRUE), then use the contents of randrsl[0..RANDSIZ-1] as the seed.
+------------------------------------------------------------------------------
+*/
+void randinit(randctx *r, word flag);
+
+void isaac(randctx *r);
+
+
+/*
+------------------------------------------------------------------------------
+ Call rand(/o_ randctx *r _o/) to retrieve a single 32-bit random value
+------------------------------------------------------------------------------
+*/
+#define rand(r) \
+ (!(r)->randcnt-- ? \
+ (isaac(r), (r)->randcnt=RANDSIZ-1, (r)->randrsl[(r)->randcnt]) : \
+ (r)->randrsl[(r)->randcnt])
+
+#endif /* RAND */
+
+
diff --git a/src/rt/isaac/randport.cpp b/src/rt/isaac/randport.cpp
new file mode 100644
index 00000000..45ec590d
--- /dev/null
+++ b/src/rt/isaac/randport.cpp
@@ -0,0 +1,134 @@
+/*
+------------------------------------------------------------------------------
+rand.c: By Bob Jenkins. My random number generator, ISAAC. Public Domain
+MODIFIED:
+ 960327: Creation (addition of randinit, really)
+ 970719: use context, not global variables, for internal state
+ 980324: make a portable version
+ 010626: Note this is public domain
+------------------------------------------------------------------------------
+*/
+#ifndef STANDARD
+#include "standard.h"
+#endif
+#ifndef RAND
+#include "rand.h"
+#endif
+
+
+#define ind(mm,x) ((mm)[(x>>2)&(RANDSIZ-1)])
+#define rngstep(mix,a,b,mm,m,m2,r,x) \
+{ \
+ x = *m; \
+ a = ((a^(mix)) + *(m2++)) & 0xffffffff; \
+ *(m++) = y = (ind(mm,x) + a + b) & 0xffffffff; \
+ *(r++) = b = (ind(mm,y>>RANDSIZL) + x) & 0xffffffff; \
+}
+
+void isaac(randctx *ctx)
+{
+ register ub4 a,b,x,y,*m,*mm,*m2,*r,*mend;
+ mm=ctx->randmem; r=ctx->randrsl;
+ a = ctx->randa; b = (ctx->randb + (++ctx->randc)) & 0xffffffff;
+ for (m = mm, mend = m2 = m+(RANDSIZ/2); m<mend; )
+ {
+ rngstep( a<<13, a, b, mm, m, m2, r, x);
+ rngstep( a>>6 , a, b, mm, m, m2, r, x);
+ rngstep( a<<2 , a, b, mm, m, m2, r, x);
+ rngstep( a>>16, a, b, mm, m, m2, r, x);
+ }
+ for (m2 = mm; m2<mend; )
+ {
+ rngstep( a<<13, a, b, mm, m, m2, r, x);
+ rngstep( a>>6 , a, b, mm, m, m2, r, x);
+ rngstep( a<<2 , a, b, mm, m, m2, r, x);
+ rngstep( a>>16, a, b, mm, m, m2, r, x);
+ }
+ ctx->randb = b; ctx->randa = a;
+}
+
+
+#define mix(a,b,c,d,e,f,g,h) \
+{ \
+ a^=b<<11; d+=a; b+=c; \
+ b^=c>>2; e+=b; c+=d; \
+ c^=d<<8; f+=c; d+=e; \
+ d^=e>>16; g+=d; e+=f; \
+ e^=f<<10; h+=e; f+=g; \
+ f^=g>>4; a+=f; g+=h; \
+ g^=h<<8; b+=g; h+=a; \
+ h^=a>>9; c+=h; a+=b; \
+}
+
+/* if (flag==TRUE), then use the contents of randrsl[] to initialize mm[]. */
+void randinit(randctx *ctx, word flag)
+{
+ word i;
+ ub4 a,b,c,d,e,f,g,h;
+ ub4 *m,*r;
+ ctx->randa = ctx->randb = ctx->randc = 0;
+ m=ctx->randmem;
+ r=ctx->randrsl;
+ a=b=c=d=e=f=g=h=0x9e3779b9; /* the golden ratio */
+
+ for (i=0; i<4; ++i) /* scramble it */
+ {
+ mix(a,b,c,d,e,f,g,h);
+ }
+
+ if (flag)
+ {
+ /* initialize using the contents of r[] as the seed */
+ for (i=0; i<RANDSIZ; i+=8)
+ {
+ a+=r[i ]; b+=r[i+1]; c+=r[i+2]; d+=r[i+3];
+ e+=r[i+4]; f+=r[i+5]; g+=r[i+6]; h+=r[i+7];
+ mix(a,b,c,d,e,f,g,h);
+ m[i ]=a; m[i+1]=b; m[i+2]=c; m[i+3]=d;
+ m[i+4]=e; m[i+5]=f; m[i+6]=g; m[i+7]=h;
+ }
+ /* do a second pass to make all of the seed affect all of m */
+ for (i=0; i<RANDSIZ; i+=8)
+ {
+ a+=m[i ]; b+=m[i+1]; c+=m[i+2]; d+=m[i+3];
+ e+=m[i+4]; f+=m[i+5]; g+=m[i+6]; h+=m[i+7];
+ mix(a,b,c,d,e,f,g,h);
+ m[i ]=a; m[i+1]=b; m[i+2]=c; m[i+3]=d;
+ m[i+4]=e; m[i+5]=f; m[i+6]=g; m[i+7]=h;
+ }
+ }
+ else
+ {
+ for (i=0; i<RANDSIZ; i+=8)
+ {
+ /* fill in mm[] with messy stuff */
+ mix(a,b,c,d,e,f,g,h);
+ m[i ]=a; m[i+1]=b; m[i+2]=c; m[i+3]=d;
+ m[i+4]=e; m[i+5]=f; m[i+6]=g; m[i+7]=h;
+ }
+ }
+
+ isaac(ctx); /* fill in the first set of results */
+ ctx->randcnt=RANDSIZ; /* prepare to use the first set of results */
+}
+
+
+#ifdef NEVER
+int main()
+{
+ ub4 i,j;
+ randctx ctx;
+ ctx.randa=ctx.randb=ctx.randc=(ub4)0;
+ for (i=0; i<256; ++i) ctx.randrsl[i]=(ub4)0;
+ randinit(&ctx, TRUE);
+ for (i=0; i<2; ++i)
+ {
+ isaac(&ctx);
+ for (j=0; j<256; ++j)
+ {
+ printf("%.8lx",ctx.randrsl[j]);
+ if ((j&7)==7) printf("\n");
+ }
+ }
+}
+#endif
diff --git a/src/rt/isaac/standard.h b/src/rt/isaac/standard.h
new file mode 100644
index 00000000..202a5d65
--- /dev/null
+++ b/src/rt/isaac/standard.h
@@ -0,0 +1,57 @@
+/*
+------------------------------------------------------------------------------
+Standard definitions and types, Bob Jenkins
+------------------------------------------------------------------------------
+*/
+#ifndef STANDARD
+# define STANDARD
+# ifndef STDIO
+# include <stdio.h>
+# define STDIO
+# endif
+# ifndef STDDEF
+# include <stddef.h>
+# define STDDEF
+# endif
+typedef unsigned long long ub8;
+#define UB8MAXVAL 0xffffffffffffffffLL
+#define UB8BITS 64
+typedef signed long long sb8;
+#define SB8MAXVAL 0x7fffffffffffffffLL
+typedef unsigned long int ub4; /* unsigned 4-byte quantities */
+#define UB4MAXVAL 0xffffffff
+typedef signed long int sb4;
+#define UB4BITS 32
+#define SB4MAXVAL 0x7fffffff
+typedef unsigned short int ub2;
+#define UB2MAXVAL 0xffff
+#define UB2BITS 16
+typedef signed short int sb2;
+#define SB2MAXVAL 0x7fff
+typedef unsigned char ub1;
+#define UB1MAXVAL 0xff
+#define UB1BITS 8
+typedef signed char sb1; /* signed 1-byte quantities */
+#define SB1MAXVAL 0x7f
+typedef int word; /* fastest type available */
+
+#define bis(target,mask) ((target) |= (mask))
+#define bic(target,mask) ((target) &= ~(mask))
+#define bit(target,mask) ((target) & (mask))
+#ifndef min
+# define min(a,b) (((a)<(b)) ? (a) : (b))
+#endif /* min */
+#ifndef max
+# define max(a,b) (((a)<(b)) ? (b) : (a))
+#endif /* max */
+#ifndef align
+# define align(a) (((ub4)a+(sizeof(void *)-1))&(~(sizeof(void *)-1)))
+#endif /* align */
+#ifndef abs
+# define abs(a) (((a)>0) ? (a) : -(a))
+#endif
+#define TRUE 1
+#define FALSE 0
+#define SUCCESS 0 /* 1 on VAX */
+
+#endif /* STANDARD */
diff --git a/src/rt/memcheck.h b/src/rt/memcheck.h
new file mode 100644
index 00000000..fc50dabf
--- /dev/null
+++ b/src/rt/memcheck.h
@@ -0,0 +1,309 @@
+
+/*
+ ----------------------------------------------------------------
+
+ Notice that the following BSD-style license applies to this one
+ file (memcheck.h) only. The rest of Valgrind is licensed under the
+ terms of the GNU General Public License, version 2, unless
+ otherwise indicated. See the COPYING file in the source
+ distribution for details.
+
+ ----------------------------------------------------------------
+
+ This file is part of MemCheck, a heavyweight Valgrind tool for
+ detecting memory errors.
+
+ Copyright (C) 2000-2009 Julian Seward. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ 2. The origin of this software must not be misrepresented; you must
+ not claim that you wrote the original software. If you use this
+ software in a product, an acknowledgment in the product
+ documentation would be appreciated but is not required.
+
+ 3. Altered source versions must be plainly marked as such, and must
+ not be misrepresented as being the original software.
+
+ 4. The name of the author may not be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ ----------------------------------------------------------------
+
+ Notice that the above BSD-style license applies to this one file
+ (memcheck.h) only. The entire rest of Valgrind is licensed under
+ the terms of the GNU General Public License, version 2. See the
+ COPYING file in the source distribution for details.
+
+ ----------------------------------------------------------------
+*/
+
+
+#ifndef __MEMCHECK_H
+#define __MEMCHECK_H
+
+
+/* This file is for inclusion into client (your!) code.
+
+ You can use these macros to manipulate and query memory permissions
+ inside your own programs.
+
+ See comment near the top of valgrind.h on how to use them.
+*/
+
+#include "valgrind.h"
+
+/* !! ABIWARNING !! ABIWARNING !! ABIWARNING !! ABIWARNING !!
+ This enum comprises an ABI exported by Valgrind to programs
+ which use client requests. DO NOT CHANGE THE ORDER OF THESE
+ ENTRIES, NOR DELETE ANY -- add new ones at the end. */
+typedef
+ enum {
+ VG_USERREQ__MAKE_MEM_NOACCESS = VG_USERREQ_TOOL_BASE('M','C'),
+ VG_USERREQ__MAKE_MEM_UNDEFINED,
+ VG_USERREQ__MAKE_MEM_DEFINED,
+ VG_USERREQ__DISCARD,
+ VG_USERREQ__CHECK_MEM_IS_ADDRESSABLE,
+ VG_USERREQ__CHECK_MEM_IS_DEFINED,
+ VG_USERREQ__DO_LEAK_CHECK,
+ VG_USERREQ__COUNT_LEAKS,
+
+ VG_USERREQ__GET_VBITS,
+ VG_USERREQ__SET_VBITS,
+
+ VG_USERREQ__CREATE_BLOCK,
+
+ VG_USERREQ__MAKE_MEM_DEFINED_IF_ADDRESSABLE,
+
+ /* Not next to VG_USERREQ__COUNT_LEAKS because it was added later. */
+ VG_USERREQ__COUNT_LEAK_BLOCKS,
+
+ /* This is just for memcheck's internal use - don't use it */
+ _VG_USERREQ__MEMCHECK_RECORD_OVERLAP_ERROR
+ = VG_USERREQ_TOOL_BASE('M','C') + 256
+ } Vg_MemCheckClientRequest;
+
+
+
+/* Client-code macros to manipulate the state of memory. */
+
+/* Mark memory at _qzz_addr as unaddressable for _qzz_len bytes. */
+#define VALGRIND_MAKE_MEM_NOACCESS(_qzz_addr,_qzz_len) \
+ (__extension__({unsigned long _qzz_res; \
+ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0 /* default return */, \
+ VG_USERREQ__MAKE_MEM_NOACCESS, \
+ _qzz_addr, _qzz_len, 0, 0, 0); \
+ _qzz_res; \
+ }))
+
+/* Similarly, mark memory at _qzz_addr as addressable but undefined
+ for _qzz_len bytes. */
+#define VALGRIND_MAKE_MEM_UNDEFINED(_qzz_addr,_qzz_len) \
+ (__extension__({unsigned long _qzz_res; \
+ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0 /* default return */, \
+ VG_USERREQ__MAKE_MEM_UNDEFINED, \
+ _qzz_addr, _qzz_len, 0, 0, 0); \
+ _qzz_res; \
+ }))
+
+/* Similarly, mark memory at _qzz_addr as addressable and defined
+ for _qzz_len bytes. */
+#define VALGRIND_MAKE_MEM_DEFINED(_qzz_addr,_qzz_len) \
+ (__extension__({unsigned long _qzz_res; \
+ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0 /* default return */, \
+ VG_USERREQ__MAKE_MEM_DEFINED, \
+ _qzz_addr, _qzz_len, 0, 0, 0); \
+ _qzz_res; \
+ }))
+
+/* Similar to VALGRIND_MAKE_MEM_DEFINED except that addressability is
+ not altered: bytes which are addressable are marked as defined,
+ but those which are not addressable are left unchanged. */
+#define VALGRIND_MAKE_MEM_DEFINED_IF_ADDRESSABLE(_qzz_addr,_qzz_len) \
+ (__extension__({unsigned long _qzz_res; \
+ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0 /* default return */, \
+ VG_USERREQ__MAKE_MEM_DEFINED_IF_ADDRESSABLE, \
+ _qzz_addr, _qzz_len, 0, 0, 0); \
+ _qzz_res; \
+ }))
+
+/* Create a block-description handle. The description is an ascii
+ string which is included in any messages pertaining to addresses
+ within the specified memory range. Has no other effect on the
+ properties of the memory range. */
+#define VALGRIND_CREATE_BLOCK(_qzz_addr,_qzz_len, _qzz_desc) \
+ (__extension__({unsigned long _qzz_res; \
+ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0 /* default return */, \
+ VG_USERREQ__CREATE_BLOCK, \
+ _qzz_addr, _qzz_len, _qzz_desc, \
+ 0, 0); \
+ _qzz_res; \
+ }))
+
+/* Discard a block-description-handle. Returns 1 for an
+ invalid handle, 0 for a valid handle. */
+#define VALGRIND_DISCARD(_qzz_blkindex) \
+ (__extension__ ({unsigned long _qzz_res; \
+ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0 /* default return */, \
+ VG_USERREQ__DISCARD, \
+ 0, _qzz_blkindex, 0, 0, 0); \
+ _qzz_res; \
+ }))
+
+
+/* Client-code macros to check the state of memory. */
+
+/* Check that memory at _qzz_addr is addressable for _qzz_len bytes.
+ If suitable addressibility is not established, Valgrind prints an
+ error message and returns the address of the first offending byte.
+ Otherwise it returns zero. */
+#define VALGRIND_CHECK_MEM_IS_ADDRESSABLE(_qzz_addr,_qzz_len) \
+ (__extension__({unsigned long _qzz_res; \
+ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
+ VG_USERREQ__CHECK_MEM_IS_ADDRESSABLE,\
+ _qzz_addr, _qzz_len, 0, 0, 0); \
+ _qzz_res; \
+ }))
+
+/* Check that memory at _qzz_addr is addressable and defined for
+ _qzz_len bytes. If suitable addressibility and definedness are not
+ established, Valgrind prints an error message and returns the
+ address of the first offending byte. Otherwise it returns zero. */
+#define VALGRIND_CHECK_MEM_IS_DEFINED(_qzz_addr,_qzz_len) \
+ (__extension__({unsigned long _qzz_res; \
+ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
+ VG_USERREQ__CHECK_MEM_IS_DEFINED, \
+ _qzz_addr, _qzz_len, 0, 0, 0); \
+ _qzz_res; \
+ }))
+
+/* Use this macro to force the definedness and addressibility of an
+ lvalue to be checked. If suitable addressibility and definedness
+ are not established, Valgrind prints an error message and returns
+ the address of the first offending byte. Otherwise it returns
+ zero. */
+#define VALGRIND_CHECK_VALUE_IS_DEFINED(__lvalue) \
+ VALGRIND_CHECK_MEM_IS_DEFINED( \
+ (volatile unsigned char *)&(__lvalue), \
+ (unsigned long)(sizeof (__lvalue)))
+
+
+/* Do a full memory leak check (like --leak-check=full) mid-execution. */
+#define VALGRIND_DO_LEAK_CHECK \
+ {unsigned long _qzz_res; \
+ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
+ VG_USERREQ__DO_LEAK_CHECK, \
+ 0, 0, 0, 0, 0); \
+ }
+
+/* Do a summary memory leak check (like --leak-check=summary) mid-execution. */
+#define VALGRIND_DO_QUICK_LEAK_CHECK \
+ {unsigned long _qzz_res; \
+ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
+ VG_USERREQ__DO_LEAK_CHECK, \
+ 1, 0, 0, 0, 0); \
+ }
+
+/* Return number of leaked, dubious, reachable and suppressed bytes found by
+ all previous leak checks. They must be lvalues. */
+#define VALGRIND_COUNT_LEAKS(leaked, dubious, reachable, suppressed) \
+ /* For safety on 64-bit platforms we assign the results to private
+ unsigned long variables, then assign these to the lvalues the user
+ specified, which works no matter what type 'leaked', 'dubious', etc
+ are. We also initialise '_qzz_leaked', etc because
+ VG_USERREQ__COUNT_LEAKS doesn't mark the values returned as
+ defined. */ \
+ {unsigned long _qzz_res; \
+ unsigned long _qzz_leaked = 0, _qzz_dubious = 0; \
+ unsigned long _qzz_reachable = 0, _qzz_suppressed = 0; \
+ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
+ VG_USERREQ__COUNT_LEAKS, \
+ &_qzz_leaked, &_qzz_dubious, \
+ &_qzz_reachable, &_qzz_suppressed, 0); \
+ leaked = _qzz_leaked; \
+ dubious = _qzz_dubious; \
+ reachable = _qzz_reachable; \
+ suppressed = _qzz_suppressed; \
+ }
+
+/* Return number of leaked, dubious, reachable and suppressed bytes found by
+ all previous leak checks. They must be lvalues. */
+#define VALGRIND_COUNT_LEAK_BLOCKS(leaked, dubious, reachable, suppressed) \
+ /* For safety on 64-bit platforms we assign the results to private
+ unsigned long variables, then assign these to the lvalues the user
+ specified, which works no matter what type 'leaked', 'dubious', etc
+ are. We also initialise '_qzz_leaked', etc because
+ VG_USERREQ__COUNT_LEAKS doesn't mark the values returned as
+ defined. */ \
+ {unsigned long _qzz_res; \
+ unsigned long _qzz_leaked = 0, _qzz_dubious = 0; \
+ unsigned long _qzz_reachable = 0, _qzz_suppressed = 0; \
+ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
+ VG_USERREQ__COUNT_LEAK_BLOCKS, \
+ &_qzz_leaked, &_qzz_dubious, \
+ &_qzz_reachable, &_qzz_suppressed, 0); \
+ leaked = _qzz_leaked; \
+ dubious = _qzz_dubious; \
+ reachable = _qzz_reachable; \
+ suppressed = _qzz_suppressed; \
+ }
+
+
+/* Get the validity data for addresses [zza..zza+zznbytes-1] and copy it
+ into the provided zzvbits array. Return values:
+ 0 if not running on valgrind
+ 1 success
+ 2 [previously indicated unaligned arrays; these are now allowed]
+ 3 if any parts of zzsrc/zzvbits are not addressable.
+ The metadata is not copied in cases 0, 2 or 3 so it should be
+ impossible to segfault your system by using this call.
+*/
+#define VALGRIND_GET_VBITS(zza,zzvbits,zznbytes) \
+ (__extension__({unsigned long _qzz_res; \
+ char* czza = (char*)zza; \
+ char* czzvbits = (char*)zzvbits; \
+ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
+ VG_USERREQ__GET_VBITS, \
+ czza, czzvbits, zznbytes, 0, 0 ); \
+ _qzz_res; \
+ }))
+
+/* Set the validity data for addresses [zza..zza+zznbytes-1], copying it
+ from the provided zzvbits array. Return values:
+ 0 if not running on valgrind
+ 1 success
+ 2 [previously indicated unaligned arrays; these are now allowed]
+ 3 if any parts of zza/zzvbits are not addressable.
+ The metadata is not copied in cases 0, 2 or 3 so it should be
+ impossible to segfault your system by using this call.
+*/
+#define VALGRIND_SET_VBITS(zza,zzvbits,zznbytes) \
+ (__extension__({unsigned int _qzz_res; \
+ char* czza = (char*)zza; \
+ char* czzvbits = (char*)zzvbits; \
+ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
+ VG_USERREQ__SET_VBITS, \
+ czza, czzvbits, zznbytes, 0, 0 ); \
+ _qzz_res; \
+ }))
+
+#endif
+
diff --git a/src/rt/rust.cpp b/src/rt/rust.cpp
new file mode 100644
index 00000000..8c725bfb
--- /dev/null
+++ b/src/rt/rust.cpp
@@ -0,0 +1,267 @@
+#include "rust_internal.h"
+#include "util/array_list.h"
+
+
+// #define TRACK_ALLOCATIONS
+// For debugging, keeps track of live allocations, so you can find out
+// exactly what leaked.
+
+#ifdef TRACK_ALLOCATIONS
+array_list<void *> allocation_list;
+#endif
+
+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)
+{
+ if (live_allocs < 1) {
+ fatal("live_allocs < 1", __FILE__, __LINE__);
+ }
+ live_allocs--;
+ ::free(p);
+#ifdef TRACK_ALLOCATIONS
+ if (allocation_list.replace(p, NULL) == NULL) {
+ fatal("not in allocation_list", __FILE__, __LINE__);
+ }
+#endif
+}
+
+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);
+}
+
+rust_srv *
+rust_srv::clone()
+{
+ return new rust_srv();
+}
+
+
+int
+rust_main_loop(rust_dom *dom)
+{
+ // Make sure someone is watching, to pull us out of infinite loops.
+ rust_timer timer(*dom);
+
+ int rval;
+ rust_task *task;
+
+ dom->log(rust_log::DOM,
+ "running main-loop on domain 0x%" PRIxPTR, dom);
+ dom->logptr("exit-task glue",
+ dom->root_crate->get_exit_task_glue());
+
+ while ((task = dom->sched()) != NULL) {
+ I(dom, task->running());
+
+ dom->log(rust_log::TASK,
+ "activating task 0x%" PRIxPTR ", sp=0x%" PRIxPTR,
+ (uintptr_t)task, task->rust_sp);
+
+ dom->interrupt_flag = 0;
+
+ dom->activate(task);
+
+ dom->log(rust_log::TASK,
+ "returned from task 0x%" PRIxPTR
+ " in state '%s', sp=0x%" PRIxPTR,
+ (uintptr_t)task,
+ dom->state_vec_name(task->state),
+ task->rust_sp);
+
+ I(dom, task->rust_sp >= (uintptr_t) &task->stk->data[0]);
+ I(dom, task->rust_sp < task->stk->limit);
+
+ dom->reap_dead_tasks();
+ }
+
+ dom->log(rust_log::DOM, "finished main-loop (dom.rval = %d)", dom->rval);
+ rval = dom->rval;
+
+ return rval;
+}
+
+
+struct
+command_line_args
+{
+ rust_dom &dom;
+ int argc;
+ char **argv;
+
+ // vec[str] passed to rust_task::start.
+ rust_vec *args;
+
+ command_line_args(rust_dom &dom,
+ int sys_argc,
+ char **sys_argv)
+ : dom(dom),
+ argc(sys_argc),
+ argv(sys_argv),
+ args(NULL)
+ {
+#if defined(__WIN32__)
+ LPCWSTR cmdline = GetCommandLineW();
+ LPWSTR *wargv = CommandLineToArgvW(cmdline, &argc);
+ dom.win32_require("CommandLineToArgvW", argv != NULL);
+ argv = (char **) dom.malloc(sizeof(char*) * argc);
+ for (int i = 0; i < argc; ++i) {
+ int n_chars = WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1,
+ NULL, 0, NULL, NULL);
+ dom.win32_require("WideCharToMultiByte(0)", n_chars != 0);
+ argv[i] = (char *) dom.malloc(n_chars);
+ n_chars = WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1,
+ argv[i], n_chars, NULL, NULL);
+ dom.win32_require("WideCharToMultiByte(1)", n_chars != 0);
+ }
+ LocalFree(wargv);
+#endif
+ size_t vec_fill = sizeof(rust_str *) * argc;
+ size_t vec_alloc = next_power_of_two(sizeof(rust_vec) + vec_fill);
+ void *mem = dom.malloc(vec_alloc);
+ args = new (mem) rust_vec(&dom, vec_alloc, 0, NULL);
+ rust_str **strs = (rust_str**) &args->data[0];
+ for (int i = 0; i < argc; ++i) {
+ size_t str_fill = strlen(argv[i]) + 1;
+ size_t str_alloc = next_power_of_two(sizeof(rust_str) + str_fill);
+ mem = dom.malloc(str_alloc);
+ strs[i] = new (mem) rust_str(&dom, str_alloc, str_fill,
+ (uint8_t const *)argv[i]);
+ }
+ args->fill = vec_fill;
+ // If the caller has a declared args array, they may drop; but
+ // we don't know if they have such an array. So we pin the args
+ // array here to ensure it survives to program-shutdown.
+ args->ref();
+ }
+
+ ~command_line_args() {
+ if (args) {
+ // Drop the args we've had pinned here.
+ rust_str **strs = (rust_str**) &args->data[0];
+ for (int i = 0; i < argc; ++i)
+ dom.free(strs[i]);
+ dom.free(args);
+ }
+
+#ifdef __WIN32__
+ for (int i = 0; i < argc; ++i) {
+ dom.free(argv[i]);
+ }
+ dom.free(argv);
+#endif
+ }
+};
+
+
+extern "C" CDECL int
+rust_start(uintptr_t main_fn, rust_crate const *crate, int argc, char **argv)
+{
+ int ret;
+ {
+ rust_srv srv;
+ rust_dom dom(&srv, crate);
+ command_line_args args(dom, argc, argv);
+
+ dom.log(rust_log::DOM, "startup: %d args", args.argc);
+ for (int i = 0; i < args.argc; ++i)
+ dom.log(rust_log::DOM,
+ "startup: arg[%d] = '%s'", i, args.argv[i]);
+
+ if (dom._log.is_tracing(rust_log::DWARF)) {
+ rust_crate_reader rdr(&dom, crate);
+ }
+
+ uintptr_t main_args[3] = { 0, 0, (uintptr_t)args.args };
+
+ dom.root_task->start(crate->get_exit_task_glue(),
+ main_fn,
+ (uintptr_t)&main_args,
+ sizeof(main_args));
+
+ ret = rust_main_loop(&dom);
+ }
+
+#if !defined(__WIN32__)
+ // Don't take down the process if the main thread exits without an
+ // error.
+ if (!ret)
+ pthread_exit(NULL);
+#endif
+ return ret;
+}
+
+//
+// Local Variables:
+// mode: C++
+// 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/rt/rust.h b/src/rt/rust.h
new file mode 100644
index 00000000..135a1799
--- /dev/null
+++ b/src/rt/rust.h
@@ -0,0 +1,49 @@
+#ifndef RUST_H
+#define RUST_H
+
+/*
+ * Include this file after you've defined the ISO C9x stdint
+ * types (size_t, uint8_t, uintptr_t, etc.)
+ */
+
+#ifdef __i386__
+// 'cdecl' ABI only means anything on i386
+#ifdef __WIN32__
+#define CDECL __cdecl
+#else
+#define CDECL __attribute__((cdecl))
+#endif
+#else
+#define CDECL
+#endif
+
+struct rust_srv {
+ size_t live_allocs;
+
+ virtual void log(char const *);
+ virtual void fatal(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);
+}
+
+/*
+ * 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:
+ */
+
+#endif /* RUST_H */
diff --git a/src/rt/rust_builtin.cpp b/src/rt/rust_builtin.cpp
new file mode 100644
index 00000000..71aa644b
--- /dev/null
+++ b/src/rt/rust_builtin.cpp
@@ -0,0 +1,129 @@
+
+#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) {
+ rust_dom *dom = task->dom;
+ dom->log(rust_log::TASK, "last_os_error()");
+
+#if defined(__WIN32__)
+ LPTSTR buf;
+ DWORD err = GetLastError();
+ DWORD res = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL, err,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR) &buf, 0, NULL);
+ if (!res) {
+ task->fail(1);
+ return NULL;
+ }
+#elif defined(_GNU_SOURCE)
+ char cbuf[1024];
+ char *buf = strerror_r(errno, cbuf, sizeof(cbuf));
+ if (!buf) {
+ task->fail(1);
+ return NULL;
+ }
+#else
+ char buf[1024];
+ int err = strerror_r(errno, buf, sizeof(buf));
+ if (err) {
+ task->fail(1);
+ return NULL;
+ }
+#endif
+ size_t fill = strlen(buf) + 1;
+ size_t alloc = next_power_of_two(sizeof(rust_str) + fill);
+ void *mem = dom->malloc(alloc);
+ if (!mem) {
+ task->fail(1);
+ return NULL;
+ }
+ rust_str *st = new (mem) rust_str(dom, alloc, fill, (const uint8_t *)buf);
+
+#ifdef __WIN32__
+ LocalFree((HLOCAL)buf);
+#endif
+ return st;
+}
+
+extern "C" CDECL size_t
+size_of(rust_task *task, type_desc *t) {
+ return t->size;
+}
+
+extern "C" CDECL size_t
+align_of(rust_task *task, type_desc *t) {
+ return t->align;
+}
+
+extern "C" CDECL size_t
+refcount(rust_task *task, type_desc *t, size_t *v) {
+ // Passed-in value has refcount 1 too high
+ // because it was ref'ed while making the call.
+ return (*v) - 1;
+}
+
+extern "C" CDECL rust_vec*
+vec_alloc(rust_task *task, type_desc *t, size_t n_elts)
+{
+ rust_dom *dom = task->dom;
+ dom->log(rust_log::MEM,
+ "vec_alloc %" PRIdPTR " elements of size %" PRIdPTR,
+ n_elts, t->size);
+ size_t fill = n_elts * t->size;
+ size_t alloc = next_power_of_two(sizeof(rust_vec) + fill);
+ void *mem = dom->malloc(alloc);
+ if (!mem) {
+ task->fail(3);
+ return NULL;
+ }
+ rust_vec *vec = new (mem) rust_vec(dom, alloc, 0, NULL);
+ return vec;
+}
+
+extern "C" CDECL char const *
+str_buf(rust_task *task, rust_str *s)
+{
+ return (char const *)&s->data[0];
+}
+
+extern "C" CDECL void *
+vec_buf(rust_task *task, type_desc *ty, rust_vec *v)
+{
+ return (void *)&v->data[0];
+}
+
+extern "C" CDECL size_t
+vec_len(rust_task *task, type_desc *ty, rust_vec *v)
+{
+ return v->fill;
+}
+
+//
+// Local Variables:
+// mode: C++
+// 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/rt/rust_chan.cpp b/src/rt/rust_chan.cpp
new file mode 100644
index 00000000..38f93a7d
--- /dev/null
+++ b/src/rt/rust_chan.cpp
@@ -0,0 +1,34 @@
+
+#include "rust_internal.h"
+#include "rust_chan.h"
+
+rust_chan::rust_chan(rust_task *task, rust_port *port) :
+ task(task),
+ port(port),
+ buffer(task->dom, port->unit_sz),
+ token(this)
+{
+ if (port)
+ port->chans.push(this);
+}
+
+rust_chan::~rust_chan()
+{
+ if (port) {
+ if (token.pending())
+ token.withdraw();
+ port->chans.swapdel(this);
+ }
+}
+
+void
+rust_chan::disassociate()
+{
+ I(task->dom, port);
+
+ if (token.pending())
+ token.withdraw();
+
+ // Delete reference to the port/
+ port = NULL;
+}
diff --git a/src/rt/rust_chan.h b/src/rt/rust_chan.h
new file mode 100644
index 00000000..a56ba0ca
--- /dev/null
+++ b/src/rt/rust_chan.h
@@ -0,0 +1,22 @@
+
+#ifndef RUST_CHAN_H
+#define RUST_CHAN_H
+
+class rust_chan : public rc_base<rust_chan>, public task_owned<rust_chan> {
+public:
+ rust_chan(rust_task *task, rust_port *port);
+ ~rust_chan();
+
+ rust_task *task;
+ rust_port *port;
+ circ_buf buffer;
+ size_t idx; // Index into port->chans.
+
+ // Token belonging to this chan, it will be placed into a port's
+ // writers vector if we have something to send to the port.
+ rust_token token;
+
+ void disassociate();
+};
+
+#endif /* RUST_CHAN_H */
diff --git a/src/rt/rust_comm.cpp b/src/rt/rust_comm.cpp
new file mode 100644
index 00000000..58b9ef4c
--- /dev/null
+++ b/src/rt/rust_comm.cpp
@@ -0,0 +1,199 @@
+
+#include "rust_internal.h"
+
+template class ptr_vec<rust_token>;
+template class ptr_vec<rust_alarm>;
+template class ptr_vec<rust_chan>;
+
+rust_alarm::rust_alarm(rust_task *receiver) :
+ receiver(receiver)
+{
+}
+
+
+// Circular buffers.
+
+circ_buf::circ_buf(rust_dom *dom, size_t unit_sz) :
+ dom(dom),
+ alloc(INIT_CIRC_BUF_UNITS * unit_sz),
+ unit_sz(unit_sz),
+ next(0),
+ unread(0),
+ data((uint8_t *)dom->calloc(alloc))
+{
+ I(dom, unit_sz);
+ dom->log(rust_log::MEM|rust_log::COMM,
+ "new circ_buf(alloc=%d, unread=%d) -> circ_buf=0x%" PRIxPTR,
+ alloc, unread, this);
+ I(dom, data);
+}
+
+circ_buf::~circ_buf()
+{
+ dom->log(rust_log::MEM|rust_log::COMM,
+ "~circ_buf 0x%" PRIxPTR,
+ this);
+ I(dom, data);
+ // I(dom, unread == 0);
+ dom->free(data);
+}
+
+void
+circ_buf::transfer(void *dst)
+{
+ size_t i;
+ uint8_t *d = (uint8_t *)dst;
+ I(dom, dst);
+ for (i = 0; i < unread; i += unit_sz)
+ memcpy(&d[i], &data[next + i % alloc], unit_sz);
+}
+
+void
+circ_buf::push(void *src)
+{
+ size_t i;
+ void *tmp;
+
+ I(dom, src);
+ I(dom, unread <= alloc);
+
+ /* Grow if necessary. */
+ if (unread == alloc) {
+ I(dom, alloc <= MAX_CIRC_BUF_SIZE);
+ tmp = dom->malloc(alloc << 1);
+ transfer(tmp);
+ alloc <<= 1;
+ dom->free(data);
+ data = (uint8_t *)tmp;
+ }
+
+ dom->log(rust_log::MEM|rust_log::COMM,
+ "circ buf push, unread=%d, alloc=%d, unit_sz=%d",
+ unread, alloc, unit_sz);
+
+ I(dom, unread < alloc);
+ I(dom, unread + unit_sz <= alloc);
+
+ i = (next + unread) % alloc;
+ memcpy(&data[i], src, unit_sz);
+
+ dom->log(rust_log::MEM|rust_log::COMM, "pushed data at index %d", i);
+ unread += unit_sz;
+}
+
+void
+circ_buf::shift(void *dst)
+{
+ size_t i;
+ void *tmp;
+
+ I(dom, dst);
+ I(dom, unit_sz > 0);
+ I(dom, unread >= unit_sz);
+ I(dom, unread <= alloc);
+ I(dom, data);
+ i = next;
+ memcpy(dst, &data[i], unit_sz);
+ dom->log(rust_log::MEM|rust_log::COMM, "shifted data from index %d", i);
+ unread -= unit_sz;
+ next += unit_sz;
+ I(dom, next <= alloc);
+ if (next == alloc)
+ next = 0;
+
+ /* Shrink if necessary. */
+ if (alloc >= INIT_CIRC_BUF_UNITS * unit_sz &&
+ unread <= alloc / 4) {
+ tmp = dom->malloc(alloc / 2);
+ transfer(tmp);
+ alloc >>= 1;
+ dom->free(data);
+ data = (uint8_t *)tmp;
+ }
+}
+
+
+// Ports.
+
+rust_port::rust_port(rust_task *task, size_t unit_sz) :
+ task(task),
+ unit_sz(unit_sz),
+ writers(task->dom),
+ chans(task->dom)
+{
+ rust_dom *dom = task->dom;
+ dom->log(rust_log::MEM|rust_log::COMM,
+ "new rust_port(task=0x%" PRIxPTR ", unit_sz=%d) -> port=0x%"
+ PRIxPTR, (uintptr_t)task, unit_sz, (uintptr_t)this);
+}
+
+rust_port::~rust_port()
+{
+ rust_dom *dom = task->dom;
+ dom->log(rust_log::COMM|rust_log::MEM,
+ "~rust_port 0x%" PRIxPTR,
+ (uintptr_t)this);
+ while (chans.length() > 0)
+ chans.pop()->disassociate();
+}
+
+
+// Tokens.
+
+rust_token::rust_token(rust_chan *chan) :
+ chan(chan),
+ idx(0),
+ submitted(false)
+{
+}
+
+rust_token::~rust_token()
+{
+}
+
+bool
+rust_token::pending() const
+{
+ return submitted;
+}
+
+void
+rust_token::submit()
+{
+ rust_port *port = chan->port;
+ rust_dom *dom = chan->task->dom;
+
+ I(dom, port);
+ I(dom, !submitted);
+
+ port->writers.push(this);
+ submitted = true;
+}
+
+void
+rust_token::withdraw()
+{
+ rust_task *task = chan->task;
+ rust_port *port = chan->port;
+ rust_dom *dom = task->dom;
+
+ I(dom, port);
+ I(dom, submitted);
+
+ if (task->blocked())
+ task->wakeup(this); // must be blocked on us (or dead)
+ port->writers.swapdel(this);
+ submitted = false;
+}
+
+
+//
+// Local Variables:
+// mode: C++
+// 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/rt/rust_crate.cpp b/src/rt/rust_crate.cpp
new file mode 100644
index 00000000..d609ac64
--- /dev/null
+++ b/src/rt/rust_crate.cpp
@@ -0,0 +1,63 @@
+
+#include "rust_internal.h"
+
+uintptr_t
+rust_crate::get_image_base() const {
+ return ((uintptr_t)this + image_base_off);
+}
+
+ptrdiff_t
+rust_crate::get_relocation_diff() const {
+ return ((uintptr_t)this - self_addr);
+}
+
+activate_glue_ty
+rust_crate::get_activate_glue() const {
+ return (activate_glue_ty) ((uintptr_t)this + activate_glue_off);
+}
+
+uintptr_t
+rust_crate::get_exit_task_glue() const {
+ return ((uintptr_t)this + exit_task_glue_off);
+}
+
+uintptr_t
+rust_crate::get_unwind_glue() const {
+ return ((uintptr_t)this + unwind_glue_off);
+}
+
+uintptr_t
+rust_crate::get_yield_glue() const {
+ return ((uintptr_t)this + yield_glue_off);
+}
+
+rust_crate::mem_area::mem_area(rust_dom *dom, uintptr_t pos, size_t sz)
+ : dom(dom),
+ base(pos),
+ lim(pos + sz)
+{
+ dom->log(rust_log::MEM, "new mem_area [0x%" PRIxPTR ",0x%" PRIxPTR "]",
+ base, lim);
+}
+
+rust_crate::mem_area
+rust_crate::get_debug_info(rust_dom *dom) const {
+ return mem_area(dom, ((uintptr_t)this + debug_info_off),
+ debug_info_sz);
+}
+
+rust_crate::mem_area
+rust_crate::get_debug_abbrev(rust_dom *dom) const {
+ return mem_area(dom, ((uintptr_t)this + debug_abbrev_off),
+ debug_abbrev_sz);
+}
+
+//
+// Local Variables:
+// mode: C++
+// 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/rt/rust_crate_cache.cpp b/src/rt/rust_crate_cache.cpp
new file mode 100644
index 00000000..fa10b91b
--- /dev/null
+++ b/src/rt/rust_crate_cache.cpp
@@ -0,0 +1,306 @@
+
+#include "rust_internal.h"
+
+rust_crate_cache::lib::lib(rust_dom *dom, char const *name)
+ : handle(0),
+ dom(dom)
+{
+#if defined(__WIN32__)
+ handle = (uintptr_t)LoadLibrary(_T(name));
+#else
+ handle = (uintptr_t)dlopen(name, RTLD_LOCAL|RTLD_LAZY);
+#endif
+ dom->log(rust_log::CACHE, "loaded library '%s' as 0x%" PRIxPTR,
+ name, handle);
+}
+
+rust_crate_cache::lib::~lib() {
+ dom->log(rust_log::CACHE, "~rust_crate_cache::lib(0x%" PRIxPTR ")",
+ handle);
+ if (handle) {
+#if defined(__WIN32__)
+ FreeLibrary((HMODULE)handle);
+#else
+ dlclose((void*)handle);
+#endif
+ }
+}
+
+uintptr_t
+rust_crate_cache::lib::get_handle() {
+ return handle;
+}
+
+
+
+rust_crate_cache::c_sym::c_sym(rust_dom *dom, lib *library, char const *name)
+ : val(0),
+ library(library),
+ dom(dom)
+{
+ library->ref();
+ uintptr_t handle = library->get_handle();
+ if (handle) {
+#if defined(__WIN32__)
+ val = (uintptr_t)GetProcAddress((HMODULE)handle, _T(name));
+#else
+ val = (uintptr_t)dlsym((void*)handle, name);
+#endif
+ dom->log(rust_log::CACHE, "resolved symbol '%s' to 0x%" PRIxPTR,
+ name, val);
+ } else {
+ dom->log(rust_log::CACHE, "unresolved symbol '%s', null lib handle",
+ name);
+ }
+}
+
+rust_crate_cache::c_sym::~c_sym() {
+ dom->log(rust_log::CACHE,
+ "~rust_crate_cache::c_sym(0x%" PRIxPTR ")", val);
+ library->deref();
+}
+
+uintptr_t
+rust_crate_cache::c_sym::get_val() {
+ return val;
+}
+
+
+
+rust_crate_cache::rust_sym::rust_sym(rust_dom *dom,
+ rust_crate const *curr_crate,
+ c_sym *crate_sym,
+ char const **path)
+ : val(0),
+ crate_sym(crate_sym),
+ dom(dom)
+{
+ crate_sym->ref();
+ typedef rust_crate_reader::die die;
+ rust_crate const *crate = (rust_crate*)crate_sym->get_val();
+ if (!crate) {
+ dom->log(rust_log::CACHE,
+ "failed to resolve symbol, null crate symbol");
+ return;
+ }
+ rust_crate_reader rdr(dom, crate);
+ bool found_root = false;
+ bool found_leaf = false;
+ for (die d = rdr.dies.first_die();
+ !(found_root || d.is_null());
+ d = d.next_sibling()) {
+
+ die t1 = d;
+ die t2 = d;
+ for (char const **c = crate_rel(curr_crate, path);
+ (*c
+ && !t1.is_null()
+ && t1.find_child_by_name(crate_rel(curr_crate, *c), t2));
+ ++c, t1=t2) {
+ dom->log(rust_log::DWARF|rust_log::CACHE,
+ "matched die <0x%" PRIxPTR
+ ">, child '%s' = die<0x%" PRIxPTR ">",
+ t1.off, crate_rel(curr_crate, *c), t2.off);
+ found_root = found_root || true;
+ if (!*(c+1) && t2.find_num_attr(DW_AT_low_pc, val)) {
+ dom->log(rust_log::DWARF|rust_log::CACHE,
+ "found relative address: 0x%" PRIxPTR, val);
+ dom->log(rust_log::DWARF|rust_log::CACHE,
+ "plus image-base 0x%" PRIxPTR,
+ crate->get_image_base());
+ val += crate->get_image_base();
+ found_leaf = true;
+ break;
+ }
+ }
+ if (found_root || found_leaf)
+ break;
+ }
+ if (found_leaf) {
+ dom->log(rust_log::CACHE, "resolved symbol to 0x%" PRIxPTR, val);
+ } else {
+ dom->log(rust_log::CACHE, "failed to resolve symbol");
+ }
+}
+
+rust_crate_cache::rust_sym::~rust_sym() {
+ dom->log(rust_log::CACHE,
+ "~rust_crate_cache::rust_sym(0x%" PRIxPTR ")", val);
+ crate_sym->deref();
+}
+
+uintptr_t
+rust_crate_cache::rust_sym::get_val() {
+ return val;
+}
+
+
+
+rust_crate_cache::lib *
+rust_crate_cache::get_lib(size_t n, char const *name)
+{
+ I(dom, n < crate->n_libs);
+ lib *library = libs[n];
+ if (!library) {
+ library = new (dom) lib(dom, name);
+ libs[n] = library;
+ }
+ return library;
+}
+
+rust_crate_cache::c_sym *
+rust_crate_cache::get_c_sym(size_t n, lib *library, char const *name)
+{
+ I(dom, n < crate->n_c_syms);
+ c_sym *sym = c_syms[n];
+ dom->log(rust_log::CACHE, "cached C symbol %s = 0x%" PRIxPTR, name, sym);
+ if (!sym) {
+ sym = new (dom) c_sym(dom, library, name);
+ c_syms[n] = sym;
+ }
+ return sym;
+}
+
+rust_crate_cache::rust_sym *
+rust_crate_cache::get_rust_sym(size_t n,
+ rust_dom *dom,
+ rust_crate const *curr_crate,
+ c_sym *crate_sym,
+ char const **path)
+{
+ I(dom, n < crate->n_rust_syms);
+ rust_sym *sym = rust_syms[n];
+ if (!sym) {
+ sym = new (dom) rust_sym(dom, curr_crate, crate_sym, path);
+ rust_syms[n] = sym;
+ }
+ return sym;
+}
+
+static inline void
+adjust_disp(uintptr_t &disp, const void *oldp, const void *newp)
+{
+ if (disp) {
+ disp += (uintptr_t)oldp;
+ disp -= (uintptr_t)newp;
+ }
+}
+
+type_desc *
+rust_crate_cache::get_type_desc(size_t size,
+ size_t align,
+ size_t n_descs,
+ type_desc const **descs)
+{
+ I(dom, n_descs > 1);
+ type_desc *td = NULL;
+ size_t keysz = n_descs * sizeof(type_desc*);
+ HASH_FIND(hh, this->type_descs, descs, keysz, td);
+ if (td) {
+ dom->log(rust_log::CACHE, "rust_crate_cache::get_type_desc hit");
+ return td;
+ }
+ dom->log(rust_log::CACHE, "rust_crate_cache::get_type_desc miss");
+ td = (type_desc*) dom->malloc(sizeof(type_desc) + keysz);
+ if (!td)
+ return NULL;
+ // By convention, desc 0 is the root descriptor.
+ // but we ignore the size and alignment of it and use the
+ // passed-in, computed values.
+ memcpy(td, descs[0], sizeof(type_desc));
+ td->first_param = &td->descs[1];
+ td->size = size;
+ td->align = align;
+ for (size_t i = 0; i < n_descs; ++i) {
+ dom->log(rust_log::CACHE,
+ "rust_crate_cache::descs[%" PRIdPTR "] = 0x%" PRIxPTR,
+ i, descs[i]);
+ td->descs[i] = descs[i];
+ }
+ adjust_disp(td->copy_glue_off, descs[0], td);
+ adjust_disp(td->drop_glue_off, descs[0], td);
+ adjust_disp(td->free_glue_off, descs[0], td);
+ adjust_disp(td->mark_glue_off, descs[0], td);
+ adjust_disp(td->obj_drop_glue_off, descs[0], td);
+ HASH_ADD(hh, this->type_descs, descs, keysz, td);
+ return td;
+}
+
+rust_crate_cache::rust_crate_cache(rust_dom *dom,
+ rust_crate const *crate)
+ : rust_syms((rust_sym**)
+ dom->calloc(sizeof(rust_sym*) * crate->n_rust_syms)),
+ c_syms((c_sym**) dom->calloc(sizeof(c_sym*) * crate->n_c_syms)),
+ libs((lib**) dom->calloc(sizeof(lib*) * crate->n_libs)),
+ type_descs(NULL),
+ crate(crate),
+ dom(dom),
+ idx(0)
+{
+ I(dom, rust_syms);
+ I(dom, c_syms);
+ I(dom, libs);
+}
+
+void
+rust_crate_cache::flush() {
+ dom->log(rust_log::CACHE, "rust_crate_cache::flush()");
+ for (size_t i = 0; i < crate->n_rust_syms; ++i) {
+ rust_sym *s = rust_syms[i];
+ if (s) {
+ dom->log(rust_log::CACHE,
+ "rust_crate_cache::flush() deref rust_sym %"
+ PRIdPTR " (rc=%" PRIdPTR ")", i, s->refcnt);
+ s->deref();
+ }
+ rust_syms[i] = NULL;
+ }
+
+ for (size_t i = 0; i < crate->n_c_syms; ++i) {
+ c_sym *s = c_syms[i];
+ if (s) {
+ dom->log(rust_log::CACHE,
+ "rust_crate_cache::flush() deref c_sym %"
+ PRIdPTR " (rc=%" PRIdPTR ")", i, s->refcnt);
+ s->deref();
+ }
+ c_syms[i] = NULL;
+ }
+
+ for (size_t i = 0; i < crate->n_libs; ++i) {
+ lib *l = libs[i];
+ if (l) {
+ dom->log(rust_log::CACHE, "rust_crate_cache::flush() deref lib %"
+ PRIdPTR " (rc=%" PRIdPTR ")", i, l->refcnt);
+ l->deref();
+ }
+ libs[i] = NULL;
+ }
+
+ while (type_descs) {
+ type_desc *d = type_descs;
+ HASH_DEL(type_descs, d);
+ dom->log(rust_log::MEM,
+ "rust_crate_cache::flush() tydesc %" PRIxPTR, d);
+ dom->free(d);
+ }
+}
+
+rust_crate_cache::~rust_crate_cache()
+{
+ flush();
+ dom->free(rust_syms);
+ dom->free(c_syms);
+ dom->free(libs);
+}
+
+//
+// Local Variables:
+// mode: C++
+// 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/rt/rust_crate_reader.cpp b/src/rt/rust_crate_reader.cpp
new file mode 100644
index 00000000..3c36729f
--- /dev/null
+++ b/src/rt/rust_crate_reader.cpp
@@ -0,0 +1,578 @@
+
+#include "rust_internal.h"
+
+bool
+rust_crate_reader::mem_reader::is_ok()
+{
+ return ok;
+}
+
+bool
+rust_crate_reader::mem_reader::at_end()
+{
+ return pos == mem.lim;
+}
+
+void
+rust_crate_reader::mem_reader::fail()
+{
+ ok = false;
+}
+
+void
+rust_crate_reader::mem_reader::reset()
+{
+ pos = mem.base;
+ ok = true;
+}
+
+rust_crate_reader::mem_reader::mem_reader(rust_crate::mem_area &m)
+ : mem(m),
+ ok(true),
+ pos(m.base)
+{}
+
+size_t
+rust_crate_reader::mem_reader::tell_abs()
+{
+ return pos;
+}
+
+size_t
+rust_crate_reader::mem_reader::tell_off()
+{
+ return pos - mem.base;
+}
+
+void
+rust_crate_reader::mem_reader::seek_abs(uintptr_t p)
+{
+ if (!ok || p < mem.base || p >= mem.lim)
+ ok = false;
+ else
+ pos = p;
+}
+
+void
+rust_crate_reader::mem_reader::seek_off(uintptr_t p)
+{
+ seek_abs(p + mem.base);
+}
+
+
+bool
+rust_crate_reader::mem_reader::adv_zstr(size_t sz)
+{
+ sz = 0;
+ while (ok) {
+ char c;
+ get(c);
+ ++sz;
+ if (c == '\0')
+ return true;
+ }
+ return false;
+}
+
+bool
+rust_crate_reader::mem_reader::get_zstr(char const *&c, size_t &sz)
+{
+ if (!ok)
+ return false;
+ c = (char const *)(pos);
+ return adv_zstr(sz);
+}
+
+void
+rust_crate_reader::mem_reader::adv(size_t amt)
+{
+ if (pos < mem.base
+ || pos >= mem.lim
+ || pos + amt > mem.lim)
+ ok = false;
+ if (!ok)
+ return;
+ // mem.dom->log(rust_log::MEM, "adv %d bytes", amt);
+ pos += amt;
+ ok &= !at_end();
+ I(mem.dom, at_end() || (mem.base <= pos && pos < mem.lim));
+}
+
+
+rust_crate_reader::abbrev::abbrev(rust_dom *dom,
+ uintptr_t body_off,
+ size_t body_sz,
+ uintptr_t tag,
+ uint8_t has_children) :
+ dom(dom),
+ body_off(body_off),
+ tag(tag),
+ has_children(has_children),
+ idx(0)
+{}
+
+
+rust_crate_reader::abbrev_reader::abbrev_reader
+ (rust_crate::mem_area &abbrev_mem)
+ : mem_reader(abbrev_mem),
+ abbrevs(abbrev_mem.dom)
+{
+ rust_dom *dom = mem.dom;
+ while (is_ok()) {
+
+ // dom->log(rust_log::DWARF, "reading new abbrev at 0x%" PRIxPTR,
+ // tell_off());
+
+ uintptr_t idx, tag;
+ uint8_t has_children;
+ get_uleb(idx);
+ get_uleb(tag);
+ get(has_children);
+
+ uintptr_t attr, form;
+ size_t body_off = tell_off();
+ while (is_ok() && step_attr_form_pair(attr, form));
+
+ // dom->log(rust_log::DWARF,
+ // "finished scanning attr/form pairs, pos=0x%"
+ // PRIxPTR ", lim=0x%" PRIxPTR ", is_ok=%d, at_end=%d",
+ // pos, mem.lim, is_ok(), at_end());
+
+ if (is_ok() || at_end()) {
+ dom->log(rust_log::DWARF, "read abbrev: %" PRIdPTR, idx);
+ I(dom, idx = abbrevs.length() + 1);
+ abbrevs.push(new (dom) abbrev(dom, body_off,
+ tell_off() - body_off,
+ tag, has_children));
+ }
+ }
+}
+
+rust_crate_reader::abbrev *
+rust_crate_reader::abbrev_reader::get_abbrev(size_t i) {
+ i -= 1;
+ if (i < abbrevs.length())
+ return abbrevs[i];
+ return NULL;
+}
+
+bool
+rust_crate_reader::abbrev_reader::step_attr_form_pair(uintptr_t &attr,
+ uintptr_t &form)
+{
+ attr = 0;
+ form = 0;
+ // mem.dom->log(rust_log::DWARF, "reading attr/form pair at 0x%" PRIxPTR,
+ // tell_off());
+ get_uleb(attr);
+ get_uleb(form);
+ // mem.dom->log(rust_log::DWARF, "attr 0x%" PRIxPTR ", form 0x%" PRIxPTR,
+ // attr, form);
+ return ! (attr == 0 && form == 0);
+}
+rust_crate_reader::abbrev_reader::~abbrev_reader() {
+ while (abbrevs.length()) {
+ delete abbrevs.pop();
+ }
+}
+
+
+bool
+rust_crate_reader::attr::is_numeric() const
+{
+ switch (form) {
+ case DW_FORM_ref_addr:
+ case DW_FORM_addr:
+ case DW_FORM_data4:
+ case DW_FORM_data1:
+ case DW_FORM_flag:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+bool
+rust_crate_reader::attr::is_string() const
+{
+ return form == DW_FORM_string;
+}
+
+size_t
+rust_crate_reader::attr::get_ssz(rust_dom *dom) const
+{
+ I(dom, is_string());
+ return val.str.sz;
+}
+
+char const *
+rust_crate_reader::attr::get_str(rust_dom *dom) const
+{
+ I(dom, is_string());
+ return val.str.s;
+}
+
+uintptr_t
+rust_crate_reader::attr::get_num(rust_dom *dom) const
+{
+ I(dom, is_numeric());
+ return val.num;
+}
+
+bool
+rust_crate_reader::attr::is_unknown() const {
+ return !(is_numeric() || is_string());
+}
+
+rust_crate_reader::rdr_sess::rdr_sess(die_reader *rdr) : rdr(rdr)
+{
+ I(rdr->mem.dom, !rdr->in_use);
+ rdr->in_use = true;
+}
+
+rust_crate_reader::rdr_sess::~rdr_sess()
+{
+ rdr->in_use = false;
+}
+
+rust_crate_reader::die::die(die_reader *rdr, uintptr_t off)
+ : rdr(rdr),
+ off(off),
+ using_rdr(false)
+{
+ rust_dom *dom = rdr->mem.dom;
+ rdr_sess use(rdr);
+
+ rdr->reset();
+ rdr->seek_off(off);
+ if (!rdr->is_ok()) {
+ ab = NULL;
+ return;
+ }
+ size_t ab_idx;
+ rdr->get_uleb(ab_idx);
+ if (!ab_idx) {
+ ab = NULL;
+ dom->log(rust_log::DWARF, "DIE <0x%" PRIxPTR "> (null)", off);
+ } else {
+ ab = rdr->abbrevs.get_abbrev(ab_idx);
+ dom->log(rust_log::DWARF, "DIE <0x%" PRIxPTR "> abbrev 0x%"
+ PRIxPTR, off, ab_idx);
+ dom->log(rust_log::DWARF, " tag 0x%x, has children: %d",
+ ab->tag, ab->has_children);
+ }
+}
+
+bool
+rust_crate_reader::die::is_null() const
+{
+ return ab == NULL;
+}
+
+bool
+rust_crate_reader::die::has_children() const
+{
+ return (!is_null()) && ab->has_children;
+}
+
+dw_tag
+rust_crate_reader::die::tag() const
+{
+ if (is_null())
+ return (dw_tag) (-1);
+ return (dw_tag) ab->tag;
+}
+
+bool
+rust_crate_reader::die::start_attrs() const
+{
+ if (is_null())
+ return false;
+ rdr->reset();
+ rdr->seek_off(off + 1);
+ rdr->abbrevs.reset();
+ rdr->abbrevs.seek_off(ab->body_off);
+ return rdr->is_ok();
+}
+
+bool
+rust_crate_reader::die::step_attr(attr &a) const
+{
+ uintptr_t ai, fi;
+ if (rdr->abbrevs.step_attr_form_pair(ai, fi) && rdr->is_ok()) {
+ a.at = (dw_at)ai;
+ a.form = (dw_form)fi;
+
+ uint32_t u32;
+ uint8_t u8;
+
+ switch (a.form) {
+ case DW_FORM_string:
+ return rdr->get_zstr(a.val.str.s, a.val.str.sz);
+ break;
+
+ case DW_FORM_ref_addr:
+ I(rdr->mem.dom, sizeof(uintptr_t) == 4);
+ case DW_FORM_addr:
+ case DW_FORM_data4:
+ rdr->get(u32);
+ a.val.num = (uintptr_t)u32;
+ return rdr->is_ok() || rdr->at_end();
+ break;
+
+ case DW_FORM_data1:
+ case DW_FORM_flag:
+ rdr->get(u8);
+ a.val.num = u8;
+ return rdr->is_ok() || rdr->at_end();
+ break;
+
+ case DW_FORM_block1:
+ rdr->get(u8);
+ rdr->adv(u8);
+ return rdr->is_ok() || rdr->at_end();
+ break;
+
+ default:
+ rdr->mem.dom->log(rust_log::DWARF, " unknown dwarf form: 0x%"
+ PRIxPTR, a.form);
+ rdr->fail();
+ break;
+ }
+ }
+ return false;
+}
+
+bool
+rust_crate_reader::die::find_str_attr(dw_at at, char const *&c)
+{
+ rdr_sess use(rdr);
+ if (is_null())
+ return false;
+ if (start_attrs()) {
+ attr a;
+ while (step_attr(a)) {
+ if (a.at == at && a.is_string()) {
+ c = a.get_str(rdr->mem.dom);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool
+rust_crate_reader::die::find_num_attr(dw_at at, uintptr_t &n)
+{
+ rdr_sess use(rdr);
+ if (is_null())
+ return false;
+ if (start_attrs()) {
+ attr a;
+ while (step_attr(a)) {
+ if (a.at == at && a.is_numeric()) {
+ n = a.get_num(rdr->mem.dom);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool
+rust_crate_reader::die::is_transparent()
+{
+ // "semantically transparent" DIEs are those with
+ // children that serve to structure the tree but have
+ // tags that don't reflect anything in the rust-module
+ // name hierarchy.
+ switch (tag()) {
+ case DW_TAG_compile_unit:
+ case DW_TAG_lexical_block:
+ return (has_children());
+ default:
+ break;
+ }
+ return false;
+}
+
+bool
+rust_crate_reader::die::find_child_by_name(char const *c,
+ die &child,
+ bool exact)
+{
+ rust_dom *dom = rdr->mem.dom;
+ I(dom, has_children());
+ I(dom, !is_null());
+
+ for (die ch = next(); !ch.is_null(); ch = ch.next_sibling()) {
+ char const *ac;
+ if (!exact && ch.is_transparent()) {
+ if (ch.find_child_by_name(c, child, exact)) {
+ return true;
+ }
+ }
+ else if (ch.find_str_attr(DW_AT_name, ac)) {
+ if (strcmp(ac, c) == 0) {
+ child = ch;
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool
+rust_crate_reader::die::find_child_by_tag(dw_tag tag, die &child)
+{
+ rust_dom *dom = rdr->mem.dom;
+ I(dom, has_children());
+ I(dom, !is_null());
+
+ for (child = next(); !child.is_null();
+ child = child.next_sibling()) {
+ if (child.tag() == tag)
+ return true;
+ }
+ return false;
+}
+
+rust_crate_reader::die
+rust_crate_reader::die::next() const
+{
+ rust_dom *dom = rdr->mem.dom;
+
+ if (is_null()) {
+ rdr->seek_off(off + 1);
+ return die(rdr, rdr->tell_off());
+ }
+
+ {
+ rdr_sess use(rdr);
+ if (start_attrs()) {
+ attr a;
+ while (step_attr(a)) {
+ I(dom, !(a.is_numeric() && a.is_string()));
+ if (a.is_numeric())
+ dom->log(rust_log::DWARF, " attr num: 0x%"
+ PRIxPTR, a.get_num(dom));
+ else if (a.is_string())
+ dom->log(rust_log::DWARF, " attr str: %s",
+ a.get_str(dom));
+ else
+ dom->log(rust_log::DWARF, " attr ??:");
+ }
+ }
+ }
+ return die(rdr, rdr->tell_off());
+}
+
+rust_crate_reader::die
+rust_crate_reader::die::next_sibling() const
+{
+ // FIXME: use DW_AT_sibling, when present.
+ if (has_children()) {
+ // rdr->mem.dom->log(rust_log::DWARF, "+++ children of die 0x%"
+ // PRIxPTR, off);
+ die child = next();
+ while (!child.is_null())
+ child = child.next_sibling();
+ // rdr->mem.dom->log(rust_log::DWARF, "--- children of die 0x%"
+ // PRIxPTR, off);
+ return child.next();
+ } else {
+ return next();
+ }
+}
+
+
+rust_crate_reader::die
+rust_crate_reader::die_reader::first_die()
+{
+ reset();
+ seek_off(cu_base
+ + sizeof(dwarf_vers)
+ + sizeof(cu_abbrev_off)
+ + sizeof(sizeof_addr));
+ return die(this, tell_off());
+}
+
+void
+rust_crate_reader::die_reader::dump()
+{
+ rust_dom *dom = mem.dom;
+ die d = first_die();
+ while (!d.is_null())
+ d = d.next_sibling();
+ I(dom, d.is_null());
+ I(dom, d.off == mem.lim - mem.base);
+}
+
+
+rust_crate_reader::die_reader::die_reader(rust_crate::mem_area &die_mem,
+ abbrev_reader &abbrevs)
+ : mem_reader(die_mem),
+ abbrevs(abbrevs),
+ cu_unit_length(0),
+ cu_base(0),
+ dwarf_vers(0),
+ cu_abbrev_off(0),
+ sizeof_addr(0),
+ in_use(false)
+{
+ rust_dom *dom = mem.dom;
+
+ rdr_sess use(this);
+
+ get(cu_unit_length);
+ cu_base = tell_off();
+
+ get(dwarf_vers);
+ get(cu_abbrev_off);
+ get(sizeof_addr);
+
+ if (is_ok()) {
+ dom->log(rust_log::DWARF, "new root CU at 0x%" PRIxPTR, die_mem.base);
+ dom->log(rust_log::DWARF, "CU unit length: %" PRId32, cu_unit_length);
+ dom->log(rust_log::DWARF, "dwarf version: %" PRId16, dwarf_vers);
+ dom->log(rust_log::DWARF, "CU abbrev off: %" PRId32, cu_abbrev_off);
+ dom->log(rust_log::DWARF, "size of address: %" PRId8, sizeof_addr);
+ I(dom, sizeof_addr == sizeof(uintptr_t));
+ I(dom, dwarf_vers >= 2);
+ I(dom, cu_base + cu_unit_length == die_mem.lim - die_mem.base);
+ } else {
+ dom->log(rust_log::DWARF, "failed to read root CU header");
+ }
+}
+
+rust_crate_reader::die_reader::~die_reader() {
+}
+
+
+rust_crate_reader::rust_crate_reader(rust_dom *dom,
+ rust_crate const *crate)
+ : dom(dom),
+ crate(crate),
+ abbrev_mem(crate->get_debug_abbrev(dom)),
+ abbrevs(abbrev_mem),
+ die_mem(crate->get_debug_info(dom)),
+ dies(die_mem, abbrevs)
+{
+ dom->log(rust_log::MEM, "crate_reader on crate: 0x%" PRIxPTR, this);
+ dom->log(rust_log::MEM, "debug_abbrev: 0x%" PRIxPTR, abbrev_mem.base);
+ dom->log(rust_log::MEM, "debug_info: 0x%" PRIxPTR, die_mem.base);
+ // For now, perform diagnostics only.
+ dies.dump();
+}
+
+
+//
+// Local Variables:
+// mode: C++
+// 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/rt/rust_dom.cpp b/src/rt/rust_dom.cpp
new file mode 100644
index 00000000..3b5e23b2
--- /dev/null
+++ b/src/rt/rust_dom.cpp
@@ -0,0 +1,271 @@
+
+#include <stdarg.h>
+#include "rust_internal.h"
+
+template class ptr_vec<rust_task>;
+
+rust_dom::rust_dom(rust_srv *srv, rust_crate const *root_crate) :
+ interrupt_flag(0),
+ root_crate(root_crate),
+ _log(srv, this),
+ srv(srv),
+ running_tasks(this),
+ blocked_tasks(this),
+ dead_tasks(this),
+ caches(this),
+ root_task(NULL),
+ curr_task(NULL),
+ rval(0)
+{
+ logptr("new dom", (uintptr_t)this);
+ memset(&rctx, 0, sizeof(rctx));
+
+#ifdef __WIN32__
+ {
+ HCRYPTPROV hProv;
+ win32_require
+ (_T("CryptAcquireContext"),
+ CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL,
+ CRYPT_VERIFYCONTEXT|CRYPT_SILENT));
+ win32_require
+ (_T("CryptGenRandom"),
+ CryptGenRandom(hProv, sizeof(rctx.randrsl),
+ (BYTE*)(&rctx.randrsl)));
+ win32_require
+ (_T("CryptReleaseContext"),
+ CryptReleaseContext(hProv, 0));
+ }
+#else
+ int fd = open("/dev/urandom", O_RDONLY);
+ I(this, fd > 0);
+ I(this, read(fd, (void*) &rctx.randrsl, sizeof(rctx.randrsl))
+ == sizeof(rctx.randrsl));
+ I(this, close(fd) == 0);
+ pthread_attr_init(&attr);
+ pthread_attr_setstacksize(&attr, 1024 * 1024);
+ pthread_attr_setdetachstate(&attr, true);
+#endif
+ randinit(&rctx, 1);
+
+ root_task = new (this) rust_task(this, NULL);
+}
+
+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);
+ delete v->pop();
+ }
+}
+
+rust_dom::~rust_dom() {
+ log(rust_log::TASK, "deleting all running tasks");
+ del_all_tasks(this, &running_tasks);
+ log(rust_log::TASK, "deleting all blocked tasks");
+ del_all_tasks(this, &blocked_tasks);
+ log(rust_log::TASK, "deleting all dead tasks");
+ del_all_tasks(this, &dead_tasks);
+#ifndef __WIN32__
+ pthread_attr_destroy(&attr);
+#endif
+ while (caches.length())
+ delete caches.pop();
+}
+
+void
+rust_dom::activate(rust_task *task) {
+ curr_task = task;
+ root_crate->get_activate_glue()(task);
+ curr_task = NULL;
+}
+
+void
+rust_dom::log(uint32_t type_bits, char const *fmt, ...) {
+ char buf[256];
+ if (_log.is_tracing(type_bits)) {
+ va_list args;
+ va_start(args, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, args);
+ _log.trace_ln(type_bits, buf);
+ va_end(args);
+ }
+}
+
+rust_log &
+rust_dom::get_log() {
+ return _log;
+}
+
+void
+rust_dom::logptr(char const *msg, uintptr_t ptrval) {
+ log(rust_log::MEM, "%s 0x%" PRIxPTR, msg, ptrval);
+}
+
+template<typename T> void
+rust_dom::logptr(char const *msg, T* ptrval) {
+ log(rust_log::MEM, "%s 0x%" PRIxPTR, msg, (uintptr_t)ptrval);
+}
+
+
+void
+rust_dom::fail() {
+ log(rust_log::DOM, "domain 0x%" PRIxPTR " root task failed", 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, "rust_dom::malloc(%d) -> 0x%" PRIxPTR,
+ sz, p);
+ return p;
+}
+
+void *
+rust_dom::calloc(size_t sz) {
+ void *p = this->malloc(sz);
+ memset(p, 0, sz);
+ return p;
+}
+
+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;
+}
+
+void
+rust_dom::free(void *p) {
+ log(rust_log::MEM, "rust_dom::free(0x%" PRIxPTR ")", p);
+ I(this, p);
+ srv->free(p);
+}
+
+#ifdef __WIN32__
+void
+rust_dom::win32_require(LPCTSTR fn, BOOL ok) {
+ if (!ok) {
+ LPTSTR buf;
+ DWORD err = GetLastError();
+ FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL, err,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR) &buf, 0, NULL );
+ log(rust_log::ERR, "%s failed with error %ld: %s", fn, err, buf);
+ LocalFree((HLOCAL)buf);
+ I(this, ok);
+ }
+}
+#endif
+
+size_t
+rust_dom::n_live_tasks()
+{
+ return running_tasks.length() + blocked_tasks.length();
+}
+
+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);
+ v->push(task);
+}
+
+
+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);
+ I(this, (*v)[task->idx] == task);
+ v->swapdel(task);
+}
+
+const char *
+rust_dom::state_vec_name(ptr_vec<rust_task> *v)
+{
+ if (v == &running_tasks)
+ return "running";
+ if (v == &blocked_tasks)
+ return "blocked";
+ I(this, v == &dead_tasks);
+ return "dead";
+}
+
+void
+rust_dom::reap_dead_tasks()
+{
+ for (size_t i = 0; i < dead_tasks.length(); ) {
+ rust_task *t = dead_tasks[i];
+ if (t == root_task || t->refcnt == 0) {
+ I(this, !t->waiting_tasks.length());
+ dead_tasks.swapdel(t);
+ log(rust_log::TASK,
+ "deleting unreferenced dead task 0x%" PRIxPTR, t);
+ delete t;
+ continue;
+ }
+ ++i;
+ }
+}
+
+rust_task *
+rust_dom::sched()
+{
+ 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];
+ }
+ log(rust_log::DOM|rust_log::TASK,
+ "no schedulable tasks");
+ return NULL;
+}
+
+rust_crate_cache *
+rust_dom::get_cache(rust_crate const *crate) {
+ log(rust_log::CACHE,
+ "looking for crate-cache for crate 0x%" PRIxPTR, crate);
+ rust_crate_cache *cache = NULL;
+ for (size_t i = 0; i < caches.length(); ++i) {
+ rust_crate_cache *c = caches[i];
+ if (c->crate == crate) {
+ cache = c;
+ break;
+ }
+ }
+ if (!cache) {
+ log(rust_log::CACHE,
+ "making new crate-cache for crate 0x%" PRIxPTR, crate);
+ cache = new (this) rust_crate_cache(this, crate);
+ caches.push(cache);
+ }
+ cache->ref();
+ return cache;
+}
+
+
+//
+// Local Variables:
+// mode: C++
+// fill-column: 70;
+// 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/rt/rust_dwarf.h b/src/rt/rust_dwarf.h
new file mode 100644
index 00000000..8eff3b8c
--- /dev/null
+++ b/src/rt/rust_dwarf.h
@@ -0,0 +1,198 @@
+#ifndef RUST_DWARF_H
+#define RUST_DWARF_H
+
+enum
+dw_form
+ {
+ DW_FORM_addr = 0x01,
+ DW_FORM_block2 = 0x03,
+ DW_FORM_block4 = 0x04,
+ DW_FORM_data2 = 0x05,
+ DW_FORM_data4 = 0x06,
+ DW_FORM_data8 = 0x07,
+ DW_FORM_string = 0x08,
+ DW_FORM_block = 0x09,
+ DW_FORM_block1 = 0x0a,
+ DW_FORM_data1 = 0x0b,
+ DW_FORM_flag = 0x0c,
+ DW_FORM_sdata = 0x0d,
+ DW_FORM_strp = 0x0e,
+ DW_FORM_udata = 0x0f,
+ DW_FORM_ref_addr = 0x10,
+ DW_FORM_ref1 = 0x11,
+ DW_FORM_ref2 = 0x12,
+ DW_FORM_ref4 = 0x13,
+ DW_FORM_ref8 = 0x14,
+ DW_FORM_ref_udata = 0x15,
+ DW_FORM_indirect = 0x16
+ };
+
+enum
+dw_at
+ {
+ DW_AT_sibling = 0x01,
+ DW_AT_location = 0x02,
+ DW_AT_name = 0x03,
+ DW_AT_ordering = 0x09,
+ DW_AT_byte_size = 0x0b,
+ DW_AT_bit_offset = 0x0c,
+ DW_AT_bit_size = 0x0d,
+ DW_AT_stmt_list = 0x10,
+ DW_AT_low_pc = 0x11,
+ DW_AT_high_pc = 0x12,
+ DW_AT_language = 0x13,
+ DW_AT_discr = 0x15,
+ DW_AT_discr_value = 0x16,
+ DW_AT_visibility = 0x17,
+ DW_AT_import = 0x18,
+ DW_AT_string_length = 0x19,
+ DW_AT_common_reference = 0x1a,
+ DW_AT_comp_dir = 0x1b,
+ DW_AT_const_value = 0x1c,
+ DW_AT_containing_type = 0x1d,
+ DW_AT_default_value = 0x1e,
+ DW_AT_inline = 0x20,
+ DW_AT_is_optional = 0x21,
+ DW_AT_lower_bound = 0x22,
+ DW_AT_producer = 0x25,
+ DW_AT_prototyped = 0x27,
+ DW_AT_return_addr = 0x2a,
+ DW_AT_start_scope = 0x2c,
+ DW_AT_bit_stride = 0x2e,
+ DW_AT_upper_bound = 0x2f,
+ DW_AT_abstract_origin = 0x31,
+ DW_AT_accessibility = 0x32,
+ DW_AT_address_class = 0x33,
+ DW_AT_artificial = 0x34,
+ DW_AT_base_types = 0x35,
+ DW_AT_calling_convention = 0x36,
+ DW_AT_count = 0x37,
+ DW_AT_data_member_location = 0x38,
+ DW_AT_decl_column = 0x39,
+ DW_AT_decl_file = 0x3a,
+ DW_AT_decl_line = 0x3b,
+ DW_AT_declaration = 0x3c,
+ DW_AT_discr_list = 0x3d,
+ DW_AT_encoding = 0x3e,
+ DW_AT_external = 0x3f,
+ DW_AT_frame_base = 0x40,
+ DW_AT_friend = 0x41,
+ DW_AT_identifier_case = 0x42,
+ DW_AT_macro_info = 0x43,
+ DW_AT_namelist_item = 0x44,
+ DW_AT_priority = 0x45,
+ DW_AT_segment = 0x46,
+ DW_AT_specification = 0x47,
+ DW_AT_static_link = 0x48,
+ DW_AT_type = 0x49,
+ DW_AT_use_location = 0x4a,
+ DW_AT_variable_parameter = 0x4b,
+ DW_AT_virtuality = 0x4c,
+ DW_AT_vtable_elem_location = 0x4d,
+ DW_AT_allocated = 0x4e,
+ DW_AT_associated = 0x4f,
+ DW_AT_data_location = 0x50,
+ DW_AT_byte_stride = 0x51,
+ DW_AT_entry_pc = 0x52,
+ DW_AT_use_UTF8 = 0x53,
+ DW_AT_extension = 0x54,
+ DW_AT_ranges = 0x55,
+ DW_AT_trampoline = 0x56,
+ DW_AT_call_column = 0x57,
+ DW_AT_call_file = 0x58,
+ DW_AT_call_line = 0x59,
+ DW_AT_description = 0x5a,
+ DW_AT_binary_scale = 0x5b,
+ DW_AT_decimal_scale = 0x5c,
+ DW_AT_small = 0x5d,
+ DW_AT_decimal_sign = 0x5e,
+ DW_AT_digit_count = 0x5f,
+ DW_AT_picture_string = 0x60,
+ DW_AT_mutable = 0x61,
+ DW_AT_threads_scaled = 0x62,
+ DW_AT_explicit = 0x63,
+ DW_AT_object_pointer = 0x64,
+ DW_AT_endianity = 0x65,
+ DW_AT_elemental = 0x66,
+ DW_AT_pure = 0x67,
+ DW_AT_recursive = 0x68,
+ DW_AT_lo_user = 0x2000,
+ DW_AT_hi_user = 0x3fff
+};
+
+enum
+dw_tag
+ {
+ DW_TAG_array_type = 0x01,
+ DW_TAG_class_type = 0x02,
+ DW_TAG_entry_point = 0x03,
+ DW_TAG_enumeration_type = 0x04,
+ DW_TAG_formal_parameter = 0x05,
+ DW_TAG_imported_declaration = 0x08,
+ DW_TAG_label = 0x0a,
+ DW_TAG_lexical_block = 0x0b,
+ DW_TAG_member = 0x0d,
+ DW_TAG_pointer_type = 0x0f,
+ DW_TAG_reference_type = 0x10,
+ DW_TAG_compile_unit = 0x11,
+ DW_TAG_string_type = 0x12,
+ DW_TAG_structure_type = 0x13,
+ DW_TAG_subroutine_type = 0x15,
+ DW_TAG_typedef = 0x16,
+ DW_TAG_union_type = 0x17,
+ DW_TAG_unspecified_parameters = 0x18,
+ DW_TAG_variant = 0x19,
+ DW_TAG_common_block = 0x1a,
+ DW_TAG_common_inclusion = 0x1b,
+ DW_TAG_inheritance = 0x1c,
+ DW_TAG_inlined_subroutine = 0x1d,
+ DW_TAG_module = 0x1e,
+ DW_TAG_ptr_to_member_type = 0x1f,
+ DW_TAG_set_type = 0x20,
+ DW_TAG_subrange_type = 0x21,
+ DW_TAG_with_stmt = 0x22,
+ DW_TAG_access_declaration = 0x23,
+ DW_TAG_base_type = 0x24,
+ DW_TAG_catch_block = 0x25,
+ DW_TAG_const_type = 0x26,
+ DW_TAG_constant = 0x27,
+ DW_TAG_enumerator = 0x28,
+ DW_TAG_file_type = 0x29,
+ DW_TAG_friend = 0x2a,
+ DW_TAG_namelist = 0x2b,
+ DW_TAG_namelist_item = 0x2c,
+ DW_TAG_packed_type = 0x2d,
+ DW_TAG_subprogram = 0x2e,
+ DW_TAG_template_type_parameter = 0x2f,
+ DW_TAG_template_value_parameter = 0x30,
+ DW_TAG_thrown_type = 0x31,
+ DW_TAG_try_block = 0x32,
+ DW_TAG_variant_part = 0x33,
+ DW_TAG_variable = 0x34,
+ DW_TAG_volatile_type = 0x35,
+ DW_TAG_dwarf_procedure = 0x36,
+ DW_TAG_restrict_type = 0x37,
+ DW_TAG_interface_type = 0x38,
+ DW_TAG_namespace = 0x39,
+ DW_TAG_imported_module = 0x3a,
+ DW_TAG_unspecified_type = 0x3b,
+ DW_TAG_partial_unit = 0x3c,
+ DW_TAG_imported_unit = 0x3d,
+ DW_TAG_condition = 0x3f,
+ DW_TAG_shared_type = 0x40,
+ DW_TAG_lo_user = 0x4080,
+ DW_TAG_hi_user = 0xffff,
+ };
+
+//
+// Local Variables:
+// mode: C++
+// 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:
+//
+
+#endif
diff --git a/src/rt/rust_internal.h b/src/rt/rust_internal.h
new file mode 100644
index 00000000..c393b210
--- /dev/null
+++ b/src/rt/rust_internal.h
@@ -0,0 +1,730 @@
+#ifndef RUST_INTERNAL_H
+#define RUST_INTERNAL_H
+
+#define __STDC_LIMIT_MACROS 1
+#define __STDC_CONSTANT_MACROS 1
+#define __STDC_FORMAT_MACROS 1
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <inttypes.h>
+
+#include <stdio.h>
+#include <string.h>
+
+#include "rust.h"
+
+#include "rand.h"
+#include "rust_log.h"
+#include "uthash.h"
+
+#if defined(__WIN32__)
+extern "C" {
+#include <windows.h>
+#include <tchar.h>
+#include <wincrypt.h>
+}
+#elif defined(__GNUC__)
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <dlfcn.h>
+#include <pthread.h>
+#include <errno.h>
+#else
+#error "Platform not supported."
+#endif
+
+#ifndef __i386__
+#error "Target CPU not supported."
+#endif
+
+#define I(dom, e) ((e) ? (void)0 : \
+ (dom)->srv->fatal(#e, __FILE__, __LINE__))
+
+struct rust_task;
+struct rust_port;
+class rust_chan;
+struct rust_token;
+struct rust_dom;
+class rust_crate;
+class rust_crate_cache;
+class lockfree_queue;
+
+struct stk_seg;
+struct type_desc;
+struct frame_glue_fns;
+
+// This drives our preemption scheme.
+
+static size_t const TIME_SLICE_IN_MS = 10;
+
+// Every reference counted object should derive from this base class.
+
+template <typename T>
+struct
+rc_base
+{
+ size_t refcnt;
+
+ void ref() {
+ ++refcnt;
+ }
+
+ void deref() {
+ if (--refcnt == 0) {
+ delete (T*)this;
+ }
+ }
+
+ rc_base();
+ ~rc_base();
+};
+
+template <typename T>
+struct
+dom_owned
+{
+ void operator delete(void *ptr) {
+ ((T *)ptr)->dom->free(ptr);
+ }
+};
+
+template <typename T>
+struct
+task_owned
+{
+ void operator delete(void *ptr) {
+ ((T *)ptr)->task->dom->free(ptr);
+ }
+};
+
+
+// Helper class used regularly elsewhere.
+
+template <typename T>
+class
+ptr_vec : public dom_owned<ptr_vec<T> >
+{
+ static const size_t INIT_SIZE = 8;
+
+ rust_dom *dom;
+ size_t alloc;
+ size_t fill;
+ T **data;
+
+public:
+ ptr_vec(rust_dom *dom);
+ ~ptr_vec();
+
+ size_t length() {
+ return fill;
+ }
+
+ T *& operator[](size_t offset);
+ void push(T *p);
+ T *pop();
+ void trim(size_t fill);
+ void swapdel(T* p);
+};
+
+struct
+rust_dom
+{
+ // Fields known to the compiler:
+ uintptr_t interrupt_flag;
+
+ // Fields known only by the runtime:
+
+ // NB: the root crate must remain in memory until the root of the
+ // tree of domains exits. All domains within this tree have a
+ // copy of this root_crate value and use it for finding utility
+ // glue.
+ rust_crate const *root_crate;
+ rust_log _log;
+ rust_srv *srv;
+ // uint32_t logbits;
+ ptr_vec<rust_task> running_tasks;
+ ptr_vec<rust_task> blocked_tasks;
+ ptr_vec<rust_task> dead_tasks;
+ ptr_vec<rust_crate_cache> caches;
+ randctx rctx;
+ rust_task *root_task;
+ rust_task *curr_task;
+ int rval;
+ lockfree_queue *incoming; // incoming messages from other threads
+
+#ifndef __WIN32__
+ pthread_attr_t attr;
+#endif
+
+ rust_dom(rust_srv *srv, rust_crate const *root_crate);
+ ~rust_dom();
+
+ void activate(rust_task *task);
+ void log(uint32_t logbit, char const *fmt, ...);
+ rust_log & get_log();
+ void logptr(char const *msg, uintptr_t ptrval);
+ 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);
+
+#ifdef __WIN32__
+ void win32_require(LPCTSTR fn, BOOL ok);
+#endif
+
+ rust_crate_cache *get_cache(rust_crate const *crate);
+ size_t n_live_tasks();
+ void add_task_to_state_vec(ptr_vec<rust_task> *v, rust_task *task);
+ void remove_task_from_state_vec(ptr_vec<rust_task> *v, rust_task *task);
+ const char *state_vec_name(ptr_vec<rust_task> *v);
+
+ void reap_dead_tasks();
+ rust_task *sched();
+};
+
+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);
+}
+
+struct
+rust_timer
+{
+ // FIXME: This will probably eventually need replacement
+ // with something more sophisticated and integrated with
+ // an IO event-handling library, when we have such a thing.
+ // For now it's just the most basic "thread that can interrupt
+ // its associated domain-thread" device, so that we have
+ // *some* form of task-preemption.
+ rust_dom &dom;
+ uintptr_t exit_flag;
+
+#if defined(__WIN32__)
+ HANDLE thread;
+#else
+ pthread_attr_t attr;
+ pthread_t thread;
+#endif
+
+ rust_timer(rust_dom &dom);
+ ~rust_timer();
+};
+
+#include "rust_util.h"
+
+// Crates.
+
+template<typename T> T*
+crate_rel(rust_crate const *crate, T *t) {
+ return (T*)(((uintptr_t)crate) + ((ptrdiff_t)t));
+}
+
+template<typename T> T const*
+crate_rel(rust_crate const *crate, T const *t) {
+ return (T const*)(((uintptr_t)crate) + ((ptrdiff_t)t));
+}
+
+typedef void CDECL (*activate_glue_ty)(rust_task *);
+
+class
+rust_crate
+{
+ // The following fields are emitted by the compiler for the static
+ // rust_crate object inside each compiled crate.
+
+ ptrdiff_t image_base_off; // (Loaded image base) - this.
+ uintptr_t self_addr; // Un-relocated addres of 'this'.
+
+ ptrdiff_t debug_abbrev_off; // Offset from this to .debug_abbrev.
+ size_t debug_abbrev_sz; // Size of .debug_abbrev.
+
+ ptrdiff_t debug_info_off; // Offset from this to .debug_info.
+ size_t debug_info_sz; // Size of .debug_info.
+
+ ptrdiff_t activate_glue_off;
+ ptrdiff_t exit_task_glue_off;
+ ptrdiff_t unwind_glue_off;
+ ptrdiff_t yield_glue_off;
+
+public:
+
+ size_t n_rust_syms;
+ size_t n_c_syms;
+ size_t n_libs;
+
+ // Crates are immutable, constructed by the compiler.
+
+ uintptr_t get_image_base() const;
+ ptrdiff_t get_relocation_diff() const;
+ activate_glue_ty get_activate_glue() const;
+ uintptr_t get_exit_task_glue() const;
+ uintptr_t get_unwind_glue() const;
+ uintptr_t get_yield_glue() const;
+ struct mem_area
+ {
+ rust_dom *dom;
+ uintptr_t base;
+ uintptr_t lim;
+ mem_area(rust_dom *dom, uintptr_t pos, size_t sz);
+ };
+
+ mem_area get_debug_info(rust_dom *dom) const;
+ mem_area get_debug_abbrev(rust_dom *dom) const;
+};
+
+
+struct type_desc {
+ // First part of type_desc is known to compiler.
+ // first_param = &descs[1] if dynamic, null if static.
+ const type_desc **first_param;
+ size_t size;
+ size_t align;
+ uintptr_t copy_glue_off;
+ uintptr_t drop_glue_off;
+ uintptr_t free_glue_off;
+ uintptr_t mark_glue_off; // For GC.
+ uintptr_t obj_drop_glue_off; // For custom destructors.
+
+ // Residual fields past here are known only to runtime.
+ UT_hash_handle hh;
+ size_t n_descs;
+ const type_desc *descs[];
+};
+
+class
+rust_crate_cache : public dom_owned<rust_crate_cache>,
+ public rc_base<rust_crate_cache>
+{
+public:
+ class lib :
+ public rc_base<lib>, public dom_owned<lib>
+ {
+ uintptr_t handle;
+ public:
+ rust_dom *dom;
+ lib(rust_dom *dom, char const *name);
+ uintptr_t get_handle();
+ ~lib();
+ };
+
+ class c_sym :
+ public rc_base<c_sym>, public dom_owned<c_sym>
+ {
+ uintptr_t val;
+ lib *library;
+ public:
+ rust_dom *dom;
+ c_sym(rust_dom *dom, lib *library, char const *name);
+ uintptr_t get_val();
+ ~c_sym();
+ };
+
+ class rust_sym :
+ public rc_base<rust_sym>, public dom_owned<rust_sym>
+ {
+ uintptr_t val;
+ c_sym *crate_sym;
+ public:
+ rust_dom *dom;
+ rust_sym(rust_dom *dom, rust_crate const *curr_crate,
+ c_sym *crate_sym, char const **path);
+ uintptr_t get_val();
+ ~rust_sym();
+ };
+
+ lib *get_lib(size_t n, char const *name);
+ c_sym *get_c_sym(size_t n, lib *library, char const *name);
+ rust_sym *get_rust_sym(size_t n,
+ rust_dom *dom,
+ rust_crate const *curr_crate,
+ c_sym *crate_sym,
+ char const **path);
+ type_desc *get_type_desc(size_t size,
+ size_t align,
+ size_t n_descs,
+ type_desc const **descs);
+
+private:
+
+ rust_sym **rust_syms;
+ c_sym **c_syms;
+ lib **libs;
+ type_desc *type_descs;
+
+public:
+
+ rust_crate const *crate;
+ rust_dom *dom;
+ size_t idx;
+
+ rust_crate_cache(rust_dom *dom,
+ rust_crate const *crate);
+ ~rust_crate_cache();
+ void flush();
+};
+
+#include "rust_dwarf.h"
+
+class
+rust_crate_reader
+{
+ struct mem_reader
+ {
+ rust_crate::mem_area &mem;
+ bool ok;
+ uintptr_t pos;
+
+ bool is_ok();
+ bool at_end();
+ void fail();
+ void reset();
+ mem_reader(rust_crate::mem_area &m);
+ size_t tell_abs();
+ size_t tell_off();
+ void seek_abs(uintptr_t p);
+ void seek_off(uintptr_t p);
+
+ template<typename T>
+ void get(T &out) {
+ if (pos < mem.base
+ || pos >= mem.lim
+ || pos + sizeof(T) > mem.lim)
+ ok = false;
+ if (!ok)
+ return;
+ out = *((T*)(pos));
+ pos += sizeof(T);
+ ok &= !at_end();
+ I(mem.dom, at_end() || (mem.base <= pos && pos < mem.lim));
+ }
+
+ template<typename T>
+ void get_uleb(T &out) {
+ out = T(0);
+ for (size_t i = 0; i < sizeof(T) && ok; ++i) {
+ uint8_t byte;
+ get(byte);
+ out <<= 7;
+ out |= byte & 0x7f;
+ if (!(byte & 0x80))
+ break;
+ }
+ I(mem.dom, at_end() || (mem.base <= pos && pos < mem.lim));
+ }
+
+ template<typename T>
+ void adv_sizeof(T &) {
+ adv(sizeof(T));
+ }
+
+ bool adv_zstr(size_t sz);
+ bool get_zstr(char const *&c, size_t &sz);
+ void adv(size_t amt);
+ };
+
+ struct
+ abbrev : dom_owned<abbrev>
+ {
+ rust_dom *dom;
+ uintptr_t body_off;
+ size_t body_sz;
+ uintptr_t tag;
+ uint8_t has_children;
+ size_t idx;
+ abbrev(rust_dom *dom, uintptr_t body_off, size_t body_sz,
+ uintptr_t tag, uint8_t has_children);
+ };
+
+ class
+ abbrev_reader : public mem_reader
+ {
+ ptr_vec<abbrev> abbrevs;
+ public:
+ abbrev_reader(rust_crate::mem_area &abbrev_mem);
+ abbrev *get_abbrev(size_t i);
+ bool step_attr_form_pair(uintptr_t &attr, uintptr_t &form);
+ ~abbrev_reader();
+ };
+
+ rust_dom *dom;
+ size_t idx;
+ rust_crate const *crate;
+
+ rust_crate::mem_area abbrev_mem;
+ abbrev_reader abbrevs;
+
+ rust_crate::mem_area die_mem;
+
+public:
+
+ struct
+ attr
+ {
+ dw_form form;
+ dw_at at;
+ union {
+ struct {
+ char const *s;
+ size_t sz;
+ } str;
+ uintptr_t num;
+ } val;
+
+ bool is_numeric() const;
+ bool is_string() const;
+ size_t get_ssz(rust_dom *dom) const;
+ char const *get_str(rust_dom *dom) const;
+ uintptr_t get_num(rust_dom *dom) const;
+ bool is_unknown() const;
+ };
+
+ struct die_reader;
+
+ struct
+ die
+ {
+ die_reader *rdr;
+ uintptr_t off;
+ abbrev *ab;
+ bool using_rdr;
+
+ die(die_reader *rdr, uintptr_t off);
+ bool is_null() const;
+ bool has_children() const;
+ dw_tag tag() const;
+ bool start_attrs() const;
+ bool step_attr(attr &a) const;
+ bool find_str_attr(dw_at at, char const *&c);
+ bool find_num_attr(dw_at at, uintptr_t &n);
+ bool is_transparent();
+ bool find_child_by_name(char const *c, die &child,
+ bool exact=false);
+ bool find_child_by_tag(dw_tag tag, die &child);
+ die next() const;
+ die next_sibling() const;
+ };
+
+ struct
+ rdr_sess
+ {
+ die_reader *rdr;
+ rdr_sess(die_reader *rdr);
+ ~rdr_sess();
+ };
+
+ struct
+ die_reader : public mem_reader
+ {
+ abbrev_reader &abbrevs;
+ uint32_t cu_unit_length;
+ uintptr_t cu_base;
+ uint16_t dwarf_vers;
+ uint32_t cu_abbrev_off;
+ uint8_t sizeof_addr;
+ bool in_use;
+
+ die first_die();
+ void dump();
+ die_reader(rust_crate::mem_area &die_mem,
+ abbrev_reader &abbrevs);
+ ~die_reader();
+ };
+ die_reader dies;
+ rust_crate_reader(rust_dom *dom, rust_crate const *crate);
+};
+
+
+// A cond(ition) is something we can block on. This can be a channel
+// (writing), a port (reading) or a task (waiting).
+
+struct
+rust_cond
+{
+};
+
+// An alarm can be put into a wait queue and the task will be notified
+// when the wait queue is flushed.
+
+struct
+rust_alarm
+{
+ rust_task *receiver;
+ size_t idx;
+
+ rust_alarm(rust_task *receiver);
+};
+
+
+typedef ptr_vec<rust_alarm> rust_wait_queue;
+
+
+struct stk_seg {
+ unsigned int valgrind_id;
+ uintptr_t limit;
+ uint8_t data[];
+};
+
+struct frame_glue_fns {
+ uintptr_t mark_glue_off;
+ uintptr_t drop_glue_off;
+ uintptr_t reloc_glue_off;
+};
+
+struct
+rust_task : public rc_base<rust_task>,
+ public dom_owned<rust_task>,
+ public rust_cond
+{
+ // Fields known to the compiler.
+ stk_seg *stk;
+ uintptr_t runtime_sp; // Runtime sp while task running.
+ uintptr_t rust_sp; // Saved sp when not running.
+ uintptr_t gc_alloc_chain; // Linked list of GC allocations.
+ rust_dom *dom;
+ rust_crate_cache *cache;
+
+ // Fields known only to the runtime.
+ ptr_vec<rust_task> *state;
+ rust_cond *cond;
+ uintptr_t* dptr; // Rendezvous pointer for send/recv.
+ rust_task *spawner; // Parent-link.
+ size_t idx;
+
+ // Wait queue for tasks waiting for this task.
+ rust_wait_queue waiting_tasks;
+ rust_alarm alarm;
+
+ rust_task(rust_dom *dom,
+ rust_task *spawner);
+ ~rust_task();
+
+ void start(uintptr_t exit_task_glue,
+ uintptr_t spawnee_fn,
+ uintptr_t args,
+ size_t callsz);
+ void grow(size_t n_frame_bytes);
+ bool running();
+ bool blocked();
+ bool blocked_on(rust_cond *cond);
+ bool dead();
+
+ const char *state_str();
+ void transition(ptr_vec<rust_task> *svec, ptr_vec<rust_task> *dvec);
+
+ void block(rust_cond *on);
+ void wakeup(rust_cond *from);
+ void die();
+ void unblock();
+
+ void check_active() { I(dom, dom->curr_task == this); }
+ void check_suspended() { I(dom, dom->curr_task != this); }
+
+ // Swap in some glue code to run when we have returned to the
+ // task's context (assuming we're the active task).
+ void run_after_return(size_t nargs, uintptr_t glue);
+
+ // Swap in some glue code to run when we're next activated
+ // (assuming we're the suspended task).
+ void run_on_resume(uintptr_t glue);
+
+ // Save callee-saved registers and return to the main loop.
+ void yield(size_t nargs);
+
+ // Fail this task (assuming caller-on-stack is different task).
+ void kill();
+
+ // Fail self, assuming caller-on-stack is this task.
+ void fail(size_t nargs);
+
+ // Notify tasks waiting for us that we are about to die.
+ void notify_waiting_tasks();
+
+ uintptr_t get_fp();
+ uintptr_t get_previous_fp(uintptr_t fp);
+ frame_glue_fns *get_frame_glue_fns(uintptr_t fp);
+ rust_crate_cache * get_crate_cache(rust_crate const *curr_crate);
+};
+
+struct rust_port : public rc_base<rust_port>,
+ public task_owned<rust_port>,
+ public rust_cond {
+ rust_task *task;
+ size_t unit_sz;
+ ptr_vec<rust_token> writers;
+ ptr_vec<rust_chan> chans;
+
+ rust_port(rust_task *task, size_t unit_sz);
+ ~rust_port();
+};
+
+struct rust_token : public rust_cond {
+ rust_chan *chan; // Link back to the channel this token belongs to
+ size_t idx; // Index into port->writers.
+ bool submitted; // Whether token is in a port->writers.
+
+ rust_token(rust_chan *chan);
+ ~rust_token();
+
+ bool pending() const;
+ void submit();
+ void withdraw();
+};
+
+
+struct circ_buf : public dom_owned<circ_buf> {
+ static const size_t INIT_CIRC_BUF_UNITS = 8;
+ static const size_t MAX_CIRC_BUF_SIZE = 1 << 24;
+
+ rust_dom *dom;
+ size_t alloc;
+ size_t unit_sz;
+ size_t next;
+ size_t unread;
+ uint8_t *data;
+
+ circ_buf(rust_dom *dom, size_t unit_sz);
+ ~circ_buf();
+
+ void transfer(void *dst);
+ void push(void *src);
+ void shift(void *dst);
+};
+
+#include "rust_chan.h"
+
+int
+rust_main_loop(rust_dom *dom);
+
+//
+// Local Variables:
+// mode: C++
+// 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:
+//
+
+#endif
diff --git a/src/rt/rust_log.cpp b/src/rt/rust_log.cpp
new file mode 100644
index 00000000..102a2623
--- /dev/null
+++ b/src/rt/rust_log.cpp
@@ -0,0 +1,117 @@
+/*
+ * Logging infrastructure that aims to support multi-threading, indentation
+ * and ansi colors.
+ */
+
+#include "rust_internal.h"
+
+static uint32_t read_type_bit_mask() {
+ uint32_t bits = rust_log::ULOG | rust_log::ERR;
+ char *env_str = getenv("RUST_LOG");
+ if (env_str) {
+ bits = 0;
+ bits |= strstr(env_str, "err") ? rust_log::ERR : 0;
+ bits |= strstr(env_str, "mem") ? rust_log::MEM : 0;
+ bits |= strstr(env_str, "comm") ? rust_log::COMM : 0;
+ bits |= strstr(env_str, "task") ? rust_log::TASK : 0;
+ bits |= strstr(env_str, "up") ? rust_log::UPCALL : 0;
+ bits |= strstr(env_str, "dom") ? rust_log::DOM : 0;
+ bits |= strstr(env_str, "ulog") ? rust_log::ULOG : 0;
+ bits |= strstr(env_str, "trace") ? rust_log::TRACE : 0;
+ bits |= strstr(env_str, "dwarf") ? rust_log::DWARF : 0;
+ bits |= strstr(env_str, "cache") ? rust_log::CACHE : 0;
+ bits |= strstr(env_str, "timer") ? rust_log::TIMER : 0;
+ bits |= strstr(env_str, "all") ? rust_log::ALL : 0;
+ }
+ return bits;
+}
+
+rust_log::ansi_color rust_log::get_type_color(log_type type) {
+ switch (type) {
+ case ERR:
+ return rust_log::RED;
+ case UPCALL:
+ return rust_log::GREEN;
+ case COMM:
+ return rust_log::MAGENTA;
+ case DOM:
+ case TASK:
+ return rust_log::LIGHTTEAL;
+ case MEM:
+ return rust_log::YELLOW;
+ default:
+ return rust_log::WHITE;
+ }
+}
+
+static const char * _foreground_colors[] = { "[30m", "[1;30m", "[37m",
+ "[31m", "[1;31m", "[32m",
+ "[1;32m", "[33m", "[33m",
+ "[34m", "[1;34m", "[35m",
+ "[1;35m", "[36m", "[1;36m" };
+rust_log::rust_log(rust_srv *srv, rust_dom *dom) :
+ _srv(srv), _dom(dom), _type_bit_mask(read_type_bit_mask()),
+ _use_colors(getenv("RUST_COLOR_LOG")), _indent(0) {
+}
+
+rust_log::~rust_log() {
+
+}
+
+void rust_log::trace_ln(char *message) {
+ char buffer[512];
+ if (_use_colors) {
+ snprintf(buffer, sizeof(buffer), "\x1b%s0x%08" PRIxPTR "\x1b[0m: ",
+ _foreground_colors[1 + ((uintptr_t) _dom % 2687 % (LIGHTTEAL
+ - 1))], (uintptr_t) _dom);
+ } else {
+ snprintf(buffer, sizeof(buffer), "0x%08" PRIxPTR ": ",
+ (uintptr_t) _dom);
+ }
+
+ for (uint32_t i = 0; i < _indent; i++) {
+ strncat(buffer, "\t", sizeof(buffer) - strlen(buffer) - 1);
+ }
+ strncat(buffer, message, sizeof(buffer) - strlen(buffer) - 1);
+ _srv->log(buffer);
+}
+
+/**
+ * Traces a log message if the specified logging type is not filtered.
+ */
+void rust_log::trace_ln(uint32_t type_bits, char *message) {
+ trace_ln(get_type_color((rust_log::log_type) type_bits), type_bits,
+ message);
+}
+
+/**
+ * Traces a log message using the specified ANSI color code.
+ */
+void rust_log::trace_ln(ansi_color color, uint32_t type_bits, char *message) {
+ if (is_tracing(type_bits)) {
+ if (_use_colors) {
+ char buffer[512];
+ snprintf(buffer, sizeof(buffer), "\x1b%s%s\x1b[0m",
+ _foreground_colors[color], message);
+ trace_ln(buffer);
+ } else {
+ trace_ln(message);
+ }
+ }
+}
+
+bool rust_log::is_tracing(uint32_t type_bits) {
+ return type_bits & _type_bit_mask;
+}
+
+void rust_log::indent() {
+ _indent++;
+}
+
+void rust_log::outdent() {
+ _indent--;
+}
+
+void rust_log::reset_indent(uint32_t indent) {
+ _indent = indent;
+}
diff --git a/src/rt/rust_log.h b/src/rt/rust_log.h
new file mode 100644
index 00000000..b0c5fbec
--- /dev/null
+++ b/src/rt/rust_log.h
@@ -0,0 +1,59 @@
+#ifndef RUST_LOG_H_
+#define RUST_LOG_H_
+
+class rust_dom;
+
+class rust_log {
+ rust_srv *_srv;
+ rust_dom *_dom;
+ uint32_t _type_bit_mask;
+ bool _use_colors;
+ uint32_t _indent;
+ void trace_ln(char *message);
+public:
+ rust_log(rust_srv *srv, rust_dom *dom);
+ virtual ~rust_log();
+
+ enum ansi_color {
+ BLACK,
+ GRAY,
+ WHITE,
+ RED,
+ LIGHTRED,
+ GREEN,
+ LIGHTGREEN,
+ YELLOW,
+ LIGHTYELLOW,
+ BLUE,
+ LIGHTBLUE,
+ MAGENTA,
+ LIGHTMAGENTA,
+ TEAL,
+ LIGHTTEAL
+ };
+
+ enum log_type {
+ ERR = 0x1,
+ MEM = 0x2,
+ COMM = 0x4,
+ TASK = 0x8,
+ DOM = 0x10,
+ ULOG = 0x20,
+ TRACE = 0x40,
+ DWARF = 0x80,
+ CACHE = 0x100,
+ UPCALL = 0x200,
+ TIMER = 0x400,
+ ALL = 0xffffffff
+ };
+
+ void indent();
+ void outdent();
+ void reset_indent(uint32_t indent);
+ void trace_ln(uint32_t type_bits, char *message);
+ void trace_ln(ansi_color color, uint32_t type_bits, char *message);
+ bool is_tracing(uint32_t type_bits);
+ static ansi_color get_type_color(log_type type);
+};
+
+#endif /* RUST_LOG_H_ */
diff --git a/src/rt/rust_task.cpp b/src/rt/rust_task.cpp
new file mode 100644
index 00000000..beba11a0
--- /dev/null
+++ b/src/rt/rust_task.cpp
@@ -0,0 +1,474 @@
+
+#include "rust_internal.h"
+
+#include "valgrind.h"
+#include "memcheck.h"
+
+// Stacks
+
+static size_t const min_stk_bytes = 0x300;
+
+// Task stack segments. Heap allocated and chained together.
+
+static stk_seg*
+new_stk(rust_dom *dom, size_t minsz)
+{
+ if (minsz < min_stk_bytes)
+ minsz = min_stk_bytes;
+ size_t sz = sizeof(stk_seg) + minsz;
+ stk_seg *stk = (stk_seg *)dom->malloc(sz);
+ dom->logptr("new stk", (uintptr_t)stk);
+ memset(stk, 0, sizeof(stk_seg));
+ stk->limit = (uintptr_t) &stk->data[minsz];
+ dom->logptr("stk limit", stk->limit);
+ stk->valgrind_id =
+ VALGRIND_STACK_REGISTER(&stk->data[0],
+ &stk->data[minsz]);
+ return stk;
+}
+
+static void
+del_stk(rust_dom *dom, stk_seg *stk)
+{
+ VALGRIND_STACK_DEREGISTER(stk->valgrind_id);
+ dom->logptr("freeing stk segment", (uintptr_t)stk);
+ dom->free(stk);
+}
+
+// Tasks
+
+// FIXME (issue #31): ifdef by platform. This is getting absurdly
+// x86-specific.
+
+size_t const n_callee_saves = 4;
+size_t const callee_save_fp = 0;
+
+static uintptr_t
+align_down(uintptr_t sp)
+{
+ // There is no platform we care about that needs more than a
+ // 16-byte alignment.
+ return sp & ~(16 - 1);
+}
+
+
+rust_task::rust_task(rust_dom *dom, rust_task *spawner) :
+ stk(new_stk(dom, 0)),
+ runtime_sp(0),
+ rust_sp(stk->limit),
+ gc_alloc_chain(0),
+ dom(dom),
+ cache(NULL),
+ state(&dom->running_tasks),
+ cond(NULL),
+ dptr(0),
+ spawner(spawner),
+ idx(0),
+ waiting_tasks(dom),
+ alarm(this)
+{
+ dom->logptr("new task", (uintptr_t)this);
+}
+
+rust_task::~rust_task()
+{
+ dom->log(rust_log::MEM|rust_log::TASK,
+ "~rust_task 0x%" PRIxPTR ", refcnt=%d",
+ (uintptr_t)this, refcnt);
+
+ /*
+ for (uintptr_t fp = get_fp(); fp; fp = get_previous_fp(fp)) {
+ frame_glue_fns *glue_fns = get_frame_glue_fns(fp);
+ dom->log(rust_log::MEM|rust_log::TASK,
+ "~rust_task, frame fp=0x%" PRIxPTR ", glue_fns=0x%" PRIxPTR,
+ fp, glue_fns);
+ if (glue_fns) {
+ dom->log(rust_log::MEM|rust_log::TASK,
+ "~rust_task, mark_glue=0x%" PRIxPTR,
+ glue_fns->mark_glue);
+ dom->log(rust_log::MEM|rust_log::TASK,
+ "~rust_task, drop_glue=0x%" PRIxPTR,
+ glue_fns->drop_glue);
+ dom->log(rust_log::MEM|rust_log::TASK,
+ "~rust_task, reloc_glue=0x%" PRIxPTR,
+ glue_fns->reloc_glue);
+ }
+ }
+ */
+
+ /* FIXME: tighten this up, there are some more
+ assertions that hold at task-lifecycle events. */
+ I(dom, refcnt == 0 ||
+ (refcnt == 1 && this == dom->root_task));
+
+ del_stk(dom, stk);
+ if (cache)
+ cache->deref();
+}
+
+void
+rust_task::start(uintptr_t exit_task_glue,
+ uintptr_t spawnee_fn,
+ uintptr_t args,
+ size_t callsz)
+{
+ dom->logptr("exit-task glue", exit_task_glue);
+ dom->logptr("from spawnee", spawnee_fn);
+
+ // Set sp to last uintptr_t-sized cell of segment and align down.
+ rust_sp -= sizeof(uintptr_t);
+ rust_sp = align_down(rust_sp);
+
+ // Begin synthesizing frames. There are two: a "fully formed"
+ // exit-task frame at the top of the stack -- that pretends to be
+ // mid-execution -- and a just-starting frame beneath it that
+ // starts executing the first instruction of the spawnee. The
+ // spawnee *thinks* it was called by the exit-task frame above
+ // it. It wasn't; we put that fake frame in place here, but the
+ // illusion is enough for the spawnee to return to the exit-task
+ // frame when it's done, and exit.
+ uintptr_t *spp = (uintptr_t *)rust_sp;
+
+ // The exit_task_glue frame we synthesize above the frame we activate:
+ *spp-- = (uintptr_t) this; // task
+ *spp-- = (uintptr_t) 0; // output
+ *spp-- = (uintptr_t) 0; // retpc
+ for (size_t j = 0; j < n_callee_saves; ++j) {
+ *spp-- = 0;
+ }
+
+ // We want 'frame_base' to point to the last callee-save in this
+ // (exit-task) frame, because we're going to inject this
+ // frame-pointer into the callee-save frame pointer value in the
+ // *next* (spawnee) frame. A cheap trick, but this means the
+ // spawnee frame will restore the proper frame pointer of the glue
+ // frame as it runs its epilogue.
+ uintptr_t frame_base = (uintptr_t) (spp+1);
+
+ *spp-- = (uintptr_t) dom->root_crate; // crate ptr
+ *spp-- = (uintptr_t) 0; // frame_glue_fns
+
+ // Copy args from spawner to spawnee.
+ if (args) {
+ uintptr_t *src = (uintptr_t *)args;
+ src += 1; // spawn-call output slot
+ src += 1; // spawn-call task slot
+ // Memcpy all but the task and output pointers
+ callsz -= (2 * sizeof(uintptr_t));
+ spp = (uintptr_t*) (((uintptr_t)spp) - callsz);
+ memcpy(spp, src, callsz);
+
+ // Move sp down to point to task cell.
+ spp--;
+ } else {
+ // We're at root, starting up.
+ I(dom, callsz==0);
+ }
+
+ // The *implicit* incoming args to the spawnee frame we're
+ // activating:
+
+ *spp-- = (uintptr_t) this; // task
+ *spp-- = (uintptr_t) 0; // output addr
+ *spp-- = (uintptr_t) exit_task_glue; // retpc
+
+ // The context the activate_glue needs to switch stack.
+ *spp-- = (uintptr_t) spawnee_fn; // instruction to start at
+ for (size_t j = 0; j < n_callee_saves; ++j) {
+ // callee-saves to carry in when we activate
+ if (j == callee_save_fp)
+ *spp-- = frame_base;
+ else
+ *spp-- = NULL;
+ }
+
+ // Back up one, we overshot where sp should be.
+ rust_sp = (uintptr_t) (spp+1);
+
+ dom->add_task_to_state_vec(&dom->running_tasks, this);
+}
+
+void
+rust_task::grow(size_t n_frame_bytes)
+{
+ 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];
+ uintptr_t rust_sp_disp = old_top - this->rust_sp;
+ size_t ssz = old_top - old_bottom;
+ dom->log(rust_log::MEM|rust_log::TASK|rust_log::UPCALL,
+ "upcall_grow_task(%" PRIdPTR
+ "), old size %" PRIdPTR
+ " bytes (old lim: 0x%" PRIxPTR ")",
+ n_frame_bytes, ssz, old_top);
+ ssz *= 2;
+ if (ssz < n_frame_bytes)
+ ssz = n_frame_bytes;
+ ssz = next_power_of_two(ssz);
+
+ dom->log(rust_log::MEM|rust_log::TASK, "upcall_grow_task growing stk 0x%"
+ PRIxPTR " to %d bytes", old_stk, ssz);
+
+ stk_seg *nstk = new_stk(dom, ssz);
+ uintptr_t new_top = (uintptr_t) &nstk->data[ssz];
+ size_t n_copy = old_top - old_bottom;
+ dom->log(rust_log::MEM|rust_log::TASK,
+ "copying %d bytes of stack from [0x%" PRIxPTR ", 0x%" PRIxPTR "]"
+ " to [0x%" PRIxPTR ", 0x%" PRIxPTR "]",
+ n_copy,
+ old_bottom, old_bottom + n_copy,
+ new_top - n_copy, new_top);
+
+ VALGRIND_MAKE_MEM_DEFINED((void*)old_bottom, n_copy);
+ memcpy((void*)(new_top - n_copy), (void*)old_bottom, n_copy);
+
+ nstk->limit = new_top;
+ this->stk = nstk;
+ this->rust_sp = new_top - rust_sp_disp;
+
+ dom->log(rust_log::MEM|rust_log::TASK, "processing relocations");
+
+ // FIXME (issue #32): this is the most ridiculously crude
+ // relocation scheme ever. Try actually, you know, writing out
+ // reloc descriptors?
+ size_t n_relocs = 0;
+ for (uintptr_t* p = (uintptr_t*)(new_top - n_copy);
+ p < (uintptr_t*)new_top; ++p) {
+ if (old_bottom <= *p && *p < old_top) {
+ //dom->log(rust_log::MEM, "relocating pointer 0x%" PRIxPTR
+ // " by %d bytes", *p, (new_top - old_top));
+ n_relocs++;
+ *p += (new_top - old_top);
+ }
+ }
+ dom->log(rust_log::MEM|rust_log::TASK,
+ "processed %d relocations", n_relocs);
+ del_stk(dom, old_stk);
+ dom->logptr("grown stk limit", new_top);
+}
+
+void
+push_onto_thread_stack(uintptr_t &sp, uintptr_t value)
+{
+ asm("xchgl %0, %%esp\n"
+ "push %2\n"
+ "xchgl %0, %%esp\n"
+ : "=r" (sp)
+ : "0" (sp), "r" (value)
+ : "eax");
+}
+
+void
+rust_task::run_after_return(size_t nargs, uintptr_t glue)
+{
+ // This is only safe to call if we're the currently-running task.
+ check_active();
+
+ uintptr_t sp = runtime_sp;
+
+ // The compiler reserves nargs + 1 word for oldsp on the stack and
+ // then aligns it.
+ sp = align_down(sp - nargs * sizeof(uintptr_t));
+
+ uintptr_t *retpc = ((uintptr_t *) sp) - 1;
+ dom->log(rust_log::TASK|rust_log::MEM,
+ "run_after_return: overwriting retpc=0x%" PRIxPTR
+ " @ runtime_sp=0x%" PRIxPTR
+ " with glue=0x%" PRIxPTR,
+ *retpc, sp, glue);
+
+ // Move the current return address (which points into rust code)
+ // onto the rust stack and pretend we just called into the glue.
+ push_onto_thread_stack(rust_sp, *retpc);
+ *retpc = glue;
+}
+
+void
+rust_task::run_on_resume(uintptr_t glue)
+{
+ // This is only safe to call if we're suspended.
+ check_suspended();
+
+ // Inject glue as resume address in the suspended frame.
+ uintptr_t* rsp = (uintptr_t*) rust_sp;
+ rsp += n_callee_saves;
+ dom->log(rust_log::TASK|rust_log::MEM,
+ "run_on_resume: overwriting retpc=0x%" PRIxPTR
+ " @ rust_sp=0x%" PRIxPTR
+ " with glue=0x%" PRIxPTR,
+ *rsp, rsp, glue);
+ *rsp = glue;
+}
+
+void
+rust_task::yield(size_t nargs)
+{
+ dom->log(rust_log::TASK,
+ "task 0x%" PRIxPTR " yielding", this);
+ run_after_return(nargs, dom->root_crate->get_yield_glue());
+}
+
+static inline uintptr_t
+get_callee_save_fp(uintptr_t *top_of_callee_saves)
+{
+ return top_of_callee_saves[n_callee_saves - (callee_save_fp + 1)];
+}
+
+void
+rust_task::kill() {
+ // 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).
+ dom->log(rust_log::TASK, "killing task 0x%" PRIxPTR, this);
+ // Unblock the task so it can unwind.
+ unblock();
+ if (this == dom->root_task)
+ dom->fail();
+ 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);
+ // Unblock the task so it can unwind.
+ unblock();
+ if (this == dom->root_task)
+ dom->fail();
+ run_after_return(nargs, dom->root_crate->get_unwind_glue());
+ if (spawner) {
+ dom->log(rust_log::TASK,
+ "task 0x%" PRIxPTR
+ " propagating failure to parent 0x%" PRIxPTR,
+ this, spawner);
+ spawner->kill();
+ }
+}
+
+void
+rust_task::notify_waiting_tasks()
+{
+ while (waiting_tasks.length() > 0) {
+ rust_task *t = waiting_tasks.pop()->receiver;
+ if (!t->dead())
+ t->wakeup(this);
+ }
+}
+
+uintptr_t
+rust_task::get_fp() {
+ // sp in any suspended task points to the last callee-saved reg on
+ // the task stack.
+ return get_callee_save_fp((uintptr_t*)rust_sp);
+}
+
+uintptr_t
+rust_task::get_previous_fp(uintptr_t fp) {
+ // fp happens to, coincidentally (!) also point to the last
+ // callee-save on the task stack.
+ return get_callee_save_fp((uintptr_t*)fp);
+}
+
+frame_glue_fns*
+rust_task::get_frame_glue_fns(uintptr_t fp) {
+ fp -= sizeof(uintptr_t);
+ return *((frame_glue_fns**) fp);
+}
+
+bool
+rust_task::running()
+{
+ return state == &dom->running_tasks;
+}
+
+bool
+rust_task::blocked()
+{
+ return state == &dom->blocked_tasks;
+}
+
+bool
+rust_task::blocked_on(rust_cond *on)
+{
+ return blocked() && cond == on;
+}
+
+bool
+rust_task::dead()
+{
+ return state == &dom->dead_tasks;
+}
+
+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'",
+ (uintptr_t)this,
+ dom->state_vec_name(src),
+ dom->state_vec_name(dst));
+ dom->remove_task_from_state_vec(src, this);
+ dom->add_task_to_state_vec(dst, this);
+ state = dst;
+}
+
+void
+rust_task::block(rust_cond *on)
+{
+ I(dom, on);
+ transition(&dom->running_tasks, &dom->blocked_tasks);
+ dom->log(rust_log::TASK,
+ "task 0x%" PRIxPTR " blocking on 0x%" PRIxPTR,
+ (uintptr_t)this,
+ (uintptr_t)on);
+ cond = on;
+}
+
+void
+rust_task::wakeup(rust_cond *from)
+{
+ transition(&dom->blocked_tasks, &dom->running_tasks);
+ I(dom, cond == from);
+}
+
+void
+rust_task::die()
+{
+ transition(&dom->running_tasks, &dom->dead_tasks);
+}
+
+void
+rust_task::unblock()
+{
+ if (blocked())
+ wakeup(cond);
+}
+
+rust_crate_cache *
+rust_task::get_crate_cache(rust_crate const *curr_crate)
+{
+ if (cache && cache->crate != curr_crate) {
+ dom->log(rust_log::TASK, "switching task crate-cache to crate 0x%"
+ PRIxPTR, curr_crate);
+ cache->deref();
+ cache = NULL;
+ }
+
+ if (!cache) {
+ dom->log(rust_log::TASK, "fetching cache for current crate");
+ cache = dom->get_cache(curr_crate);
+ }
+ return cache;
+}
+
+//
+// Local Variables:
+// mode: C++
+// 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/rt/rust_timer.cpp b/src/rt/rust_timer.cpp
new file mode 100644
index 00000000..897b7730
--- /dev/null
+++ b/src/rt/rust_timer.cpp
@@ -0,0 +1,97 @@
+
+#include "rust_internal.h"
+
+// The mechanism in this file is very crude; every domain (thread) spawns its
+// own secondary timer thread, and that timer thread *never idles*. It
+// sleep-loops interrupting the domain.
+//
+// This will need replacement, particularly in order to achieve an actual
+// state of idling when we're waiting on the outside world. Though that might
+// be as simple as making a secondary waitable start/stop-timer signalling
+// system between the domain and its timer thread. We'll see.
+//
+// On the other hand, we don't presently have the ability to idle domains *at
+// all*, and without the timer thread we're unable to otherwise preempt rust
+// tasks. So ... one step at a time.
+//
+// The implementation here is "lockless" in the sense that it only involves
+// one-directional signaling of one-shot events, so the event initiator just
+// writes a nonzero word to a prederermined location and waits for the
+// receiver to see it show up in their memory.
+
+#if defined(__WIN32__)
+static DWORD WINAPI
+win32_timer_loop(void *ptr)
+{
+ // We were handed the rust_timer that owns us.
+ rust_timer *timer = (rust_timer *)ptr;
+ rust_dom &dom = timer->dom;
+ dom.log(LOG_TIMER, "in timer 0x%" PRIxPTR, (uintptr_t)timer);
+ while (!timer->exit_flag) {
+ Sleep(TIME_SLICE_IN_MS);
+ dom.log(LOG_TIMER,
+ "timer 0x%" PRIxPTR
+ " interrupting domain 0x%" PRIxPTR,
+ (uintptr_t)timer,
+ (uintptr_t)&dom);
+ dom.interrupt_flag = 1;
+ }
+ ExitThread(0);
+ return 0;
+}
+
+#elif defined(__GNUC__)
+static void *
+pthread_timer_loop(void *ptr)
+{
+ // We were handed the rust_timer that owns us.
+ rust_timer *timer = (rust_timer *)ptr;
+ rust_dom &dom(timer->dom);
+ while (!timer->exit_flag) {
+ usleep(TIME_SLICE_IN_MS * 1000);
+ dom.interrupt_flag = 1;
+ }
+ pthread_exit(NULL);
+ return 0;
+
+}
+#else
+#error "Platform not supported"
+#endif
+
+
+rust_timer::rust_timer(rust_dom &dom) : dom(dom), exit_flag(0)
+{
+ dom.log(rust_log::TIMER, "creating timer for domain 0x%" PRIxPTR, &dom);
+#if defined(__WIN32__)
+ thread = CreateThread(NULL, 0, win32_timer_loop, this, 0, NULL);
+ dom.win32_require("CreateThread", thread != NULL);
+#else
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
+ pthread_create(&thread, &attr, pthread_timer_loop, (void *)this);
+#endif
+}
+
+rust_timer::~rust_timer()
+{
+ exit_flag = 1;
+#if defined(__WIN32__)
+ dom.win32_require("WaitForSingleObject",
+ WaitForSingleObject(thread, INFINITE)
+ == WAIT_OBJECT_0);
+#else
+ pthread_join(thread, NULL);
+#endif
+}
+
+//
+// Local Variables:
+// mode: C++
+// 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/rt/rust_upcall.cpp b/src/rt/rust_upcall.cpp
new file mode 100644
index 00000000..3a17ea1c
--- /dev/null
+++ b/src/rt/rust_upcall.cpp
@@ -0,0 +1,654 @@
+
+#include "rust_internal.h"
+
+
+// Upcalls.
+
+#ifdef __GNUC__
+#define LOG_UPCALL_ENTRY(task) \
+ (task)->dom->get_log().reset_indent(0); \
+ (task)->dom->log(rust_log::UPCALL, \
+ "upcall task: 0x%" PRIxPTR \
+ " retpc: 0x%" PRIxPTR, \
+ (task), __builtin_return_address(0)); \
+ (task)->dom->get_log().indent();
+#else
+#define LOG_UPCALL_ENTRY(task) \
+ (task)->dom->get_log().reset_indent(0); \
+ (task)->dom->log(rust_log::UPCALL, \
+ "upcall task: 0x%" PRIxPTR (task)); \
+ (task)->dom->get_log().indent();
+#endif
+
+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)
+{
+ LOG_UPCALL_ENTRY(task);
+ task->grow(n_frame_bytes);
+}
+
+extern "C" CDECL void
+upcall_log_int(rust_task *task, int32_t i)
+{
+ LOG_UPCALL_ENTRY(task);
+ task->dom->log(rust_log::UPCALL|rust_log::ULOG,
+ "upcall log_int(0x%" PRIx32 " = %" PRId32 " = '%c')",
+ i, i, (char)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->dom->log(rust_log::UPCALL|rust_log::ULOG,
+ "upcall log_str(\"%s\")",
+ c);
+}
+
+extern "C" CDECL void
+upcall_trace_word(rust_task *task, uintptr_t i)
+{
+ LOG_UPCALL_ENTRY(task);
+ task->dom->log(rust_log::UPCALL|rust_log::TRACE,
+ "trace: 0x%" PRIxPTR "",
+ i, i, (char)i);
+}
+
+extern "C" CDECL void
+upcall_trace_str(rust_task *task, char const *c)
+{
+ LOG_UPCALL_ENTRY(task);
+ task->dom->log(rust_log::UPCALL|rust_log::TRACE,
+ "trace: %s",
+ c);
+}
+
+extern "C" CDECL rust_port*
+upcall_new_port(rust_task *task, size_t unit_sz)
+{
+ LOG_UPCALL_ENTRY(task);
+ rust_dom *dom = task->dom;
+ dom->log(rust_log::UPCALL|rust_log::MEM|rust_log::COMM,
+ "upcall_new_port(task=0x%" PRIxPTR ", unit_sz=%d)",
+ (uintptr_t)task, unit_sz);
+ return new (dom) rust_port(task, unit_sz);
+}
+
+extern "C" CDECL void
+upcall_del_port(rust_task *task, rust_port *port)
+{
+ LOG_UPCALL_ENTRY(task);
+ task->dom->log(rust_log::UPCALL|rust_log::MEM|rust_log::COMM,
+ "upcall del_port(0x%" PRIxPTR ")", (uintptr_t)port);
+ I(task->dom, !port->refcnt);
+ delete port;
+}
+
+extern "C" CDECL rust_chan*
+upcall_new_chan(rust_task *task, rust_port *port)
+{
+ LOG_UPCALL_ENTRY(task);
+ rust_dom *dom = task->dom;
+ dom->log(rust_log::UPCALL|rust_log::MEM|rust_log::COMM,
+ "upcall_new_chan(task=0x%" PRIxPTR ", port=0x%" PRIxPTR ")",
+ (uintptr_t)task, port);
+ I(dom, port);
+ return new (dom) rust_chan(task, port);
+}
+
+extern "C" CDECL void
+upcall_del_chan(rust_task *task, rust_chan *chan)
+{
+ LOG_UPCALL_ENTRY(task);
+ rust_dom *dom = task->dom;
+ dom->log(rust_log::UPCALL|rust_log::MEM|rust_log::COMM,
+ "upcall del_chan(0x%" PRIxPTR ")", (uintptr_t)chan);
+ I(dom, !chan->refcnt);
+ delete chan;
+}
+
+extern "C" CDECL rust_chan *
+upcall_clone_chan(rust_task *task, rust_task *owner, rust_chan *chan)
+{
+ LOG_UPCALL_ENTRY(task);
+ rust_dom *dom = task->dom;
+ dom->log(rust_log::UPCALL|rust_log::MEM|rust_log::COMM,
+ "upcall clone_chan(owner 0x%" PRIxPTR ", chan 0x%" PRIxPTR ")",
+ (uintptr_t)owner, (uintptr_t)chan);
+ return new (owner->dom) rust_chan(owner, chan->port);
+}
+
+
+/*
+ * Buffering protocol:
+ *
+ * - Reader attempts to read:
+ * - Set reader to blocked-reading state.
+ * - If buf with data exists:
+ * - Attempt transmission.
+ *
+ * - Writer attempts to write:
+ * - Set writer to blocked-writing state.
+ * - Copy data into chan.
+ * - Attempt transmission.
+ *
+ * - Transmission:
+ * - Copy data from buf to reader
+ * - Decr buf
+ * - Set reader to running
+ * - If buf now empty and blocked writer:
+ * - Set blocked writer to running
+ *
+ */
+
+static int
+attempt_transmission(rust_dom *dom,
+ rust_chan *src,
+ rust_task *dst)
+{
+ I(dom, src);
+ I(dom, dst);
+
+ rust_port *port = src->port;
+ if (!port) {
+ dom->log(rust_log::COMM,
+ "src died, transmission incomplete");
+ return 0;
+ }
+
+ circ_buf *buf = &src->buffer;
+ if (buf->unread == 0) {
+ dom->log(rust_log::COMM,
+ "buffer empty, transmission incomplete");
+ return 0;
+ }
+
+ if (!dst->blocked_on(port)) {
+ dom->log(rust_log::COMM,
+ "dst in non-reading state, transmission incomplete");
+ return 0;
+ }
+
+ uintptr_t *dptr = dst->dptr;
+ dom->log(rust_log::COMM,
+ "receiving %d bytes into dst_task=0x%" PRIxPTR
+ ", dptr=0x%" PRIxPTR,
+ port->unit_sz, dst, dptr);
+ buf->shift(dptr);
+
+ // Wake up the sender if its waiting for the send operation.
+ rust_task *sender = src->task;
+ rust_token *token = &src->token;
+ if (sender->blocked_on(token))
+ sender->wakeup(token);
+
+ // Wake up the receiver, there is new data.
+ dst->wakeup(port);
+
+ dom->log(rust_log::COMM, "transmission complete");
+ return 1;
+}
+
+extern "C" CDECL void
+upcall_yield(rust_task *task)
+{
+ LOG_UPCALL_ENTRY(task);
+ rust_dom *dom = task->dom;
+ dom->log(rust_log::UPCALL|rust_log::COMM, "upcall yield()");
+ task->yield(1);
+}
+
+extern "C" CDECL void
+upcall_join(rust_task *task, rust_task *other)
+{
+ LOG_UPCALL_ENTRY(task);
+ rust_dom *dom = task->dom;
+ dom->log(rust_log::UPCALL|rust_log::COMM,
+ "upcall join(other=0x%" PRIxPTR ")",
+ (uintptr_t)other);
+
+ // If the other task is already dying, we dont have to wait for it.
+ if (!other->dead()) {
+ other->waiting_tasks.push(&task->alarm);
+ task->block(other);
+ task->yield(2);
+ }
+}
+
+extern "C" CDECL void
+upcall_send(rust_task *task, rust_chan *chan, void *sptr)
+{
+ LOG_UPCALL_ENTRY(task);
+ rust_dom *dom = task->dom;
+ dom->log(rust_log::UPCALL|rust_log::COMM,
+ "upcall send(chan=0x%" PRIxPTR ", sptr=0x%" PRIxPTR ")",
+ (uintptr_t)chan,
+ (uintptr_t)sptr);
+
+ I(dom, chan);
+ I(dom, sptr);
+
+ rust_port *port = chan->port;
+ dom->log(rust_log::MEM|rust_log::COMM,
+ "send to port", (uintptr_t)port);
+ I(dom, port);
+
+ rust_token *token = &chan->token;
+ dom->log(rust_log::MEM|rust_log::COMM,
+ "sending via token 0x%" PRIxPTR,
+ (uintptr_t)token);
+
+ if (port->task) {
+ chan->buffer.push(sptr);
+ task->block(token);
+ attempt_transmission(dom, chan, port->task);
+ if (chan->buffer.unread && !token->pending())
+ token->submit();
+ } else {
+ dom->log(rust_log::COMM|rust_log::ERR,
+ "port has no task (possibly throw?)");
+ }
+
+ if (!task->running())
+ task->yield(3);
+}
+
+extern "C" CDECL void
+upcall_recv(rust_task *task, uintptr_t *dptr, rust_port *port)
+{
+ LOG_UPCALL_ENTRY(task);
+ rust_dom *dom = task->dom;
+ dom->log(rust_log::UPCALL|rust_log::COMM,
+ "upcall recv(dptr=0x" PRIxPTR ", port=0x%" PRIxPTR ")",
+ (uintptr_t)dptr,
+ (uintptr_t)port);
+
+ I(dom, port);
+ I(dom, port->task);
+ I(dom, task);
+ I(dom, port->task == task);
+
+ task->block(port);
+
+ if (port->writers.length() > 0) {
+ I(dom, task->dom);
+ size_t i = rand(&dom->rctx);
+ i %= port->writers.length();
+ rust_token *token = port->writers[i];
+ rust_chan *chan = token->chan;
+ if (attempt_transmission(dom, chan, task))
+ token->withdraw();
+ } else {
+ dom->log(rust_log::COMM,
+ "no writers sending to port", (uintptr_t)port);
+ }
+
+ if (!task->running()) {
+ task->dptr = dptr;
+ task->yield(3);
+ }
+}
+
+extern "C" CDECL void
+upcall_fail(rust_task *task, char const *expr, char const *file, size_t line)
+{
+ LOG_UPCALL_ENTRY(task);
+ task->dom->log(rust_log::UPCALL|rust_log::ERR,
+ "upcall fail '%s', %s:%" PRIdPTR,
+ expr, file, line);
+ task->fail(4);
+}
+
+extern "C" CDECL void
+upcall_kill(rust_task *task, rust_task *target)
+{
+ LOG_UPCALL_ENTRY(task);
+ task->dom->log(rust_log::UPCALL|rust_log::TASK,
+ "upcall kill target=0x%" PRIxPTR, target);
+ target->kill();
+}
+
+extern "C" CDECL void
+upcall_exit(rust_task *task)
+{
+ LOG_UPCALL_ENTRY(task);
+
+ rust_dom *dom = task->dom;
+ dom->log(rust_log::UPCALL|rust_log::TASK, "upcall exit");
+ task->die();
+ task->notify_waiting_tasks();
+ task->yield(1);
+}
+
+extern "C" CDECL uintptr_t
+upcall_malloc(rust_task *task, size_t nbytes)
+{
+ LOG_UPCALL_ENTRY(task);
+
+ void *p = task->dom->malloc(nbytes);
+ task->dom->log(rust_log::UPCALL|rust_log::MEM,
+ "upcall malloc(%u) = 0x%" PRIxPTR,
+ nbytes, (uintptr_t)p);
+ return (uintptr_t) p;
+}
+
+extern "C" CDECL void
+upcall_free(rust_task *task, void* ptr)
+{
+ LOG_UPCALL_ENTRY(task);
+
+ rust_dom *dom = task->dom;
+ dom->log(rust_log::UPCALL|rust_log::MEM,
+ "upcall free(0x%" PRIxPTR ")",
+ (uintptr_t)ptr);
+ dom->free(ptr);
+}
+
+extern "C" CDECL rust_str *
+upcall_new_str(rust_task *task, char const *s, size_t fill)
+{
+ LOG_UPCALL_ENTRY(task);
+ rust_dom *dom = task->dom;
+ dom->log(rust_log::UPCALL|rust_log::MEM,
+ "upcall new_str('%s', %" PRIdPTR ")", s, fill);
+ size_t alloc = next_power_of_two(sizeof(rust_str) + fill);
+ void *mem = dom->malloc(alloc);
+ if (!mem) {
+ task->fail(3);
+ return NULL;
+ }
+ rust_str *st = new (mem) rust_str(dom, alloc, fill, (uint8_t const *)s);
+ dom->log(rust_log::UPCALL|rust_log::MEM,
+ "upcall new_str('%s', %" PRIdPTR ") = 0x%" PRIxPTR,
+ s, fill, st);
+ return st;
+}
+
+extern "C" CDECL rust_vec *
+upcall_new_vec(rust_task *task, size_t fill)
+{
+ LOG_UPCALL_ENTRY(task);
+ rust_dom *dom = task->dom;
+ dom->log(rust_log::UPCALL|rust_log::MEM,
+ "upcall new_vec(%" PRIdPTR ")", fill);
+ size_t alloc = next_power_of_two(sizeof(rust_vec) + fill);
+ void *mem = dom->malloc(alloc);
+ if (!mem) {
+ task->fail(3);
+ return NULL;
+ }
+ rust_vec *v = new (mem) rust_vec(dom, alloc, 0, NULL);
+ dom->log(rust_log::UPCALL|rust_log::MEM,
+ "upcall new_vec(%" PRIdPTR ") = 0x%" PRIxPTR,
+ fill, v);
+ return v;
+}
+
+
+extern "C" CDECL rust_str *
+upcall_vec_grow(rust_task *task, rust_vec *v, size_t n_bytes)
+{
+ LOG_UPCALL_ENTRY(task);
+ rust_dom *dom = task->dom;
+ dom->log(rust_log::UPCALL|rust_log::MEM,
+ "upcall vec_grow(%" PRIxPTR ", %" PRIdPTR ")", v, n_bytes);
+ size_t alloc = next_power_of_two(sizeof(rust_vec) + v->fill + n_bytes);
+ if (v->refcnt == 1) {
+
+ // Fastest path: already large enough.
+ if (v->alloc >= alloc) {
+ dom->log(rust_log::UPCALL|rust_log::MEM, "no-growth path");
+ return v;
+ }
+
+ // Second-fastest path: can at least realloc.
+ dom->log(rust_log::UPCALL|rust_log::MEM, "realloc path");
+ v = (rust_vec*)dom->realloc(v, alloc);
+ if (!v) {
+ task->fail(3);
+ return NULL;
+ }
+ v->alloc = alloc;
+
+ } else {
+ // Slowest path: make a new vec.
+ dom->log(rust_log::UPCALL|rust_log::MEM, "new vec path");
+ void *mem = dom->malloc(alloc);
+ if (!mem) {
+ task->fail(3);
+ return NULL;
+ }
+ v->deref();
+ v = new (mem) rust_vec(dom, alloc, v->fill, &v->data[0]);
+ }
+ I(dom, sizeof(rust_vec) + v->fill <= v->alloc);
+ return v;
+}
+
+
+static rust_crate_cache::c_sym *
+fetch_c_sym(rust_task *task,
+ rust_crate const *curr_crate,
+ size_t lib_num,
+ size_t c_sym_num,
+ char const *library,
+ char const *symbol)
+{
+ rust_crate_cache *cache = task->get_crate_cache(curr_crate);
+ rust_crate_cache::lib *l = cache->get_lib(lib_num, library);
+ return cache->get_c_sym(c_sym_num, l, symbol);
+}
+
+extern "C" CDECL uintptr_t
+upcall_require_rust_sym(rust_task *task,
+ rust_crate const *curr_crate,
+ size_t lib_num, // # of lib
+ size_t c_sym_num, // # of C sym "rust_crate" in lib
+ size_t rust_sym_num, // # of rust sym
+ char const *library,
+ char const **path)
+{
+ LOG_UPCALL_ENTRY(task);
+ rust_dom *dom = task->dom;
+
+ dom->log(rust_log::UPCALL|rust_log::CACHE,
+ "upcall require rust sym: lib #%" PRIdPTR
+ " = %s, c_sym #%" PRIdPTR
+ ", rust_sym #%" PRIdPTR
+ ", curr_crate = 0x%" PRIxPTR,
+ lib_num, library, c_sym_num, rust_sym_num,
+ curr_crate);
+ for (char const **c = crate_rel(curr_crate, path); *c; ++c) {
+ dom->log(rust_log::UPCALL, " + %s", crate_rel(curr_crate, *c));
+ }
+
+ dom->log(rust_log::UPCALL|rust_log::CACHE,
+ "require C symbol 'rust_crate' from lib #%" PRIdPTR,lib_num);
+ rust_crate_cache::c_sym *c =
+ fetch_c_sym(task, curr_crate, lib_num, c_sym_num,
+ library, "rust_crate");
+
+ dom->log(rust_log::UPCALL|rust_log::CACHE,
+ "require rust symbol inside crate");
+ rust_crate_cache::rust_sym *s =
+ task->cache->get_rust_sym(rust_sym_num, dom, curr_crate, c, path);
+
+ uintptr_t addr = s->get_val();
+ if (addr) {
+ dom->log(rust_log::UPCALL|rust_log::CACHE,
+ "found-or-cached addr: 0x%" PRIxPTR, addr);
+ } else {
+ dom->log(rust_log::UPCALL|rust_log::CACHE,
+ "failed to resolve symbol");
+ task->fail(7);
+ }
+ return addr;
+}
+
+extern "C" CDECL uintptr_t
+upcall_require_c_sym(rust_task *task,
+ rust_crate const *curr_crate,
+ size_t lib_num, // # of lib
+ size_t c_sym_num, // # of C sym
+ char const *library,
+ char const *symbol)
+{
+ LOG_UPCALL_ENTRY(task);
+ rust_dom *dom = task->dom;
+
+ dom->log(rust_log::UPCALL|rust_log::CACHE,
+ "upcall require c sym: lib #%" PRIdPTR
+ " = %s, c_sym #%" PRIdPTR
+ " = %s"
+ ", curr_crate = 0x%" PRIxPTR,
+ lib_num, library, c_sym_num, symbol, curr_crate);
+
+ rust_crate_cache::c_sym *c =
+ fetch_c_sym(task, curr_crate, lib_num, c_sym_num, library, symbol);
+
+ uintptr_t addr = c->get_val();
+ if (addr) {
+ dom->log(rust_log::UPCALL|rust_log::CACHE,
+ "found-or-cached addr: 0x%" PRIxPTR, addr);
+ } else {
+ dom->log(rust_log::UPCALL|rust_log::CACHE,
+ "failed to resolve symbol");
+ task->fail(6);
+ }
+ return addr;
+}
+
+extern "C" CDECL type_desc *
+upcall_get_type_desc(rust_task *task,
+ rust_crate const *curr_crate,
+ size_t size,
+ size_t align,
+ size_t n_descs,
+ type_desc const **descs)
+{
+ LOG_UPCALL_ENTRY(task);
+ rust_dom *dom = task->dom;
+ dom->log(rust_log::UPCALL|rust_log::CACHE,
+ "upcall get_type_desc with size=%" PRIdPTR
+ ", align=%" PRIdPTR ", %" PRIdPTR " descs",
+ size, align, n_descs);
+ rust_crate_cache *cache = task->get_crate_cache(curr_crate);
+ type_desc *td = cache->get_type_desc(size, align, n_descs, descs);
+ dom->log(rust_log::UPCALL|rust_log::CACHE,
+ "returning tydesc 0x%" PRIxPTR, td);
+ return td;
+}
+
+
+#if defined(__WIN32__)
+static DWORD WINAPI rust_thread_start(void *ptr)
+#elif defined(__GNUC__)
+static void *rust_thread_start(void *ptr)
+#else
+#error "Platform not supported"
+#endif
+{
+ // We were handed the domain we are supposed to run.
+ rust_dom *dom = (rust_dom *)ptr;
+
+ // Start a new rust main loop for this thread.
+ rust_main_loop(dom);
+
+ rust_srv *srv = dom->srv;
+ delete dom;
+ delete srv;
+
+ return 0;
+}
+
+extern "C" CDECL rust_task *
+upcall_new_task(rust_task *spawner)
+{
+ LOG_UPCALL_ENTRY(spawner);
+
+ rust_dom *dom = spawner->dom;
+ rust_task *task = new (dom) rust_task(dom, spawner);
+ dom->log(rust_log::UPCALL|rust_log::MEM|rust_log::TASK,
+ "upcall new_task(spawner 0x%" PRIxPTR ") = 0x%" PRIxPTR,
+ spawner, task);
+ return task;
+}
+
+extern "C" CDECL rust_task *
+upcall_start_task(rust_task *spawner,
+ rust_task *task,
+ uintptr_t exit_task_glue,
+ uintptr_t spawnee_fn,
+ size_t callsz)
+{
+ LOG_UPCALL_ENTRY(spawner);
+
+ rust_dom *dom = spawner->dom;
+ dom->log(rust_log::UPCALL|rust_log::MEM|rust_log::TASK,
+ "upcall start_task(task 0x%" PRIxPTR
+ " exit_task_glue 0x%" PRIxPTR
+ ", spawnee 0x%" PRIxPTR
+ ", callsz %" PRIdPTR ")",
+ task, exit_task_glue, spawnee_fn, callsz);
+ task->start(exit_task_glue, spawnee_fn, spawner->rust_sp, callsz);
+ return task;
+}
+
+extern "C" CDECL rust_task *
+upcall_new_thread(rust_task *task)
+{
+ 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);
+ new_dom->log(rust_log::UPCALL|rust_log::MEM,
+ "upcall new_thread() = 0x%" PRIxPTR,
+ new_dom->root_task);
+ return new_dom->root_task;
+}
+
+extern "C" CDECL rust_task *
+upcall_start_thread(rust_task *spawner,
+ rust_task *root_task,
+ uintptr_t exit_task_glue,
+ uintptr_t spawnee_fn,
+ size_t callsz)
+{
+ LOG_UPCALL_ENTRY(spawner);
+
+ rust_dom *dom = spawner->dom;
+ dom->log(rust_log::UPCALL|rust_log::MEM|rust_log::TASK,
+ "upcall start_thread(exit_task_glue 0x%" PRIxPTR
+ ", spawnee 0x%" PRIxPTR
+ ", callsz %" PRIdPTR ")",
+ exit_task_glue, spawnee_fn, callsz);
+ root_task->start(exit_task_glue, spawnee_fn, spawner->rust_sp, callsz);
+
+#if defined(__WIN32__)
+ HANDLE thread;
+ thread = CreateThread(NULL, 0, rust_thread_start, root_task->dom,
+ 0, NULL);
+ dom->win32_require("CreateThread", thread != NULL);
+#else
+ pthread_t thread;
+ pthread_create(&thread, &dom->attr, rust_thread_start,
+ (void *)root_task->dom);
+#endif
+
+ return 0;
+}
+
+//
+// Local Variables:
+// mode: C++
+// 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/rt/rust_util.h b/src/rt/rust_util.h
new file mode 100644
index 00000000..6f34dad9
--- /dev/null
+++ b/src/rt/rust_util.h
@@ -0,0 +1,155 @@
+#ifndef RUST_UTIL_H
+#define RUST_UTIL_H
+
+// Reference counted objects
+
+template <typename T>
+rc_base<T>::rc_base() :
+ refcnt(1)
+{
+}
+
+template <typename T>
+rc_base<T>::~rc_base()
+{
+}
+
+// Utility type: pointer-vector.
+
+template <typename T>
+ptr_vec<T>::ptr_vec(rust_dom *dom) :
+ dom(dom),
+ alloc(INIT_SIZE),
+ fill(0),
+ data(new (dom) T*[alloc])
+{
+ I(dom, data);
+ dom->log(rust_log::MEM,
+ "new ptr_vec(data=0x%" PRIxPTR ") -> 0x%" PRIxPTR,
+ (uintptr_t)data, (uintptr_t)this);
+}
+
+template <typename T>
+ptr_vec<T>::~ptr_vec()
+{
+ I(dom, data);
+ dom->log(rust_log::MEM,
+ "~ptr_vec 0x%" PRIxPTR ", data=0x%" PRIxPTR,
+ (uintptr_t)this, (uintptr_t)data);
+ I(dom, fill == 0);
+ dom->free(data);
+}
+
+template <typename T> T *&
+ptr_vec<T>::operator[](size_t offset) {
+ I(dom, data[offset]->idx == offset);
+ return data[offset];
+}
+
+template <typename T>
+void
+ptr_vec<T>::push(T *p)
+{
+ I(dom, data);
+ I(dom, fill <= alloc);
+ if (fill == alloc) {
+ alloc *= 2;
+ data = (T **)dom->realloc(data, alloc * sizeof(T*));
+ I(dom, data);
+ }
+ I(dom, fill < alloc);
+ p->idx = fill;
+ data[fill++] = p;
+}
+
+template <typename T>
+T *
+ptr_vec<T>::pop()
+{
+ return data[--fill];
+}
+
+template <typename T>
+void
+ptr_vec<T>::trim(size_t sz)
+{
+ I(dom, data);
+ if (sz <= (alloc / 4) &&
+ (alloc / 2) >= INIT_SIZE) {
+ alloc /= 2;
+ I(dom, alloc >= fill);
+ data = (T **)dom->realloc(data, alloc * sizeof(T*));
+ I(dom, data);
+ }
+}
+
+template <typename T>
+void
+ptr_vec<T>::swapdel(T *item)
+{
+ /* Swap the endpoint into i and decr fill. */
+ I(dom, data);
+ I(dom, fill > 0);
+ I(dom, item->idx < fill);
+ fill--;
+ if (fill > 0) {
+ T *subst = data[fill];
+ size_t idx = item->idx;
+ data[idx] = subst;
+ subst->idx = idx;
+ }
+}
+
+// Inline fn used regularly elsewhere.
+
+static inline size_t
+next_power_of_two(size_t s)
+{
+ size_t tmp = s - 1;
+ tmp |= tmp >> 1;
+ tmp |= tmp >> 2;
+ tmp |= tmp >> 4;
+ tmp |= tmp >> 8;
+ tmp |= tmp >> 16;
+#if SIZE_MAX == UINT64_MAX
+ tmp |= tmp >> 32;
+#endif
+ return tmp + 1;
+}
+
+// Vectors (rust-user-code level).
+
+struct
+rust_vec : public rc_base<rust_vec>
+{
+ size_t alloc;
+ size_t fill;
+ uint8_t data[];
+ rust_vec(rust_dom *dom, size_t alloc, size_t fill, uint8_t const *d) :
+ alloc(alloc),
+ fill(fill)
+ {
+ if (d || fill) {
+ I(dom, d);
+ I(dom, fill);
+ memcpy(&data[0], d, fill);
+ }
+ }
+ ~rust_vec() {}
+};
+
+// Rust types vec and str look identical from our perspective.
+typedef rust_vec rust_str;
+
+//
+// Local Variables:
+// mode: C++
+// 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:
+//
+
+#endif
diff --git a/src/rt/sync/fair_ticket_lock.cpp b/src/rt/sync/fair_ticket_lock.cpp
new file mode 100644
index 00000000..0306ee1d
--- /dev/null
+++ b/src/rt/sync/fair_ticket_lock.cpp
@@ -0,0 +1,43 @@
+/*
+ * This works well as long as the number of contending threads
+ * is less than the number of processors. This is because of
+ * the fair locking scheme. If the thread that is next in line
+ * for acquiring the lock is not currently running, no other
+ * thread can acquire the lock. This is terrible for performance,
+ * and it seems that all fair locking schemes suffer from this
+ * behavior.
+ */
+
+// #define TRACE
+
+fair_ticket_lock::fair_ticket_lock() {
+ next_ticket = now_serving = 0;
+}
+
+fair_ticket_lock::~fair_ticket_lock() {
+
+}
+
+void fair_ticket_lock::lock() {
+ unsigned ticket = __sync_fetch_and_add(&next_ticket, 1);
+ while (now_serving != ticket) {
+ pause();
+ }
+#ifdef TRACE
+ printf("locked nextTicket: %d nowServing: %d",
+ next_ticket, now_serving);
+#endif
+}
+
+void fair_ticket_lock::unlock() {
+ now_serving++;
+#ifdef TRACE
+ printf("unlocked nextTicket: %d nowServing: %d",
+ next_ticket, now_serving);
+#endif
+}
+
+void fair_ticket_lock::pause() {
+ asm volatile("pause\n" : : : "memory");
+}
+
diff --git a/src/rt/sync/fair_ticket_lock.h b/src/rt/sync/fair_ticket_lock.h
new file mode 100644
index 00000000..c34c9041
--- /dev/null
+++ b/src/rt/sync/fair_ticket_lock.h
@@ -0,0 +1,15 @@
+#ifndef FAIR_TICKET_LOCK_H
+#define FAIR_TICKET_LOCK_H
+
+class fair_ticket_lock {
+ unsigned next_ticket;
+ unsigned now_serving;
+ void pause();
+public:
+ fair_ticket_lock();
+ virtual ~fair_ticket_lock();
+ void lock();
+ void unlock();
+};
+
+#endif /* FAIR_TICKET_LOCK_H */
diff --git a/src/rt/sync/lock_free_queue.cpp b/src/rt/sync/lock_free_queue.cpp
new file mode 100644
index 00000000..9d1081de
--- /dev/null
+++ b/src/rt/sync/lock_free_queue.cpp
@@ -0,0 +1,37 @@
+/*
+ * Interrupt transparent queue, Schoen et. al, "On Interrupt-Transparent
+ * Synchronization in an Embedded Object-Oriented Operating System", 2000.
+ * enqueue() is allowed to interrupt enqueue() and dequeue(), however,
+ * dequeue() is not allowed to interrupt itself.
+ */
+
+#include "lock_free_queue.h"
+
+lock_free_queue::lock_free_queue() :
+ tail(this) {
+}
+
+void lock_free_queue::enqueue(lock_free_queue_node *item) {
+ item->next = (lock_free_queue_node *) 0;
+ lock_free_queue_node *last = tail;
+ tail = item;
+ while (last->next)
+ last = last->next;
+ last->next = item;
+}
+
+lock_free_queue_node *lockfree_queue::dequeue() {
+ lock_free_queue_node *item = next;
+ if (item && !(next = item->next)) {
+ tail = (lock_free_queue_node *) this;
+ if (item->next) {
+ lock_free_queue_node *lost = item->next;
+ lock_free_queue_node *help;
+ do {
+ help = lost->next;
+ enqueue(lost);
+ } while ((lost = help) != (lock_free_queue_node *) 0);
+ }
+ }
+ return item;
+}
diff --git a/src/rt/sync/lock_free_queue.h b/src/rt/sync/lock_free_queue.h
new file mode 100644
index 00000000..fba4aa9a
--- /dev/null
+++ b/src/rt/sync/lock_free_queue.h
@@ -0,0 +1,15 @@
+#ifndef LOCK_FREE_QUEUE_H
+#define LOCK_FREE_QUEUE_H
+
+class lock_free_queue_node {
+ lock_free_queue_node *next;
+};
+
+class lock_free_queue {
+public:
+ lock_free_queue();
+ void enqueue(lock_free_queue_node *item);
+ lock_free_queue_node *dequeue();
+};
+
+#endif /* LOCK_FREE_QUEUE_H */
diff --git a/src/rt/sync/spin_lock.cpp b/src/rt/sync/spin_lock.cpp
new file mode 100644
index 00000000..11a5cb20
--- /dev/null
+++ b/src/rt/sync/spin_lock.cpp
@@ -0,0 +1,47 @@
+/*
+ * Your average spin lock.
+ */
+
+#include "globals.h"
+
+// #define TRACE
+
+spin_lock::spin_lock() {
+ unlock();
+}
+
+spin_lock::~spin_lock() {
+}
+
+static inline unsigned xchg32(void *ptr, unsigned x) {
+ __asm__ __volatile__("xchgl %0,%1"
+ :"=r" ((unsigned) x)
+ :"m" (*(volatile unsigned *)ptr), "0" (x)
+ :"memory");
+ return x;
+}
+
+void spin_lock::lock() {
+ while (true) {
+ if (!xchg32(&ticket, 1)) {
+ return;
+ }
+ while (ticket) {
+ pause();
+ }
+ }
+#ifdef TRACE
+ printf(" lock: %d", ticket);
+#endif
+}
+
+void spin_lock::unlock() {
+ ticket = 0;
+#ifdef TRACE
+ printf("unlock:");
+#endif
+}
+
+void spin_lock::pause() {
+ asm volatile("pause\n" : : : "memory");
+}
diff --git a/src/rt/sync/spin_lock.h b/src/rt/sync/spin_lock.h
new file mode 100644
index 00000000..3684c23a
--- /dev/null
+++ b/src/rt/sync/spin_lock.h
@@ -0,0 +1,14 @@
+#ifndef UNFAIR_TICKET_LOCK_H
+#define UNFAIR_TICKET_LOCK_H
+
+class spin_lock {
+ unsigned ticket;
+ void pause();
+public:
+ spin_lock();
+ virtual ~spin_lock();
+ void lock();
+ void unlock();
+};
+
+#endif /* UNFAIR_TICKET_LOCK_H */
diff --git a/src/rt/uthash/uthash.h b/src/rt/uthash/uthash.h
new file mode 100644
index 00000000..28021b61
--- /dev/null
+++ b/src/rt/uthash/uthash.h
@@ -0,0 +1,766 @@
+/*
+Copyright (c) 2003-2009, Troy D. Hanson http://uthash.sourceforge.net
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef UTHASH_H
+#define UTHASH_H
+
+#include <string.h> /* memcmp,strlen */
+#include <stddef.h> /* ptrdiff_t */
+#include <inttypes.h> /* uint32_t etc */
+
+#define UTHASH_VERSION 1.6
+
+/* C++ requires extra stringent casting */
+#if defined __cplusplus
+#define TYPEOF(x) (typeof(x))
+#else
+#define TYPEOF(x)
+#endif
+
+
+#define uthash_fatal(msg) exit(-1) /* fatal error (out of memory,etc) */
+#define uthash_bkt_malloc(sz) malloc(sz) /* malloc fcn for UT_hash_bucket's */
+#define uthash_bkt_free(ptr) free(ptr) /* free fcn for UT_hash_bucket's */
+#define uthash_tbl_malloc(sz) malloc(sz) /* malloc fcn for UT_hash_table */
+#define uthash_tbl_free(ptr) free(ptr) /* free fcn for UT_hash_table */
+
+#define uthash_noexpand_fyi(tbl) /* can be defined to log noexpand */
+#define uthash_expand_fyi(tbl) /* can be defined to log expands */
+
+/* initial number of buckets */
+#define HASH_INITIAL_NUM_BUCKETS 32 /* initial number of buckets */
+#define HASH_INITIAL_NUM_BUCKETS_LOG2 5 /* lg2 of initial number of buckets */
+#define HASH_BKT_CAPACITY_THRESH 10 /* expand when bucket count reaches */
+
+/* calculate the element whose hash handle address is hhe */
+#define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)hhp) - (tbl)->hho))
+
+#define HASH_FIND(hh,head,keyptr,keylen,out) \
+do { \
+ unsigned _hf_bkt,_hf_hashv; \
+ out=TYPEOF(out)head; \
+ if (head) { \
+ HASH_FCN(keyptr,keylen, (head)->hh.tbl->num_buckets, _hf_hashv, _hf_bkt); \
+ HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], \
+ keyptr,keylen,out); \
+ } \
+} while (0)
+
+#define HASH_MAKE_TABLE(hh,head) \
+do { \
+ (head)->hh.tbl = (UT_hash_table*)uthash_tbl_malloc( \
+ sizeof(UT_hash_table)); \
+ if (!((head)->hh.tbl)) { uthash_fatal( "out of memory"); } \
+ memset((head)->hh.tbl, 0, sizeof(UT_hash_table)); \
+ (head)->hh.tbl->tail = &((head)->hh); \
+ (head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS; \
+ (head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2; \
+ (head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head); \
+ (head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_bkt_malloc( \
+ HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \
+ if (! (head)->hh.tbl->buckets) { uthash_fatal( "out of memory"); } \
+ memset((head)->hh.tbl->buckets, 0, \
+ HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \
+} while(0)
+
+#define HASH_ADD(hh,head,fieldname,keylen_in,add) \
+ HASH_ADD_KEYPTR(hh,head,&add->fieldname,keylen_in,add)
+
+#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add) \
+do { \
+ unsigned _ha_bkt; \
+ (add)->hh.next = NULL; \
+ (add)->hh.key = (char*)keyptr; \
+ (add)->hh.keylen = keylen_in; \
+ if (!(head)) { \
+ head = (add); \
+ (head)->hh.prev = NULL; \
+ HASH_MAKE_TABLE(hh,head); \
+ } else { \
+ (head)->hh.tbl->tail->next = (add); \
+ (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail); \
+ (head)->hh.tbl->tail = &((add)->hh); \
+ } \
+ (head)->hh.tbl->num_items++; \
+ (add)->hh.tbl = (head)->hh.tbl; \
+ HASH_FCN(keyptr,keylen_in, (head)->hh.tbl->num_buckets, \
+ (add)->hh.hashv, _ha_bkt); \
+ HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt],&(add)->hh); \
+ HASH_EMIT_KEY(hh,head,keyptr,keylen_in); \
+ HASH_FSCK(hh,head); \
+} while(0)
+
+#define HASH_TO_BKT( hashv, num_bkts, bkt ) \
+do { \
+ bkt = ((hashv) & ((num_bkts) - 1)); \
+} while(0)
+
+/* delete "delptr" from the hash table.
+ * "the usual" patch-up process for the app-order doubly-linked-list.
+ * The use of _hd_hh_del below deserves special explanation.
+ * These used to be expressed using (delptr) but that led to a bug
+ * if someone used the same symbol for the head and deletee, like
+ * HASH_DELETE(hh,users,users);
+ * We want that to work, but by changing the head (users) below
+ * we were forfeiting our ability to further refer to the deletee (users)
+ * in the patch-up process. Solution: use scratch space in the table to
+ * copy the deletee pointer, then the latter references are via that
+ * scratch pointer rather than through the repointed (users) symbol.
+ */
+#define HASH_DELETE(hh,head,delptr) \
+do { \
+ unsigned _hd_bkt; \
+ struct UT_hash_handle *_hd_hh_del; \
+ if ( ((delptr)->hh.prev == NULL) && ((delptr)->hh.next == NULL) ) { \
+ uthash_bkt_free((head)->hh.tbl->buckets ); \
+ uthash_tbl_free((head)->hh.tbl); \
+ head = NULL; \
+ } else { \
+ _hd_hh_del = &((delptr)->hh); \
+ if ((delptr) == ELMT_FROM_HH((head)->hh.tbl,(head)->hh.tbl->tail)) { \
+ (head)->hh.tbl->tail = \
+ (UT_hash_handle*)((char*)((delptr)->hh.prev) + \
+ (head)->hh.tbl->hho); \
+ } \
+ if ((delptr)->hh.prev) { \
+ ((UT_hash_handle*)((char*)((delptr)->hh.prev) + \
+ (head)->hh.tbl->hho))->next = (delptr)->hh.next; \
+ } else { \
+ head = TYPEOF(head)((delptr)->hh.next); \
+ } \
+ if (_hd_hh_del->next) { \
+ ((UT_hash_handle*)((char*)_hd_hh_del->next + \
+ (head)->hh.tbl->hho))->prev = \
+ _hd_hh_del->prev; \
+ } \
+ HASH_TO_BKT( _hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \
+ HASH_DEL_IN_BKT(hh,(head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del); \
+ (head)->hh.tbl->num_items--; \
+ } \
+ HASH_FSCK(hh,head); \
+} while (0)
+
+
+/* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */
+#define HASH_FIND_STR(head,findstr,out) \
+ HASH_FIND(hh,head,findstr,strlen(findstr),out)
+#define HASH_ADD_STR(head,strfield,add) \
+ HASH_ADD(hh,head,strfield,strlen(add->strfield),add)
+#define HASH_FIND_INT(head,findint,out) \
+ HASH_FIND(hh,head,findint,sizeof(int),out)
+#define HASH_ADD_INT(head,intfield,add) \
+ HASH_ADD(hh,head,intfield,sizeof(int),add)
+#define HASH_DEL(head,delptr) \
+ HASH_DELETE(hh,head,delptr)
+
+/* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined.
+ * This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined.
+ */
+#ifdef HASH_DEBUG
+#define HASH_OOPS(...) do { fprintf(stderr,__VA_ARGS__); exit(-1); } while (0)
+#define HASH_FSCK(hh,head) \
+do { \
+ unsigned _bkt_i; \
+ unsigned _count, _bkt_count; \
+ char *_prev; \
+ struct UT_hash_handle *_thh; \
+ if (head) { \
+ _count = 0; \
+ for( _bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; _bkt_i++) { \
+ _bkt_count = 0; \
+ _thh = (head)->hh.tbl->buckets[_bkt_i].hh_head; \
+ _prev = NULL; \
+ while (_thh) { \
+ if (_prev != (char*)(_thh->hh_prev)) { \
+ HASH_OOPS("invalid hh_prev %p, actual %p\n", \
+ _thh->hh_prev, _prev ); \
+ } \
+ _bkt_count++; \
+ _prev = (char*)(_thh); \
+ _thh = _thh->hh_next; \
+ } \
+ _count += _bkt_count; \
+ if ((head)->hh.tbl->buckets[_bkt_i].count != _bkt_count) { \
+ HASH_OOPS("invalid bucket count %d, actual %d\n", \
+ (head)->hh.tbl->buckets[_bkt_i].count, _bkt_count); \
+ } \
+ } \
+ if (_count != (head)->hh.tbl->num_items) { \
+ HASH_OOPS("invalid hh item count %d, actual %d\n", \
+ (head)->hh.tbl->num_items, _count ); \
+ } \
+ /* traverse hh in app order; check next/prev integrity, count */ \
+ _count = 0; \
+ _prev = NULL; \
+ _thh = &(head)->hh; \
+ while (_thh) { \
+ _count++; \
+ if (_prev !=(char*)(_thh->prev)) { \
+ HASH_OOPS("invalid prev %p, actual %p\n", \
+ _thh->prev, _prev ); \
+ } \
+ _prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh); \
+ _thh = ( _thh->next ? (UT_hash_handle*)((char*)(_thh->next) + \
+ (head)->hh.tbl->hho) : NULL ); \
+ } \
+ if (_count != (head)->hh.tbl->num_items) { \
+ HASH_OOPS("invalid app item count %d, actual %d\n", \
+ (head)->hh.tbl->num_items, _count ); \
+ } \
+ } \
+} while (0)
+#else
+#define HASH_FSCK(hh,head)
+#endif
+
+/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to
+ * the descriptor to which this macro is defined for tuning the hash function.
+ * The app can #include <unistd.h> to get the prototype for write(2). */
+#ifdef HASH_EMIT_KEYS
+#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) \
+do { \
+ unsigned _klen = fieldlen; \
+ write(HASH_EMIT_KEYS, &_klen, sizeof(_klen)); \
+ write(HASH_EMIT_KEYS, keyptr, fieldlen); \
+} while (0)
+#else
+#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen)
+#endif
+
+/* default to MurmurHash unless overridden e.g. DHASH_FUNCTION=HASH_SAX */
+#ifdef HASH_FUNCTION
+#define HASH_FCN HASH_FUNCTION
+#else
+#define HASH_FCN HASH_MUR
+#endif
+
+/* The Bernstein hash function, used in Perl prior to v5.6 */
+#define HASH_BER(key,keylen,num_bkts,hashv,bkt) \
+do { \
+ unsigned _hb_keylen=keylen; \
+ char *_hb_key=(char*)key; \
+ (hashv) = 0; \
+ while (_hb_keylen--) { (hashv) = ((hashv) * 33) + *_hb_key++; } \
+ bkt = (hashv) & (num_bkts-1); \
+} while (0)
+
+
+/* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at
+ * http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx */
+#define HASH_SAX(key,keylen,num_bkts,hashv,bkt) \
+do { \
+ unsigned _sx_i; \
+ char *_hs_key=(char*)key; \
+ hashv = 0; \
+ for(_sx_i=0; _sx_i < keylen; _sx_i++) \
+ hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i]; \
+ bkt = hashv & (num_bkts-1); \
+} while (0)
+
+#define HASH_FNV(key,keylen,num_bkts,hashv,bkt) \
+do { \
+ unsigned _fn_i; \
+ char *_hf_key=(char*)key; \
+ hashv = 2166136261UL; \
+ for(_fn_i=0; _fn_i < keylen; _fn_i++) \
+ hashv = (hashv * 16777619) ^ _hf_key[_fn_i]; \
+ bkt = hashv & (num_bkts-1); \
+} while(0);
+
+#define HASH_OAT(key,keylen,num_bkts,hashv,bkt) \
+do { \
+ unsigned _ho_i; \
+ char *_ho_key=(char*)key; \
+ hashv = 0; \
+ for(_ho_i=0; _ho_i < keylen; _ho_i++) { \
+ hashv += _ho_key[_ho_i]; \
+ hashv += (hashv << 10); \
+ hashv ^= (hashv >> 6); \
+ } \
+ hashv += (hashv << 3); \
+ hashv ^= (hashv >> 11); \
+ hashv += (hashv << 15); \
+ bkt = hashv & (num_bkts-1); \
+} while(0)
+
+#define HASH_JEN_MIX(a,b,c) \
+do { \
+ a -= b; a -= c; a ^= ( c >> 13 ); \
+ b -= c; b -= a; b ^= ( a << 8 ); \
+ c -= a; c -= b; c ^= ( b >> 13 ); \
+ a -= b; a -= c; a ^= ( c >> 12 ); \
+ b -= c; b -= a; b ^= ( a << 16 ); \
+ c -= a; c -= b; c ^= ( b >> 5 ); \
+ a -= b; a -= c; a ^= ( c >> 3 ); \
+ b -= c; b -= a; b ^= ( a << 10 ); \
+ c -= a; c -= b; c ^= ( b >> 15 ); \
+} while (0)
+
+#define HASH_JEN(key,keylen,num_bkts,hashv,bkt) \
+do { \
+ unsigned _hj_i,_hj_j,_hj_k; \
+ char *_hj_key=(char*)key; \
+ hashv = 0xfeedbeef; \
+ _hj_i = _hj_j = 0x9e3779b9; \
+ _hj_k = keylen; \
+ while (_hj_k >= 12) { \
+ _hj_i += (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 ) \
+ + ( (unsigned)_hj_key[2] << 16 ) \
+ + ( (unsigned)_hj_key[3] << 24 ) ); \
+ _hj_j += (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 ) \
+ + ( (unsigned)_hj_key[6] << 16 ) \
+ + ( (unsigned)_hj_key[7] << 24 ) ); \
+ hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 ) \
+ + ( (unsigned)_hj_key[10] << 16 ) \
+ + ( (unsigned)_hj_key[11] << 24 ) ); \
+ \
+ HASH_JEN_MIX(_hj_i, _hj_j, hashv); \
+ \
+ _hj_key += 12; \
+ _hj_k -= 12; \
+ } \
+ hashv += keylen; \
+ switch ( _hj_k ) { \
+ case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); \
+ case 10: hashv += ( (unsigned)_hj_key[9] << 16 ); \
+ case 9: hashv += ( (unsigned)_hj_key[8] << 8 ); \
+ case 8: _hj_j += ( (unsigned)_hj_key[7] << 24 ); \
+ case 7: _hj_j += ( (unsigned)_hj_key[6] << 16 ); \
+ case 6: _hj_j += ( (unsigned)_hj_key[5] << 8 ); \
+ case 5: _hj_j += _hj_key[4]; \
+ case 4: _hj_i += ( (unsigned)_hj_key[3] << 24 ); \
+ case 3: _hj_i += ( (unsigned)_hj_key[2] << 16 ); \
+ case 2: _hj_i += ( (unsigned)_hj_key[1] << 8 ); \
+ case 1: _hj_i += _hj_key[0]; \
+ } \
+ HASH_JEN_MIX(_hj_i, _hj_j, hashv); \
+ bkt = hashv & (num_bkts-1); \
+} while(0)
+
+/* The Paul Hsieh hash function */
+#undef get16bits
+#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \
+ || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__)
+#define get16bits(d) (*((const uint16_t *) (d)))
+#endif
+
+#if !defined (get16bits)
+#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8)\
+ +(uint32_t)(((const uint8_t *)(d))[0]) )
+#endif
+#define HASH_SFH(key,keylen,num_bkts,hashv,bkt) \
+do { \
+ char *_sfh_key=(char*)key; \
+ hashv = 0xcafebabe; \
+ uint32_t _sfh_tmp, _sfh_len = keylen; \
+ \
+ int _sfh_rem = _sfh_len & 3; \
+ _sfh_len >>= 2; \
+ \
+ /* Main loop */ \
+ for (;_sfh_len > 0; _sfh_len--) { \
+ hashv += get16bits (_sfh_key); \
+ _sfh_tmp = (get16bits (_sfh_key+2) << 11) ^ hashv; \
+ hashv = (hashv << 16) ^ _sfh_tmp; \
+ _sfh_key += 2*sizeof (uint16_t); \
+ hashv += hashv >> 11; \
+ } \
+ \
+ /* Handle end cases */ \
+ switch (_sfh_rem) { \
+ case 3: hashv += get16bits (_sfh_key); \
+ hashv ^= hashv << 16; \
+ hashv ^= _sfh_key[sizeof (uint16_t)] << 18; \
+ hashv += hashv >> 11; \
+ break; \
+ case 2: hashv += get16bits (_sfh_key); \
+ hashv ^= hashv << 11; \
+ hashv += hashv >> 17; \
+ break; \
+ case 1: hashv += *_sfh_key; \
+ hashv ^= hashv << 10; \
+ hashv += hashv >> 1; \
+ } \
+ \
+ /* Force "avalanching" of final 127 bits */ \
+ hashv ^= hashv << 3; \
+ hashv += hashv >> 5; \
+ hashv ^= hashv << 4; \
+ hashv += hashv >> 17; \
+ hashv ^= hashv << 25; \
+ hashv += hashv >> 6; \
+ bkt = hashv & (num_bkts-1); \
+} while(0);
+
+/* Austin Appleby's MurmurHash */
+#define HASH_MUR(key,keylen,num_bkts,hashv,bkt) \
+do { \
+ const unsigned int _mur_m = 0x5bd1e995; \
+ const int _mur_r = 24; \
+ hashv = 0xcafebabe ^ keylen; \
+ char *_mur_key = (char *)key; \
+ uint32_t _mur_tmp, _mur_len = keylen; \
+ \
+ for (;_mur_len >= 4; _mur_len-=4) { \
+ _mur_tmp = *(uint32_t *)_mur_key; \
+ _mur_tmp *= _mur_m; \
+ _mur_tmp ^= _mur_tmp >> _mur_r; \
+ _mur_tmp *= _mur_m; \
+ hashv *= _mur_m; \
+ hashv ^= _mur_tmp; \
+ _mur_key += 4; \
+ } \
+ \
+ switch(_mur_len) \
+ { \
+ case 3: hashv ^= _mur_key[2] << 16; \
+ case 2: hashv ^= _mur_key[1] << 8; \
+ case 1: hashv ^= _mur_key[0]; \
+ hashv *= _mur_m; \
+ }; \
+ \
+ hashv ^= hashv >> 13; \
+ hashv *= _mur_m; \
+ hashv ^= hashv >> 15; \
+ \
+ bkt = hashv & (num_bkts-1); \
+} while(0)
+
+/* key comparison function; return 0 if keys equal */
+#define HASH_KEYCMP(a,b,len) memcmp(a,b,len)
+
+/* iterate over items in a known bucket to find desired item */
+#define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,out) \
+out = TYPEOF(out)((head.hh_head) ? ELMT_FROM_HH(tbl,head.hh_head) : NULL); \
+while (out) { \
+ if (out->hh.keylen == keylen_in) { \
+ if ((HASH_KEYCMP(out->hh.key,keyptr,keylen_in)) == 0) break; \
+ } \
+ out= TYPEOF(out)((out->hh.hh_next) ? \
+ ELMT_FROM_HH(tbl,out->hh.hh_next) : NULL); \
+}
+
+/* add an item to a bucket */
+#define HASH_ADD_TO_BKT(head,addhh) \
+do { \
+ head.count++; \
+ (addhh)->hh_next = head.hh_head; \
+ (addhh)->hh_prev = NULL; \
+ if (head.hh_head) { (head).hh_head->hh_prev = (addhh); } \
+ (head).hh_head=addhh; \
+ if (head.count >= ((head.expand_mult+1) * HASH_BKT_CAPACITY_THRESH) \
+ && (addhh)->tbl->noexpand != 1) { \
+ HASH_EXPAND_BUCKETS((addhh)->tbl); \
+ } \
+} while(0)
+
+/* remove an item from a given bucket */
+#define HASH_DEL_IN_BKT(hh,head,hh_del) \
+ (head).count--; \
+ if ((head).hh_head == hh_del) { \
+ (head).hh_head = hh_del->hh_next; \
+ } \
+ if (hh_del->hh_prev) { \
+ hh_del->hh_prev->hh_next = hh_del->hh_next; \
+ } \
+ if (hh_del->hh_next) { \
+ hh_del->hh_next->hh_prev = hh_del->hh_prev; \
+ }
+
+/* Bucket expansion has the effect of doubling the number of buckets
+ * and redistributing the items into the new buckets. Ideally the
+ * items will distribute more or less evenly into the new buckets
+ * (the extent to which this is true is a measure of the quality of
+ * the hash function as it applies to the key domain).
+ *
+ * With the items distributed into more buckets, the chain length
+ * (item count) in each bucket is reduced. Thus by expanding buckets
+ * the hash keeps a bound on the chain length. This bounded chain
+ * length is the essence of how a hash provides constant time lookup.
+ *
+ * The calculation of tbl->ideal_chain_maxlen below deserves some
+ * explanation. First, keep in mind that we're calculating the ideal
+ * maximum chain length based on the *new* (doubled) bucket count.
+ * In fractions this is just n/b (n=number of items,b=new num buckets).
+ * Since the ideal chain length is an integer, we want to calculate
+ * ceil(n/b). We don't depend on floating point arithmetic in this
+ * hash, so to calculate ceil(n/b) with integers we could write
+ *
+ * ceil(n/b) = (n/b) + ((n%b)?1:0)
+ *
+ * and in fact a previous version of this hash did just that.
+ * But now we have improved things a bit by recognizing that b is
+ * always a power of two. We keep its base 2 log handy (call it lb),
+ * so now we can write this with a bit shift and logical AND:
+ *
+ * ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0)
+ *
+ */
+#define HASH_EXPAND_BUCKETS(tbl) \
+do { \
+ unsigned _he_bkt; \
+ unsigned _he_bkt_i; \
+ struct UT_hash_handle *_he_thh, *_he_hh_nxt; \
+ UT_hash_bucket *_he_new_buckets, *_he_newbkt; \
+ _he_new_buckets = (UT_hash_bucket*)uthash_bkt_malloc( \
+ 2 * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \
+ if (!_he_new_buckets) { uthash_fatal( "out of memory"); } \
+ memset(_he_new_buckets, 0, \
+ 2 * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \
+ tbl->ideal_chain_maxlen = \
+ (tbl->num_items >> (tbl->log2_num_buckets+1)) + \
+ ((tbl->num_items & ((tbl->num_buckets*2)-1)) ? 1 : 0); \
+ tbl->nonideal_items = 0; \
+ for(_he_bkt_i = 0; _he_bkt_i < tbl->num_buckets; _he_bkt_i++) \
+ { \
+ _he_thh = tbl->buckets[ _he_bkt_i ].hh_head; \
+ while (_he_thh) { \
+ _he_hh_nxt = _he_thh->hh_next; \
+ HASH_TO_BKT( _he_thh->hashv, tbl->num_buckets*2, _he_bkt); \
+ _he_newbkt = &(_he_new_buckets[ _he_bkt ]); \
+ if (++(_he_newbkt->count) > tbl->ideal_chain_maxlen) { \
+ tbl->nonideal_items++; \
+ _he_newbkt->expand_mult = _he_newbkt->count / \
+ tbl->ideal_chain_maxlen; \
+ } \
+ _he_thh->hh_prev = NULL; \
+ _he_thh->hh_next = _he_newbkt->hh_head; \
+ if (_he_newbkt->hh_head) _he_newbkt->hh_head->hh_prev = \
+ _he_thh; \
+ _he_newbkt->hh_head = _he_thh; \
+ _he_thh = _he_hh_nxt; \
+ } \
+ } \
+ tbl->num_buckets *= 2; \
+ tbl->log2_num_buckets++; \
+ uthash_bkt_free( tbl->buckets ); \
+ tbl->buckets = _he_new_buckets; \
+ tbl->ineff_expands = (tbl->nonideal_items > (tbl->num_items >> 1)) ? \
+ (tbl->ineff_expands+1) : 0; \
+ if (tbl->ineff_expands > 1) { \
+ tbl->noexpand=1; \
+ uthash_noexpand_fyi(tbl); \
+ } \
+ uthash_expand_fyi(tbl); \
+} while(0)
+
+
+/* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */
+/* Note that HASH_SORT assumes the hash handle name to be hh.
+ * HASH_SRT was added to allow the hash handle name to be passed in. */
+#define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn)
+#define HASH_SRT(hh,head,cmpfcn) \
+do { \
+ unsigned _hs_i; \
+ unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize; \
+ struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail; \
+ if (head) { \
+ _hs_insize = 1; \
+ _hs_looping = 1; \
+ _hs_list = &((head)->hh); \
+ while (_hs_looping) { \
+ _hs_p = _hs_list; \
+ _hs_list = NULL; \
+ _hs_tail = NULL; \
+ _hs_nmerges = 0; \
+ while (_hs_p) { \
+ _hs_nmerges++; \
+ _hs_q = _hs_p; \
+ _hs_psize = 0; \
+ for ( _hs_i = 0; _hs_i < _hs_insize; _hs_i++ ) { \
+ _hs_psize++; \
+ _hs_q = (UT_hash_handle*)((_hs_q->next) ? \
+ ((void*)((char*)(_hs_q->next) + \
+ (head)->hh.tbl->hho)) : NULL); \
+ if (! (_hs_q) ) break; \
+ } \
+ _hs_qsize = _hs_insize; \
+ while ((_hs_psize > 0) || ((_hs_qsize > 0) && _hs_q )) { \
+ if (_hs_psize == 0) { \
+ _hs_e = _hs_q; \
+ _hs_q = (UT_hash_handle*)((_hs_q->next) ? \
+ ((void*)((char*)(_hs_q->next) + \
+ (head)->hh.tbl->hho)) : NULL); \
+ _hs_qsize--; \
+ } else if ( (_hs_qsize == 0) || !(_hs_q) ) { \
+ _hs_e = _hs_p; \
+ _hs_p = (UT_hash_handle*)((_hs_p->next) ? \
+ ((void*)((char*)(_hs_p->next) + \
+ (head)->hh.tbl->hho)) : NULL); \
+ _hs_psize--; \
+ } else if (( \
+ cmpfcn(TYPEOF(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_p)), \
+ TYPEOF(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_q))) \
+ ) <= 0) { \
+ _hs_e = _hs_p; \
+ _hs_p = (UT_hash_handle*)((_hs_p->next) ? \
+ ((void*)((char*)(_hs_p->next) + \
+ (head)->hh.tbl->hho)) : NULL); \
+ _hs_psize--; \
+ } else { \
+ _hs_e = _hs_q; \
+ _hs_q = (UT_hash_handle*)((_hs_q->next) ? \
+ ((void*)((char*)(_hs_q->next) + \
+ (head)->hh.tbl->hho)) : NULL); \
+ _hs_qsize--; \
+ } \
+ if ( _hs_tail ) { \
+ _hs_tail->next = ((_hs_e) ? \
+ ELMT_FROM_HH((head)->hh.tbl,_hs_e) : NULL); \
+ } else { \
+ _hs_list = _hs_e; \
+ } \
+ _hs_e->prev = ((_hs_tail) ? \
+ ELMT_FROM_HH((head)->hh.tbl,_hs_tail) : NULL); \
+ _hs_tail = _hs_e; \
+ } \
+ _hs_p = _hs_q; \
+ } \
+ _hs_tail->next = NULL; \
+ if ( _hs_nmerges <= 1 ) { \
+ _hs_looping=0; \
+ (head)->hh.tbl->tail = _hs_tail; \
+ (head) = TYPEOF(head)ELMT_FROM_HH((head)->hh.tbl, _hs_list); \
+ } \
+ _hs_insize *= 2; \
+ } \
+ HASH_FSCK(hh,head); \
+ } \
+} while (0)
+
+/* This function selects items from one hash into another hash.
+ * The end result is that the selected items have dual presence
+ * in both hashes. There is no copy of the items made; rather
+ * they are added into the new hash through a secondary hash
+ * hash handle that must be present in the structure. */
+#define HASH_SELECT(hh_dst, dst, hh_src, src, cond) \
+do { \
+ unsigned _src_bkt, _dst_bkt; \
+ void *_last_elt=NULL, *_elt; \
+ UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL; \
+ ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst)); \
+ if (src) { \
+ for(_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) { \
+ for(_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head; \
+ _src_hh; \
+ _src_hh = _src_hh->hh_next) { \
+ _elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh); \
+ if (cond(_elt)) { \
+ _dst_hh = (UT_hash_handle*)(((char*)_elt) + _dst_hho); \
+ _dst_hh->key = _src_hh->key; \
+ _dst_hh->keylen = _src_hh->keylen; \
+ _dst_hh->hashv = _src_hh->hashv; \
+ _dst_hh->prev = _last_elt; \
+ _dst_hh->next = NULL; \
+ if (_last_elt_hh) { _last_elt_hh->next = _elt; } \
+ if (!dst) { \
+ dst = TYPEOF(dst)_elt; \
+ HASH_MAKE_TABLE(hh_dst,dst); \
+ } else { \
+ _dst_hh->tbl = (dst)->hh_dst.tbl; \
+ } \
+ HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt); \
+ HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt],_dst_hh); \
+ (dst)->hh_dst.tbl->num_items++; \
+ _last_elt = _elt; \
+ _last_elt_hh = _dst_hh; \
+ } \
+ } \
+ } \
+ } \
+ HASH_FSCK(hh_dst,dst); \
+} while (0)
+
+#define HASH_CLEAR(hh,head) \
+do { \
+ if (head) { \
+ uthash_bkt_free((head)->hh.tbl->buckets ); \
+ uthash_tbl_free((head)->hh.tbl); \
+ (head)=NULL; \
+ } \
+} while(0)
+
+/* obtain a count of items in the hash */
+#define HASH_COUNT(head) HASH_CNT(hh,head)
+#define HASH_CNT(hh,head) (head?(head->hh.tbl->num_items):0)
+
+typedef struct UT_hash_bucket {
+ struct UT_hash_handle *hh_head;
+ unsigned count;
+
+ /* expand_mult is normally set to 0. In this situation, the max chain length
+ * threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If
+ * the bucket's chain exceeds this length, bucket expansion is triggered).
+ * However, setting expand_mult to a non-zero value delays bucket expansion
+ * (that would be triggered by additions to this particular bucket)
+ * until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH.
+ * (The multiplier is simply expand_mult+1). The whole idea of this
+ * multiplier is to reduce bucket expansions, since they are expensive, in
+ * situations where we know that a particular bucket tends to be overused.
+ * It is better to let its chain length grow to a longer yet-still-bounded
+ * value, than to do an O(n) bucket expansion too often.
+ */
+ unsigned expand_mult;
+
+} UT_hash_bucket;
+
+typedef struct UT_hash_table {
+ UT_hash_bucket *buckets;
+ unsigned num_buckets, log2_num_buckets;
+ unsigned num_items;
+ struct UT_hash_handle *tail; /* tail hh in app order, for fast append */
+ ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */
+
+ /* in an ideal situation (all buckets used equally), no bucket would have
+ * more than ceil(#items/#buckets) items. that's the ideal chain length. */
+ unsigned ideal_chain_maxlen;
+
+ /* nonideal_items is the number of items in the hash whose chain position
+ * exceeds the ideal chain maxlen. these items pay the penalty for an uneven
+ * hash distribution; reaching them in a chain traversal takes >ideal steps */
+ unsigned nonideal_items;
+
+ /* ineffective expands occur when a bucket doubling was performed, but
+ * afterward, more than half the items in the hash had nonideal chain
+ * positions. If this happens on two consecutive expansions we inhibit any
+ * further expansion, as it's not helping; this happens when the hash
+ * function isn't a good fit for the key domain. When expansion is inhibited
+ * the hash will still work, albeit no longer in constant time. */
+ unsigned ineff_expands, noexpand;
+
+
+} UT_hash_table;
+
+
+typedef struct UT_hash_handle {
+ struct UT_hash_table *tbl;
+ void *prev; /* prev element in app order */
+ void *next; /* next element in app order */
+ struct UT_hash_handle *hh_prev; /* previous hh in bucket order */
+ struct UT_hash_handle *hh_next; /* next hh in bucket order */
+ void *key; /* ptr to enclosing struct's key */
+ unsigned keylen; /* enclosing struct's key len */
+ unsigned hashv; /* result of hash-fcn(key) */
+} UT_hash_handle;
+
+#endif /* UTHASH_H */
diff --git a/src/rt/uthash/utlist.h b/src/rt/uthash/utlist.h
new file mode 100644
index 00000000..a33615e1
--- /dev/null
+++ b/src/rt/uthash/utlist.h
@@ -0,0 +1,280 @@
+/*
+Copyright (c) 2007-2009, Troy D. Hanson
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef UTLIST_H
+#define UTLIST_H
+
+#define UTLIST_VERSION 1.0
+
+/* C++ requires extra stringent casting */
+#if defined __cplusplus
+#define LTYPEOF(x) (typeof(x))
+#else
+#define LTYPEOF(x)
+#endif
+/*
+ * This file contains macros to manipulate singly and doubly-linked lists.
+ *
+ * 1. LL_ macros: singly-linked lists.
+ * 2. DL_ macros: doubly-linked lists.
+ * 3. CDL_ macros: circular doubly-linked lists.
+ *
+ * To use singly-linked lists, your structure must have a "next" pointer.
+ * To use doubly-linked lists, your structure must "prev" and "next" pointers.
+ * Either way, the pointer to the head of the list must be initialized to NULL.
+ *
+ * ----------------.EXAMPLE -------------------------
+ * struct item {
+ * int id;
+ * struct item *prev, *next;
+ * }
+ *
+ * struct item *list = NULL:
+ *
+ * int main() {
+ * struct item *item;
+ * ... allocate and populate item ...
+ * DL_APPEND(list, item);
+ * }
+ * --------------------------------------------------
+ *
+ * For doubly-linked lists, the append and delete macros are O(1)
+ * For singly-linked lists, append and delete are O(n) but prepend is O(1)
+ * The sort macro is O(n log(n)) for all types of single/double/circular lists.
+ */
+
+/******************************************************************************
+ * The SORT macros *
+ *****************************************************************************/
+#define LL_SORT(l,cmp) \
+ LISTSORT(l,0,0,FIELD_OFFSET(l,next),cmp)
+#define DL_SORT(l,cmp) \
+ LISTSORT(l,0,FIELD_OFFSET(l,prev),FIELD_OFFSET(l,next),cmp)
+#define CDL_SORT(l,cmp) \
+ LISTSORT(l,1,FIELD_OFFSET(l,prev),FIELD_OFFSET(l,next),cmp)
+
+/* The macros can't assume or cast to the caller's list element type. So we use
+ * a couple tricks when we need to deal with those element's prev/next pointers.
+ * Basically we use char pointer arithmetic to get those field offsets. */
+#define FIELD_OFFSET(ptr,field) ((char*)&((ptr)->field) - (char*)(ptr))
+#define LNEXT(e,no) (*(char**)(((char*)e) + no))
+#define LPREV(e,po) (*(char**)(((char*)e) + po))
+/******************************************************************************
+ * The LISTSORT macro is an adaptation of Simon Tatham's O(n log(n)) mergesort*
+ * Unwieldy variable names used here to avoid shadowing passed-in variables. *
+ *****************************************************************************/
+#define LISTSORT(list, is_circular, po, no, cmp) \
+do { \
+ void *_ls_p, *_ls_q, *_ls_e, *_ls_tail, *_ls_oldhead; \
+ int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \
+ int _ls_is_double = (po==0) ? 0 : 1; \
+ if (list) { \
+ _ls_insize = 1; \
+ _ls_looping = 1; \
+ while (_ls_looping) { \
+ _ls_p = list; \
+ _ls_oldhead = list; \
+ list = NULL; \
+ _ls_tail = NULL; \
+ _ls_nmerges = 0; \
+ while (_ls_p) { \
+ _ls_nmerges++; \
+ _ls_q = _ls_p; \
+ _ls_psize = 0; \
+ for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \
+ _ls_psize++; \
+ if (is_circular) { \
+ _ls_q = ((LNEXT(_ls_q,no) == _ls_oldhead) ? NULL : LNEXT(_ls_q,no)); \
+ } else { \
+ _ls_q = LNEXT(_ls_q,no); \
+ } \
+ if (!_ls_q) break; \
+ } \
+ _ls_qsize = _ls_insize; \
+ while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \
+ if (_ls_psize == 0) { \
+ _ls_e = _ls_q; _ls_q = LNEXT(_ls_q,no); _ls_qsize--; \
+ if (is_circular && _ls_q == _ls_oldhead) { _ls_q = NULL; } \
+ } else if (_ls_qsize == 0 || !_ls_q) { \
+ _ls_e = _ls_p; _ls_p = LNEXT(_ls_p,no); _ls_psize--; \
+ if (is_circular && (_ls_p == _ls_oldhead)) { _ls_p = NULL; } \
+ } else if (cmp(LTYPEOF(list)_ls_p,LTYPEOF(list)_ls_q) <= 0) { \
+ _ls_e = _ls_p; _ls_p = LNEXT(_ls_p,no); _ls_psize--; \
+ if (is_circular && (_ls_p == _ls_oldhead)) { _ls_p = NULL; } \
+ } else { \
+ _ls_e = _ls_q; _ls_q = LNEXT(_ls_q,no); _ls_qsize--; \
+ if (is_circular && (_ls_q == _ls_oldhead)) { _ls_q = NULL; } \
+ } \
+ if (_ls_tail) { \
+ LNEXT(_ls_tail,no) = (char*)_ls_e; \
+ } else { \
+ list = LTYPEOF(list)_ls_e; \
+ } \
+ if (_ls_is_double) { \
+ LPREV(_ls_e,po) = (char*)_ls_tail; \
+ } \
+ _ls_tail = _ls_e; \
+ } \
+ _ls_p = _ls_q; \
+ } \
+ if (is_circular) { \
+ LNEXT(_ls_tail,no) = (char*)list; \
+ if (_ls_is_double) { \
+ LPREV(list,po) = (char*)_ls_tail; \
+ } \
+ } else { \
+ LNEXT(_ls_tail,no) = NULL; \
+ } \
+ if (_ls_nmerges <= 1) { \
+ _ls_looping=0; \
+ } \
+ _ls_insize *= 2; \
+ } \
+ } \
+} while (0)
+
+/******************************************************************************
+ * singly linked list macros (non-circular) *
+ *****************************************************************************/
+#define LL_PREPEND(head,add) \
+do { \
+ (add)->next = head; \
+ head = add; \
+} while (0)
+
+#define LL_APPEND(head,add) \
+do { \
+ (add)->next=NULL; \
+ if (head) { \
+ char *_lla_el = (char*)(head); \
+ unsigned _lla_no = FIELD_OFFSET(head,next); \
+ while (LNEXT(_lla_el,_lla_no)) { _lla_el = LNEXT(_lla_el,_lla_no); } \
+ LNEXT(_lla_el,_lla_no)=(char*)(add); \
+ } else { \
+ (head)=(add); \
+ } \
+} while (0)
+
+#define LL_DELETE(head,del) \
+do { \
+ if ((head) == (del)) { \
+ (head)=(head)->next; \
+ } else { \
+ char *_lld_el = (char*)(head); \
+ unsigned _lld_no = FIELD_OFFSET(head,next); \
+ while (LNEXT(_lld_el,_lld_no) && (LNEXT(_lld_el,_lld_no) != (char*)(del))) { \
+ _lld_el = LNEXT(_lld_el,_lld_no); \
+ } \
+ if (LNEXT(_lld_el,_lld_no)) { \
+ LNEXT(_lld_el,_lld_no) = (char*)((del)->next); \
+ } \
+ } \
+} while (0)
+
+#define LL_FOREACH(head,el) \
+ for(el=head;el;el=el->next)
+
+/******************************************************************************
+ * doubly linked list macros (non-circular) *
+ *****************************************************************************/
+#define DL_PREPEND(head,add) \
+do { \
+ (add)->next = head; \
+ if (head) { \
+ (add)->prev = (head)->prev; \
+ (head)->prev = (add); \
+ } else { \
+ (add)->prev = (add); \
+ } \
+ (head) = (add); \
+} while (0)
+
+#define DL_APPEND(head,add) \
+do { \
+ if (head) { \
+ (add)->prev = (head)->prev; \
+ (head)->prev->next = (add); \
+ (head)->prev = (add); \
+ (add)->next = NULL; \
+ } else { \
+ (head)=(add); \
+ (head)->prev = (head); \
+ (head)->next = NULL; \
+ } \
+} while (0);
+
+#define DL_DELETE(head,del) \
+do { \
+ if ((del)->prev == (del)) { \
+ (head)=NULL; \
+ } else if ((del)==(head)) { \
+ (del)->next->prev = (del)->prev; \
+ (head) = (del)->next; \
+ } else { \
+ (del)->prev->next = (del)->next; \
+ if ((del)->next) { \
+ (del)->next->prev = (del)->prev; \
+ } else { \
+ (head)->prev = (del)->prev; \
+ } \
+ } \
+} while (0);
+
+
+#define DL_FOREACH(head,el) \
+ for(el=head;el;el=el->next)
+
+/******************************************************************************
+ * circular doubly linked list macros *
+ *****************************************************************************/
+#define CDL_PREPEND(head,add) \
+do { \
+ if (head) { \
+ (add)->prev = (head)->prev; \
+ (add)->next = (head); \
+ (head)->prev = (add); \
+ (add)->prev->next = (add); \
+ } else { \
+ (add)->prev = (add); \
+ (add)->next = (add); \
+ } \
+(head)=(add); \
+} while (0)
+
+#define CDL_DELETE(head,del) \
+do { \
+ if ( ((head)==(del)) && ((head)->next == (head))) { \
+ (head) = 0L; \
+ } else { \
+ (del)->next->prev = (del)->prev; \
+ (del)->prev->next = (del)->next; \
+ if ((del) == (head)) (head)=(del)->next; \
+ } \
+} while (0);
+
+#define CDL_FOREACH(head,el) \
+ for(el=head;el;el= (el->next==head ? 0L : el->next))
+
+
+#endif /* UTLIST_H */
+
diff --git a/src/rt/util/array_list.h b/src/rt/util/array_list.h
new file mode 100644
index 00000000..0d112575
--- /dev/null
+++ b/src/rt/util/array_list.h
@@ -0,0 +1,69 @@
+#ifndef ARRAY_LIST_H
+#define ARRAY_LIST_H
+
+/**
+ * A simple, resizable array list.
+ */
+template<typename T> class array_list {
+ static const size_t INITIAL_CAPACITY = 8;
+ size_t _size;
+ T * _data;
+ size_t _capacity;
+public:
+ array_list();
+ ~array_list();
+ size_t size();
+ void append(T value);
+ T replace(T old_value, T new_value);
+ size_t index_of(T value);
+ T & operator[](size_t index);
+};
+
+template<typename T> array_list<T>::array_list() {
+ _capacity = INITIAL_CAPACITY;
+ _data = (T *) malloc(sizeof(T) * _capacity);
+}
+
+template<typename T> array_list<T>::~array_list() {
+ delete _data;
+}
+
+template<typename T> size_t array_list<T>::size() {
+ return _size;
+}
+
+template<typename T> void array_list<T>::append(T value) {
+ if (_size == _capacity) {
+ _capacity = _capacity * 2;
+ _data = (T *) realloc(_data, _capacity * sizeof(T));
+ }
+ _data[_size++] = value;
+}
+
+/**
+ * Replaces the old_value in the list with the new_value.
+ * Returns the old_value if the replacement succeeded, or NULL otherwise.
+ */
+template<typename T> T array_list<T>::replace(T old_value, T new_value) {
+ int index = index_of(old_value);
+ if (index < 0) {
+ return NULL;
+ }
+ _data[index] = new_value;
+ return old_value;
+}
+
+template<typename T> size_t array_list<T>::index_of(T value) {
+ for (size_t i = 0; i < _size; i++) {
+ if (_data[i] == value) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+template<typename T> T & array_list<T>::operator[](size_t index) {
+ return _data[index];
+}
+
+#endif /* ARRAY_LIST_H */
diff --git a/src/rt/valgrind.h b/src/rt/valgrind.h
new file mode 100644
index 00000000..530fa184
--- /dev/null
+++ b/src/rt/valgrind.h
@@ -0,0 +1,3926 @@
+/* -*- c -*-
+ ----------------------------------------------------------------
+
+ Notice that the following BSD-style license applies to this one
+ file (valgrind.h) only. The rest of Valgrind is licensed under the
+ terms of the GNU General Public License, version 2, unless
+ otherwise indicated. See the COPYING file in the source
+ distribution for details.
+
+ ----------------------------------------------------------------
+
+ This file is part of Valgrind, a dynamic binary instrumentation
+ framework.
+
+ Copyright (C) 2000-2008 Julian Seward. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ 2. The origin of this software must not be misrepresented; you must
+ not claim that you wrote the original software. If you use this
+ software in a product, an acknowledgment in the product
+ documentation would be appreciated but is not required.
+
+ 3. Altered source versions must be plainly marked as such, and must
+ not be misrepresented as being the original software.
+
+ 4. The name of the author may not be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ ----------------------------------------------------------------
+
+ Notice that the above BSD-style license applies to this one file
+ (valgrind.h) only. The entire rest of Valgrind is licensed under
+ the terms of the GNU General Public License, version 2. See the
+ COPYING file in the source distribution for details.
+
+ ----------------------------------------------------------------
+*/
+
+
+/* This file is for inclusion into client (your!) code.
+
+ You can use these macros to manipulate and query Valgrind's
+ execution inside your own programs.
+
+ The resulting executables will still run without Valgrind, just a
+ little bit more slowly than they otherwise would, but otherwise
+ unchanged. When not running on valgrind, each client request
+ consumes very few (eg. 7) instructions, so the resulting performance
+ loss is negligible unless you plan to execute client requests
+ millions of times per second. Nevertheless, if that is still a
+ problem, you can compile with the NVALGRIND symbol defined (gcc
+ -DNVALGRIND) so that client requests are not even compiled in. */
+
+#ifndef __VALGRIND_H
+#define __VALGRIND_H
+
+#include <stdarg.h>
+
+/* Nb: this file might be included in a file compiled with -ansi. So
+ we can't use C++ style "//" comments nor the "asm" keyword (instead
+ use "__asm__"). */
+
+/* Derive some tags indicating what the target platform is. Note
+ that in this file we're using the compiler's CPP symbols for
+ identifying architectures, which are different to the ones we use
+ within the rest of Valgrind. Note, __powerpc__ is active for both
+ 32 and 64-bit PPC, whereas __powerpc64__ is only active for the
+ latter (on Linux, that is). */
+#undef PLAT_x86_linux
+#undef PLAT_amd64_linux
+#undef PLAT_ppc32_linux
+#undef PLAT_ppc64_linux
+#undef PLAT_ppc32_aix5
+#undef PLAT_ppc64_aix5
+
+#if !defined(_AIX) && defined(__i386__)
+# define PLAT_x86_linux 1
+#elif !defined(_AIX) && defined(__x86_64__)
+# define PLAT_amd64_linux 1
+#elif !defined(_AIX) && defined(__powerpc__) && !defined(__powerpc64__)
+# define PLAT_ppc32_linux 1
+#elif !defined(_AIX) && defined(__powerpc__) && defined(__powerpc64__)
+# define PLAT_ppc64_linux 1
+#elif defined(_AIX) && defined(__64BIT__)
+# define PLAT_ppc64_aix5 1
+#elif defined(_AIX) && !defined(__64BIT__)
+# define PLAT_ppc32_aix5 1
+#endif
+
+
+/* If we're not compiling for our target platform, don't generate
+ any inline asms. */
+#if !defined(PLAT_x86_linux) && !defined(PLAT_amd64_linux) \
+ && !defined(PLAT_ppc32_linux) && !defined(PLAT_ppc64_linux) \
+ && !defined(PLAT_ppc32_aix5) && !defined(PLAT_ppc64_aix5)
+# if !defined(NVALGRIND)
+# define NVALGRIND 1
+# endif
+#endif
+
+
+/* ------------------------------------------------------------------ */
+/* ARCHITECTURE SPECIFICS for SPECIAL INSTRUCTIONS. There is nothing */
+/* in here of use to end-users -- skip to the next section. */
+/* ------------------------------------------------------------------ */
+
+#if defined(NVALGRIND)
+
+/* Define NVALGRIND to completely remove the Valgrind magic sequence
+ from the compiled code (analogous to NDEBUG's effects on
+ assert()) */
+#define VALGRIND_DO_CLIENT_REQUEST( \
+ _zzq_rlval, _zzq_default, _zzq_request, \
+ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \
+ { \
+ (_zzq_rlval) = (_zzq_default); \
+ }
+
+#else /* ! NVALGRIND */
+
+/* The following defines the magic code sequences which the JITter
+ spots and handles magically. Don't look too closely at them as
+ they will rot your brain.
+
+ The assembly code sequences for all architectures is in this one
+ file. This is because this file must be stand-alone, and we don't
+ want to have multiple files.
+
+ For VALGRIND_DO_CLIENT_REQUEST, we must ensure that the default
+ value gets put in the return slot, so that everything works when
+ this is executed not under Valgrind. Args are passed in a memory
+ block, and so there's no intrinsic limit to the number that could
+ be passed, but it's currently five.
+
+ The macro args are:
+ _zzq_rlval result lvalue
+ _zzq_default default value (result returned when running on real CPU)
+ _zzq_request request code
+ _zzq_arg1..5 request params
+
+ The other two macros are used to support function wrapping, and are
+ a lot simpler. VALGRIND_GET_NR_CONTEXT returns the value of the
+ guest's NRADDR pseudo-register and whatever other information is
+ needed to safely run the call original from the wrapper: on
+ ppc64-linux, the R2 value at the divert point is also needed. This
+ information is abstracted into a user-visible type, OrigFn.
+
+ VALGRIND_CALL_NOREDIR_* behaves the same as the following on the
+ guest, but guarantees that the branch instruction will not be
+ redirected: x86: call *%eax, amd64: call *%rax, ppc32/ppc64:
+ branch-and-link-to-r11. VALGRIND_CALL_NOREDIR is just text, not a
+ complete inline asm, since it needs to be combined with more magic
+ inline asm stuff to be useful.
+*/
+
+/* ------------------------- x86-linux ------------------------- */
+
+#if defined(PLAT_x86_linux)
+
+typedef
+ struct {
+ unsigned int nraddr; /* where's the code? */
+ }
+ OrigFn;
+
+#define __SPECIAL_INSTRUCTION_PREAMBLE \
+ "roll $3, %%edi ; roll $13, %%edi\n\t" \
+ "roll $29, %%edi ; roll $19, %%edi\n\t"
+
+#define VALGRIND_DO_CLIENT_REQUEST( \
+ _zzq_rlval, _zzq_default, _zzq_request, \
+ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \
+ { volatile unsigned int _zzq_args[6]; \
+ volatile unsigned int _zzq_result; \
+ _zzq_args[0] = (unsigned int)(_zzq_request); \
+ _zzq_args[1] = (unsigned int)(_zzq_arg1); \
+ _zzq_args[2] = (unsigned int)(_zzq_arg2); \
+ _zzq_args[3] = (unsigned int)(_zzq_arg3); \
+ _zzq_args[4] = (unsigned int)(_zzq_arg4); \
+ _zzq_args[5] = (unsigned int)(_zzq_arg5); \
+ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
+ /* %EDX = client_request ( %EAX ) */ \
+ "xchgl %%ebx,%%ebx" \
+ : "=d" (_zzq_result) \
+ : "a" (&_zzq_args[0]), "0" (_zzq_default) \
+ : "cc", "memory" \
+ ); \
+ _zzq_rlval = _zzq_result; \
+ }
+
+#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \
+ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \
+ volatile unsigned int __addr; \
+ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
+ /* %EAX = guest_NRADDR */ \
+ "xchgl %%ecx,%%ecx" \
+ : "=a" (__addr) \
+ : \
+ : "cc", "memory" \
+ ); \
+ _zzq_orig->nraddr = __addr; \
+ }
+
+#define VALGRIND_CALL_NOREDIR_EAX \
+ __SPECIAL_INSTRUCTION_PREAMBLE \
+ /* call-noredir *%EAX */ \
+ "xchgl %%edx,%%edx\n\t"
+#endif /* PLAT_x86_linux */
+
+/* ------------------------ amd64-linux ------------------------ */
+
+#if defined(PLAT_amd64_linux)
+
+typedef
+ struct {
+ unsigned long long int nraddr; /* where's the code? */
+ }
+ OrigFn;
+
+#define __SPECIAL_INSTRUCTION_PREAMBLE \
+ "rolq $3, %%rdi ; rolq $13, %%rdi\n\t" \
+ "rolq $61, %%rdi ; rolq $51, %%rdi\n\t"
+
+#define VALGRIND_DO_CLIENT_REQUEST( \
+ _zzq_rlval, _zzq_default, _zzq_request, \
+ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \
+ { volatile unsigned long long int _zzq_args[6]; \
+ volatile unsigned long long int _zzq_result; \
+ _zzq_args[0] = (unsigned long long int)(_zzq_request); \
+ _zzq_args[1] = (unsigned long long int)(_zzq_arg1); \
+ _zzq_args[2] = (unsigned long long int)(_zzq_arg2); \
+ _zzq_args[3] = (unsigned long long int)(_zzq_arg3); \
+ _zzq_args[4] = (unsigned long long int)(_zzq_arg4); \
+ _zzq_args[5] = (unsigned long long int)(_zzq_arg5); \
+ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
+ /* %RDX = client_request ( %RAX ) */ \
+ "xchgq %%rbx,%%rbx" \
+ : "=d" (_zzq_result) \
+ : "a" (&_zzq_args[0]), "0" (_zzq_default) \
+ : "cc", "memory" \
+ ); \
+ _zzq_rlval = _zzq_result; \
+ }
+
+#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \
+ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \
+ volatile unsigned long long int __addr; \
+ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
+ /* %RAX = guest_NRADDR */ \
+ "xchgq %%rcx,%%rcx" \
+ : "=a" (__addr) \
+ : \
+ : "cc", "memory" \
+ ); \
+ _zzq_orig->nraddr = __addr; \
+ }
+
+#define VALGRIND_CALL_NOREDIR_RAX \
+ __SPECIAL_INSTRUCTION_PREAMBLE \
+ /* call-noredir *%RAX */ \
+ "xchgq %%rdx,%%rdx\n\t"
+#endif /* PLAT_amd64_linux */
+
+/* ------------------------ ppc32-linux ------------------------ */
+
+#if defined(PLAT_ppc32_linux)
+
+typedef
+ struct {
+ unsigned int nraddr; /* where's the code? */
+ }
+ OrigFn;
+
+#define __SPECIAL_INSTRUCTION_PREAMBLE \
+ "rlwinm 0,0,3,0,0 ; rlwinm 0,0,13,0,0\n\t" \
+ "rlwinm 0,0,29,0,0 ; rlwinm 0,0,19,0,0\n\t"
+
+#define VALGRIND_DO_CLIENT_REQUEST( \
+ _zzq_rlval, _zzq_default, _zzq_request, \
+ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \
+ \
+ { unsigned int _zzq_args[6]; \
+ unsigned int _zzq_result; \
+ unsigned int* _zzq_ptr; \
+ _zzq_args[0] = (unsigned int)(_zzq_request); \
+ _zzq_args[1] = (unsigned int)(_zzq_arg1); \
+ _zzq_args[2] = (unsigned int)(_zzq_arg2); \
+ _zzq_args[3] = (unsigned int)(_zzq_arg3); \
+ _zzq_args[4] = (unsigned int)(_zzq_arg4); \
+ _zzq_args[5] = (unsigned int)(_zzq_arg5); \
+ _zzq_ptr = _zzq_args; \
+ __asm__ volatile("mr 3,%1\n\t" /*default*/ \
+ "mr 4,%2\n\t" /*ptr*/ \
+ __SPECIAL_INSTRUCTION_PREAMBLE \
+ /* %R3 = client_request ( %R4 ) */ \
+ "or 1,1,1\n\t" \
+ "mr %0,3" /*result*/ \
+ : "=b" (_zzq_result) \
+ : "b" (_zzq_default), "b" (_zzq_ptr) \
+ : "cc", "memory", "r3", "r4"); \
+ _zzq_rlval = _zzq_result; \
+ }
+
+#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \
+ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \
+ unsigned int __addr; \
+ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
+ /* %R3 = guest_NRADDR */ \
+ "or 2,2,2\n\t" \
+ "mr %0,3" \
+ : "=b" (__addr) \
+ : \
+ : "cc", "memory", "r3" \
+ ); \
+ _zzq_orig->nraddr = __addr; \
+ }
+
+#define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ __SPECIAL_INSTRUCTION_PREAMBLE \
+ /* branch-and-link-to-noredir *%R11 */ \
+ "or 3,3,3\n\t"
+#endif /* PLAT_ppc32_linux */
+
+/* ------------------------ ppc64-linux ------------------------ */
+
+#if defined(PLAT_ppc64_linux)
+
+typedef
+ struct {
+ unsigned long long int nraddr; /* where's the code? */
+ unsigned long long int r2; /* what tocptr do we need? */
+ }
+ OrigFn;
+
+#define __SPECIAL_INSTRUCTION_PREAMBLE \
+ "rotldi 0,0,3 ; rotldi 0,0,13\n\t" \
+ "rotldi 0,0,61 ; rotldi 0,0,51\n\t"
+
+#define VALGRIND_DO_CLIENT_REQUEST( \
+ _zzq_rlval, _zzq_default, _zzq_request, \
+ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \
+ \
+ { unsigned long long int _zzq_args[6]; \
+ register unsigned long long int _zzq_result __asm__("r3"); \
+ register unsigned long long int* _zzq_ptr __asm__("r4"); \
+ _zzq_args[0] = (unsigned long long int)(_zzq_request); \
+ _zzq_args[1] = (unsigned long long int)(_zzq_arg1); \
+ _zzq_args[2] = (unsigned long long int)(_zzq_arg2); \
+ _zzq_args[3] = (unsigned long long int)(_zzq_arg3); \
+ _zzq_args[4] = (unsigned long long int)(_zzq_arg4); \
+ _zzq_args[5] = (unsigned long long int)(_zzq_arg5); \
+ _zzq_ptr = _zzq_args; \
+ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
+ /* %R3 = client_request ( %R4 ) */ \
+ "or 1,1,1" \
+ : "=r" (_zzq_result) \
+ : "0" (_zzq_default), "r" (_zzq_ptr) \
+ : "cc", "memory"); \
+ _zzq_rlval = _zzq_result; \
+ }
+
+#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \
+ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \
+ register unsigned long long int __addr __asm__("r3"); \
+ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
+ /* %R3 = guest_NRADDR */ \
+ "or 2,2,2" \
+ : "=r" (__addr) \
+ : \
+ : "cc", "memory" \
+ ); \
+ _zzq_orig->nraddr = __addr; \
+ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
+ /* %R3 = guest_NRADDR_GPR2 */ \
+ "or 4,4,4" \
+ : "=r" (__addr) \
+ : \
+ : "cc", "memory" \
+ ); \
+ _zzq_orig->r2 = __addr; \
+ }
+
+#define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ __SPECIAL_INSTRUCTION_PREAMBLE \
+ /* branch-and-link-to-noredir *%R11 */ \
+ "or 3,3,3\n\t"
+
+#endif /* PLAT_ppc64_linux */
+
+/* ------------------------ ppc32-aix5 ------------------------- */
+
+#if defined(PLAT_ppc32_aix5)
+
+typedef
+ struct {
+ unsigned int nraddr; /* where's the code? */
+ unsigned int r2; /* what tocptr do we need? */
+ }
+ OrigFn;
+
+#define __SPECIAL_INSTRUCTION_PREAMBLE \
+ "rlwinm 0,0,3,0,0 ; rlwinm 0,0,13,0,0\n\t" \
+ "rlwinm 0,0,29,0,0 ; rlwinm 0,0,19,0,0\n\t"
+
+#define VALGRIND_DO_CLIENT_REQUEST( \
+ _zzq_rlval, _zzq_default, _zzq_request, \
+ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \
+ \
+ { unsigned int _zzq_args[7]; \
+ register unsigned int _zzq_result; \
+ register unsigned int* _zzq_ptr; \
+ _zzq_args[0] = (unsigned int)(_zzq_request); \
+ _zzq_args[1] = (unsigned int)(_zzq_arg1); \
+ _zzq_args[2] = (unsigned int)(_zzq_arg2); \
+ _zzq_args[3] = (unsigned int)(_zzq_arg3); \
+ _zzq_args[4] = (unsigned int)(_zzq_arg4); \
+ _zzq_args[5] = (unsigned int)(_zzq_arg5); \
+ _zzq_args[6] = (unsigned int)(_zzq_default); \
+ _zzq_ptr = _zzq_args; \
+ __asm__ volatile("mr 4,%1\n\t" \
+ "lwz 3, 24(4)\n\t" \
+ __SPECIAL_INSTRUCTION_PREAMBLE \
+ /* %R3 = client_request ( %R4 ) */ \
+ "or 1,1,1\n\t" \
+ "mr %0,3" \
+ : "=b" (_zzq_result) \
+ : "b" (_zzq_ptr) \
+ : "r3", "r4", "cc", "memory"); \
+ _zzq_rlval = _zzq_result; \
+ }
+
+#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \
+ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \
+ register unsigned int __addr; \
+ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
+ /* %R3 = guest_NRADDR */ \
+ "or 2,2,2\n\t" \
+ "mr %0,3" \
+ : "=b" (__addr) \
+ : \
+ : "r3", "cc", "memory" \
+ ); \
+ _zzq_orig->nraddr = __addr; \
+ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
+ /* %R3 = guest_NRADDR_GPR2 */ \
+ "or 4,4,4\n\t" \
+ "mr %0,3" \
+ : "=b" (__addr) \
+ : \
+ : "r3", "cc", "memory" \
+ ); \
+ _zzq_orig->r2 = __addr; \
+ }
+
+#define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ __SPECIAL_INSTRUCTION_PREAMBLE \
+ /* branch-and-link-to-noredir *%R11 */ \
+ "or 3,3,3\n\t"
+
+#endif /* PLAT_ppc32_aix5 */
+
+/* ------------------------ ppc64-aix5 ------------------------- */
+
+#if defined(PLAT_ppc64_aix5)
+
+typedef
+ struct {
+ unsigned long long int nraddr; /* where's the code? */
+ unsigned long long int r2; /* what tocptr do we need? */
+ }
+ OrigFn;
+
+#define __SPECIAL_INSTRUCTION_PREAMBLE \
+ "rotldi 0,0,3 ; rotldi 0,0,13\n\t" \
+ "rotldi 0,0,61 ; rotldi 0,0,51\n\t"
+
+#define VALGRIND_DO_CLIENT_REQUEST( \
+ _zzq_rlval, _zzq_default, _zzq_request, \
+ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \
+ \
+ { unsigned long long int _zzq_args[7]; \
+ register unsigned long long int _zzq_result; \
+ register unsigned long long int* _zzq_ptr; \
+ _zzq_args[0] = (unsigned int long long)(_zzq_request); \
+ _zzq_args[1] = (unsigned int long long)(_zzq_arg1); \
+ _zzq_args[2] = (unsigned int long long)(_zzq_arg2); \
+ _zzq_args[3] = (unsigned int long long)(_zzq_arg3); \
+ _zzq_args[4] = (unsigned int long long)(_zzq_arg4); \
+ _zzq_args[5] = (unsigned int long long)(_zzq_arg5); \
+ _zzq_args[6] = (unsigned int long long)(_zzq_default); \
+ _zzq_ptr = _zzq_args; \
+ __asm__ volatile("mr 4,%1\n\t" \
+ "ld 3, 48(4)\n\t" \
+ __SPECIAL_INSTRUCTION_PREAMBLE \
+ /* %R3 = client_request ( %R4 ) */ \
+ "or 1,1,1\n\t" \
+ "mr %0,3" \
+ : "=b" (_zzq_result) \
+ : "b" (_zzq_ptr) \
+ : "r3", "r4", "cc", "memory"); \
+ _zzq_rlval = _zzq_result; \
+ }
+
+#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \
+ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \
+ register unsigned long long int __addr; \
+ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
+ /* %R3 = guest_NRADDR */ \
+ "or 2,2,2\n\t" \
+ "mr %0,3" \
+ : "=b" (__addr) \
+ : \
+ : "r3", "cc", "memory" \
+ ); \
+ _zzq_orig->nraddr = __addr; \
+ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
+ /* %R3 = guest_NRADDR_GPR2 */ \
+ "or 4,4,4\n\t" \
+ "mr %0,3" \
+ : "=b" (__addr) \
+ : \
+ : "r3", "cc", "memory" \
+ ); \
+ _zzq_orig->r2 = __addr; \
+ }
+
+#define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ __SPECIAL_INSTRUCTION_PREAMBLE \
+ /* branch-and-link-to-noredir *%R11 */ \
+ "or 3,3,3\n\t"
+
+#endif /* PLAT_ppc64_aix5 */
+
+/* Insert assembly code for other platforms here... */
+
+#endif /* NVALGRIND */
+
+
+/* ------------------------------------------------------------------ */
+/* PLATFORM SPECIFICS for FUNCTION WRAPPING. This is all very */
+/* ugly. It's the least-worst tradeoff I can think of. */
+/* ------------------------------------------------------------------ */
+
+/* This section defines magic (a.k.a appalling-hack) macros for doing
+ guaranteed-no-redirection macros, so as to get from function
+ wrappers to the functions they are wrapping. The whole point is to
+ construct standard call sequences, but to do the call itself with a
+ special no-redirect call pseudo-instruction that the JIT
+ understands and handles specially. This section is long and
+ repetitious, and I can't see a way to make it shorter.
+
+ The naming scheme is as follows:
+
+ CALL_FN_{W,v}_{v,W,WW,WWW,WWWW,5W,6W,7W,etc}
+
+ 'W' stands for "word" and 'v' for "void". Hence there are
+ different macros for calling arity 0, 1, 2, 3, 4, etc, functions,
+ and for each, the possibility of returning a word-typed result, or
+ no result.
+*/
+
+/* Use these to write the name of your wrapper. NOTE: duplicates
+ VG_WRAP_FUNCTION_Z{U,Z} in pub_tool_redir.h. */
+
+#define I_WRAP_SONAME_FNNAME_ZU(soname,fnname) \
+ _vgwZU_##soname##_##fnname
+
+#define I_WRAP_SONAME_FNNAME_ZZ(soname,fnname) \
+ _vgwZZ_##soname##_##fnname
+
+/* Use this macro from within a wrapper function to collect the
+ context (address and possibly other info) of the original function.
+ Once you have that you can then use it in one of the CALL_FN_
+ macros. The type of the argument _lval is OrigFn. */
+#define VALGRIND_GET_ORIG_FN(_lval) VALGRIND_GET_NR_CONTEXT(_lval)
+
+/* Derivatives of the main macros below, for calling functions
+ returning void. */
+
+#define CALL_FN_v_v(fnptr) \
+ do { volatile unsigned long _junk; \
+ CALL_FN_W_v(_junk,fnptr); } while (0)
+
+#define CALL_FN_v_W(fnptr, arg1) \
+ do { volatile unsigned long _junk; \
+ CALL_FN_W_W(_junk,fnptr,arg1); } while (0)
+
+#define CALL_FN_v_WW(fnptr, arg1,arg2) \
+ do { volatile unsigned long _junk; \
+ CALL_FN_W_WW(_junk,fnptr,arg1,arg2); } while (0)
+
+#define CALL_FN_v_WWW(fnptr, arg1,arg2,arg3) \
+ do { volatile unsigned long _junk; \
+ CALL_FN_W_WWW(_junk,fnptr,arg1,arg2,arg3); } while (0)
+
+/* ------------------------- x86-linux ------------------------- */
+
+#if defined(PLAT_x86_linux)
+
+/* These regs are trashed by the hidden call. No need to mention eax
+ as gcc can already see that, plus causes gcc to bomb. */
+#define __CALLER_SAVED_REGS /*"eax"*/ "ecx", "edx"
+
+/* These CALL_FN_ macros assume that on x86-linux, sizeof(unsigned
+ long) == 4. */
+
+#define CALL_FN_W_v(lval, orig) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[1]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ __asm__ volatile( \
+ "movl (%%eax), %%eax\n\t" /* target->%eax */ \
+ VALGRIND_CALL_NOREDIR_EAX \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_W(lval, orig, arg1) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[2]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ __asm__ volatile( \
+ "pushl 4(%%eax)\n\t" \
+ "movl (%%eax), %%eax\n\t" /* target->%eax */ \
+ VALGRIND_CALL_NOREDIR_EAX \
+ "addl $4, %%esp\n" \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WW(lval, orig, arg1,arg2) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ __asm__ volatile( \
+ "pushl 8(%%eax)\n\t" \
+ "pushl 4(%%eax)\n\t" \
+ "movl (%%eax), %%eax\n\t" /* target->%eax */ \
+ VALGRIND_CALL_NOREDIR_EAX \
+ "addl $8, %%esp\n" \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[4]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ __asm__ volatile( \
+ "pushl 12(%%eax)\n\t" \
+ "pushl 8(%%eax)\n\t" \
+ "pushl 4(%%eax)\n\t" \
+ "movl (%%eax), %%eax\n\t" /* target->%eax */ \
+ VALGRIND_CALL_NOREDIR_EAX \
+ "addl $12, %%esp\n" \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[5]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ __asm__ volatile( \
+ "pushl 16(%%eax)\n\t" \
+ "pushl 12(%%eax)\n\t" \
+ "pushl 8(%%eax)\n\t" \
+ "pushl 4(%%eax)\n\t" \
+ "movl (%%eax), %%eax\n\t" /* target->%eax */ \
+ VALGRIND_CALL_NOREDIR_EAX \
+ "addl $16, %%esp\n" \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[6]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ __asm__ volatile( \
+ "pushl 20(%%eax)\n\t" \
+ "pushl 16(%%eax)\n\t" \
+ "pushl 12(%%eax)\n\t" \
+ "pushl 8(%%eax)\n\t" \
+ "pushl 4(%%eax)\n\t" \
+ "movl (%%eax), %%eax\n\t" /* target->%eax */ \
+ VALGRIND_CALL_NOREDIR_EAX \
+ "addl $20, %%esp\n" \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[7]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ __asm__ volatile( \
+ "pushl 24(%%eax)\n\t" \
+ "pushl 20(%%eax)\n\t" \
+ "pushl 16(%%eax)\n\t" \
+ "pushl 12(%%eax)\n\t" \
+ "pushl 8(%%eax)\n\t" \
+ "pushl 4(%%eax)\n\t" \
+ "movl (%%eax), %%eax\n\t" /* target->%eax */ \
+ VALGRIND_CALL_NOREDIR_EAX \
+ "addl $24, %%esp\n" \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[8]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ __asm__ volatile( \
+ "pushl 28(%%eax)\n\t" \
+ "pushl 24(%%eax)\n\t" \
+ "pushl 20(%%eax)\n\t" \
+ "pushl 16(%%eax)\n\t" \
+ "pushl 12(%%eax)\n\t" \
+ "pushl 8(%%eax)\n\t" \
+ "pushl 4(%%eax)\n\t" \
+ "movl (%%eax), %%eax\n\t" /* target->%eax */ \
+ VALGRIND_CALL_NOREDIR_EAX \
+ "addl $28, %%esp\n" \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[9]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ __asm__ volatile( \
+ "pushl 32(%%eax)\n\t" \
+ "pushl 28(%%eax)\n\t" \
+ "pushl 24(%%eax)\n\t" \
+ "pushl 20(%%eax)\n\t" \
+ "pushl 16(%%eax)\n\t" \
+ "pushl 12(%%eax)\n\t" \
+ "pushl 8(%%eax)\n\t" \
+ "pushl 4(%%eax)\n\t" \
+ "movl (%%eax), %%eax\n\t" /* target->%eax */ \
+ VALGRIND_CALL_NOREDIR_EAX \
+ "addl $32, %%esp\n" \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[10]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ _argvec[9] = (unsigned long)(arg9); \
+ __asm__ volatile( \
+ "pushl 36(%%eax)\n\t" \
+ "pushl 32(%%eax)\n\t" \
+ "pushl 28(%%eax)\n\t" \
+ "pushl 24(%%eax)\n\t" \
+ "pushl 20(%%eax)\n\t" \
+ "pushl 16(%%eax)\n\t" \
+ "pushl 12(%%eax)\n\t" \
+ "pushl 8(%%eax)\n\t" \
+ "pushl 4(%%eax)\n\t" \
+ "movl (%%eax), %%eax\n\t" /* target->%eax */ \
+ VALGRIND_CALL_NOREDIR_EAX \
+ "addl $36, %%esp\n" \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9,arg10) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[11]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ _argvec[9] = (unsigned long)(arg9); \
+ _argvec[10] = (unsigned long)(arg10); \
+ __asm__ volatile( \
+ "pushl 40(%%eax)\n\t" \
+ "pushl 36(%%eax)\n\t" \
+ "pushl 32(%%eax)\n\t" \
+ "pushl 28(%%eax)\n\t" \
+ "pushl 24(%%eax)\n\t" \
+ "pushl 20(%%eax)\n\t" \
+ "pushl 16(%%eax)\n\t" \
+ "pushl 12(%%eax)\n\t" \
+ "pushl 8(%%eax)\n\t" \
+ "pushl 4(%%eax)\n\t" \
+ "movl (%%eax), %%eax\n\t" /* target->%eax */ \
+ VALGRIND_CALL_NOREDIR_EAX \
+ "addl $40, %%esp\n" \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5, \
+ arg6,arg7,arg8,arg9,arg10, \
+ arg11) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[12]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ _argvec[9] = (unsigned long)(arg9); \
+ _argvec[10] = (unsigned long)(arg10); \
+ _argvec[11] = (unsigned long)(arg11); \
+ __asm__ volatile( \
+ "pushl 44(%%eax)\n\t" \
+ "pushl 40(%%eax)\n\t" \
+ "pushl 36(%%eax)\n\t" \
+ "pushl 32(%%eax)\n\t" \
+ "pushl 28(%%eax)\n\t" \
+ "pushl 24(%%eax)\n\t" \
+ "pushl 20(%%eax)\n\t" \
+ "pushl 16(%%eax)\n\t" \
+ "pushl 12(%%eax)\n\t" \
+ "pushl 8(%%eax)\n\t" \
+ "pushl 4(%%eax)\n\t" \
+ "movl (%%eax), %%eax\n\t" /* target->%eax */ \
+ VALGRIND_CALL_NOREDIR_EAX \
+ "addl $44, %%esp\n" \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5, \
+ arg6,arg7,arg8,arg9,arg10, \
+ arg11,arg12) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[13]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ _argvec[9] = (unsigned long)(arg9); \
+ _argvec[10] = (unsigned long)(arg10); \
+ _argvec[11] = (unsigned long)(arg11); \
+ _argvec[12] = (unsigned long)(arg12); \
+ __asm__ volatile( \
+ "pushl 48(%%eax)\n\t" \
+ "pushl 44(%%eax)\n\t" \
+ "pushl 40(%%eax)\n\t" \
+ "pushl 36(%%eax)\n\t" \
+ "pushl 32(%%eax)\n\t" \
+ "pushl 28(%%eax)\n\t" \
+ "pushl 24(%%eax)\n\t" \
+ "pushl 20(%%eax)\n\t" \
+ "pushl 16(%%eax)\n\t" \
+ "pushl 12(%%eax)\n\t" \
+ "pushl 8(%%eax)\n\t" \
+ "pushl 4(%%eax)\n\t" \
+ "movl (%%eax), %%eax\n\t" /* target->%eax */ \
+ VALGRIND_CALL_NOREDIR_EAX \
+ "addl $48, %%esp\n" \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#endif /* PLAT_x86_linux */
+
+/* ------------------------ amd64-linux ------------------------ */
+
+#if defined(PLAT_amd64_linux)
+
+/* ARGREGS: rdi rsi rdx rcx r8 r9 (the rest on stack in R-to-L order) */
+
+/* These regs are trashed by the hidden call. */
+#define __CALLER_SAVED_REGS /*"rax",*/ "rcx", "rdx", "rsi", \
+ "rdi", "r8", "r9", "r10", "r11"
+
+/* These CALL_FN_ macros assume that on amd64-linux, sizeof(unsigned
+ long) == 8. */
+
+/* NB 9 Sept 07. There is a nasty kludge here in all these CALL_FN_
+ macros. In order not to trash the stack redzone, we need to drop
+ %rsp by 128 before the hidden call, and restore afterwards. The
+ nastyness is that it is only by luck that the stack still appears
+ to be unwindable during the hidden call - since then the behaviour
+ of any routine using this macro does not match what the CFI data
+ says. Sigh.
+
+ Why is this important? Imagine that a wrapper has a stack
+ allocated local, and passes to the hidden call, a pointer to it.
+ Because gcc does not know about the hidden call, it may allocate
+ that local in the redzone. Unfortunately the hidden call may then
+ trash it before it comes to use it. So we must step clear of the
+ redzone, for the duration of the hidden call, to make it safe.
+
+ Probably the same problem afflicts the other redzone-style ABIs too
+ (ppc64-linux, ppc32-aix5, ppc64-aix5); but for those, the stack is
+ self describing (none of this CFI nonsense) so at least messing
+ with the stack pointer doesn't give a danger of non-unwindable
+ stack. */
+
+#define CALL_FN_W_v(lval, orig) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[1]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ __asm__ volatile( \
+ "subq $128,%%rsp\n\t" \
+ "movq (%%rax), %%rax\n\t" /* target->%rax */ \
+ VALGRIND_CALL_NOREDIR_RAX \
+ "addq $128,%%rsp\n\t" \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_W(lval, orig, arg1) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[2]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ __asm__ volatile( \
+ "subq $128,%%rsp\n\t" \
+ "movq 8(%%rax), %%rdi\n\t" \
+ "movq (%%rax), %%rax\n\t" /* target->%rax */ \
+ VALGRIND_CALL_NOREDIR_RAX \
+ "addq $128,%%rsp\n\t" \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WW(lval, orig, arg1,arg2) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ __asm__ volatile( \
+ "subq $128,%%rsp\n\t" \
+ "movq 16(%%rax), %%rsi\n\t" \
+ "movq 8(%%rax), %%rdi\n\t" \
+ "movq (%%rax), %%rax\n\t" /* target->%rax */ \
+ VALGRIND_CALL_NOREDIR_RAX \
+ "addq $128,%%rsp\n\t" \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[4]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ __asm__ volatile( \
+ "subq $128,%%rsp\n\t" \
+ "movq 24(%%rax), %%rdx\n\t" \
+ "movq 16(%%rax), %%rsi\n\t" \
+ "movq 8(%%rax), %%rdi\n\t" \
+ "movq (%%rax), %%rax\n\t" /* target->%rax */ \
+ VALGRIND_CALL_NOREDIR_RAX \
+ "addq $128,%%rsp\n\t" \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[5]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ __asm__ volatile( \
+ "subq $128,%%rsp\n\t" \
+ "movq 32(%%rax), %%rcx\n\t" \
+ "movq 24(%%rax), %%rdx\n\t" \
+ "movq 16(%%rax), %%rsi\n\t" \
+ "movq 8(%%rax), %%rdi\n\t" \
+ "movq (%%rax), %%rax\n\t" /* target->%rax */ \
+ VALGRIND_CALL_NOREDIR_RAX \
+ "addq $128,%%rsp\n\t" \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[6]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ __asm__ volatile( \
+ "subq $128,%%rsp\n\t" \
+ "movq 40(%%rax), %%r8\n\t" \
+ "movq 32(%%rax), %%rcx\n\t" \
+ "movq 24(%%rax), %%rdx\n\t" \
+ "movq 16(%%rax), %%rsi\n\t" \
+ "movq 8(%%rax), %%rdi\n\t" \
+ "movq (%%rax), %%rax\n\t" /* target->%rax */ \
+ VALGRIND_CALL_NOREDIR_RAX \
+ "addq $128,%%rsp\n\t" \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[7]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ __asm__ volatile( \
+ "subq $128,%%rsp\n\t" \
+ "movq 48(%%rax), %%r9\n\t" \
+ "movq 40(%%rax), %%r8\n\t" \
+ "movq 32(%%rax), %%rcx\n\t" \
+ "movq 24(%%rax), %%rdx\n\t" \
+ "movq 16(%%rax), %%rsi\n\t" \
+ "movq 8(%%rax), %%rdi\n\t" \
+ "movq (%%rax), %%rax\n\t" /* target->%rax */ \
+ "addq $128,%%rsp\n\t" \
+ VALGRIND_CALL_NOREDIR_RAX \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[8]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ __asm__ volatile( \
+ "subq $128,%%rsp\n\t" \
+ "pushq 56(%%rax)\n\t" \
+ "movq 48(%%rax), %%r9\n\t" \
+ "movq 40(%%rax), %%r8\n\t" \
+ "movq 32(%%rax), %%rcx\n\t" \
+ "movq 24(%%rax), %%rdx\n\t" \
+ "movq 16(%%rax), %%rsi\n\t" \
+ "movq 8(%%rax), %%rdi\n\t" \
+ "movq (%%rax), %%rax\n\t" /* target->%rax */ \
+ VALGRIND_CALL_NOREDIR_RAX \
+ "addq $8, %%rsp\n" \
+ "addq $128,%%rsp\n\t" \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[9]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ __asm__ volatile( \
+ "subq $128,%%rsp\n\t" \
+ "pushq 64(%%rax)\n\t" \
+ "pushq 56(%%rax)\n\t" \
+ "movq 48(%%rax), %%r9\n\t" \
+ "movq 40(%%rax), %%r8\n\t" \
+ "movq 32(%%rax), %%rcx\n\t" \
+ "movq 24(%%rax), %%rdx\n\t" \
+ "movq 16(%%rax), %%rsi\n\t" \
+ "movq 8(%%rax), %%rdi\n\t" \
+ "movq (%%rax), %%rax\n\t" /* target->%rax */ \
+ VALGRIND_CALL_NOREDIR_RAX \
+ "addq $16, %%rsp\n" \
+ "addq $128,%%rsp\n\t" \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[10]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ _argvec[9] = (unsigned long)(arg9); \
+ __asm__ volatile( \
+ "subq $128,%%rsp\n\t" \
+ "pushq 72(%%rax)\n\t" \
+ "pushq 64(%%rax)\n\t" \
+ "pushq 56(%%rax)\n\t" \
+ "movq 48(%%rax), %%r9\n\t" \
+ "movq 40(%%rax), %%r8\n\t" \
+ "movq 32(%%rax), %%rcx\n\t" \
+ "movq 24(%%rax), %%rdx\n\t" \
+ "movq 16(%%rax), %%rsi\n\t" \
+ "movq 8(%%rax), %%rdi\n\t" \
+ "movq (%%rax), %%rax\n\t" /* target->%rax */ \
+ VALGRIND_CALL_NOREDIR_RAX \
+ "addq $24, %%rsp\n" \
+ "addq $128,%%rsp\n\t" \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9,arg10) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[11]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ _argvec[9] = (unsigned long)(arg9); \
+ _argvec[10] = (unsigned long)(arg10); \
+ __asm__ volatile( \
+ "subq $128,%%rsp\n\t" \
+ "pushq 80(%%rax)\n\t" \
+ "pushq 72(%%rax)\n\t" \
+ "pushq 64(%%rax)\n\t" \
+ "pushq 56(%%rax)\n\t" \
+ "movq 48(%%rax), %%r9\n\t" \
+ "movq 40(%%rax), %%r8\n\t" \
+ "movq 32(%%rax), %%rcx\n\t" \
+ "movq 24(%%rax), %%rdx\n\t" \
+ "movq 16(%%rax), %%rsi\n\t" \
+ "movq 8(%%rax), %%rdi\n\t" \
+ "movq (%%rax), %%rax\n\t" /* target->%rax */ \
+ VALGRIND_CALL_NOREDIR_RAX \
+ "addq $32, %%rsp\n" \
+ "addq $128,%%rsp\n\t" \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9,arg10,arg11) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[12]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ _argvec[9] = (unsigned long)(arg9); \
+ _argvec[10] = (unsigned long)(arg10); \
+ _argvec[11] = (unsigned long)(arg11); \
+ __asm__ volatile( \
+ "subq $128,%%rsp\n\t" \
+ "pushq 88(%%rax)\n\t" \
+ "pushq 80(%%rax)\n\t" \
+ "pushq 72(%%rax)\n\t" \
+ "pushq 64(%%rax)\n\t" \
+ "pushq 56(%%rax)\n\t" \
+ "movq 48(%%rax), %%r9\n\t" \
+ "movq 40(%%rax), %%r8\n\t" \
+ "movq 32(%%rax), %%rcx\n\t" \
+ "movq 24(%%rax), %%rdx\n\t" \
+ "movq 16(%%rax), %%rsi\n\t" \
+ "movq 8(%%rax), %%rdi\n\t" \
+ "movq (%%rax), %%rax\n\t" /* target->%rax */ \
+ VALGRIND_CALL_NOREDIR_RAX \
+ "addq $40, %%rsp\n" \
+ "addq $128,%%rsp\n\t" \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9,arg10,arg11,arg12) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[13]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ _argvec[9] = (unsigned long)(arg9); \
+ _argvec[10] = (unsigned long)(arg10); \
+ _argvec[11] = (unsigned long)(arg11); \
+ _argvec[12] = (unsigned long)(arg12); \
+ __asm__ volatile( \
+ "subq $128,%%rsp\n\t" \
+ "pushq 96(%%rax)\n\t" \
+ "pushq 88(%%rax)\n\t" \
+ "pushq 80(%%rax)\n\t" \
+ "pushq 72(%%rax)\n\t" \
+ "pushq 64(%%rax)\n\t" \
+ "pushq 56(%%rax)\n\t" \
+ "movq 48(%%rax), %%r9\n\t" \
+ "movq 40(%%rax), %%r8\n\t" \
+ "movq 32(%%rax), %%rcx\n\t" \
+ "movq 24(%%rax), %%rdx\n\t" \
+ "movq 16(%%rax), %%rsi\n\t" \
+ "movq 8(%%rax), %%rdi\n\t" \
+ "movq (%%rax), %%rax\n\t" /* target->%rax */ \
+ VALGRIND_CALL_NOREDIR_RAX \
+ "addq $48, %%rsp\n" \
+ "addq $128,%%rsp\n\t" \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#endif /* PLAT_amd64_linux */
+
+/* ------------------------ ppc32-linux ------------------------ */
+
+#if defined(PLAT_ppc32_linux)
+
+/* This is useful for finding out about the on-stack stuff:
+
+ extern int f9 ( int,int,int,int,int,int,int,int,int );
+ extern int f10 ( int,int,int,int,int,int,int,int,int,int );
+ extern int f11 ( int,int,int,int,int,int,int,int,int,int,int );
+ extern int f12 ( int,int,int,int,int,int,int,int,int,int,int,int );
+
+ int g9 ( void ) {
+ return f9(11,22,33,44,55,66,77,88,99);
+ }
+ int g10 ( void ) {
+ return f10(11,22,33,44,55,66,77,88,99,110);
+ }
+ int g11 ( void ) {
+ return f11(11,22,33,44,55,66,77,88,99,110,121);
+ }
+ int g12 ( void ) {
+ return f12(11,22,33,44,55,66,77,88,99,110,121,132);
+ }
+*/
+
+/* ARGREGS: r3 r4 r5 r6 r7 r8 r9 r10 (the rest on stack somewhere) */
+
+/* These regs are trashed by the hidden call. */
+#define __CALLER_SAVED_REGS \
+ "lr", "ctr", "xer", \
+ "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", \
+ "r0", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", \
+ "r11", "r12", "r13"
+
+/* These CALL_FN_ macros assume that on ppc32-linux,
+ sizeof(unsigned long) == 4. */
+
+#define CALL_FN_W_v(lval, orig) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[1]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "lwz 11,0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr %0,3" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_W(lval, orig, arg1) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[2]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "lwz 3,4(11)\n\t" /* arg1->r3 */ \
+ "lwz 11,0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr %0,3" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WW(lval, orig, arg1,arg2) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ _argvec[2] = (unsigned long)arg2; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "lwz 3,4(11)\n\t" /* arg1->r3 */ \
+ "lwz 4,8(11)\n\t" \
+ "lwz 11,0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr %0,3" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[4]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ _argvec[2] = (unsigned long)arg2; \
+ _argvec[3] = (unsigned long)arg3; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "lwz 3,4(11)\n\t" /* arg1->r3 */ \
+ "lwz 4,8(11)\n\t" \
+ "lwz 5,12(11)\n\t" \
+ "lwz 11,0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr %0,3" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[5]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ _argvec[2] = (unsigned long)arg2; \
+ _argvec[3] = (unsigned long)arg3; \
+ _argvec[4] = (unsigned long)arg4; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "lwz 3,4(11)\n\t" /* arg1->r3 */ \
+ "lwz 4,8(11)\n\t" \
+ "lwz 5,12(11)\n\t" \
+ "lwz 6,16(11)\n\t" /* arg4->r6 */ \
+ "lwz 11,0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr %0,3" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[6]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ _argvec[2] = (unsigned long)arg2; \
+ _argvec[3] = (unsigned long)arg3; \
+ _argvec[4] = (unsigned long)arg4; \
+ _argvec[5] = (unsigned long)arg5; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "lwz 3,4(11)\n\t" /* arg1->r3 */ \
+ "lwz 4,8(11)\n\t" \
+ "lwz 5,12(11)\n\t" \
+ "lwz 6,16(11)\n\t" /* arg4->r6 */ \
+ "lwz 7,20(11)\n\t" \
+ "lwz 11,0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr %0,3" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[7]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ _argvec[2] = (unsigned long)arg2; \
+ _argvec[3] = (unsigned long)arg3; \
+ _argvec[4] = (unsigned long)arg4; \
+ _argvec[5] = (unsigned long)arg5; \
+ _argvec[6] = (unsigned long)arg6; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "lwz 3,4(11)\n\t" /* arg1->r3 */ \
+ "lwz 4,8(11)\n\t" \
+ "lwz 5,12(11)\n\t" \
+ "lwz 6,16(11)\n\t" /* arg4->r6 */ \
+ "lwz 7,20(11)\n\t" \
+ "lwz 8,24(11)\n\t" \
+ "lwz 11,0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr %0,3" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[8]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ _argvec[2] = (unsigned long)arg2; \
+ _argvec[3] = (unsigned long)arg3; \
+ _argvec[4] = (unsigned long)arg4; \
+ _argvec[5] = (unsigned long)arg5; \
+ _argvec[6] = (unsigned long)arg6; \
+ _argvec[7] = (unsigned long)arg7; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "lwz 3,4(11)\n\t" /* arg1->r3 */ \
+ "lwz 4,8(11)\n\t" \
+ "lwz 5,12(11)\n\t" \
+ "lwz 6,16(11)\n\t" /* arg4->r6 */ \
+ "lwz 7,20(11)\n\t" \
+ "lwz 8,24(11)\n\t" \
+ "lwz 9,28(11)\n\t" \
+ "lwz 11,0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr %0,3" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[9]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ _argvec[2] = (unsigned long)arg2; \
+ _argvec[3] = (unsigned long)arg3; \
+ _argvec[4] = (unsigned long)arg4; \
+ _argvec[5] = (unsigned long)arg5; \
+ _argvec[6] = (unsigned long)arg6; \
+ _argvec[7] = (unsigned long)arg7; \
+ _argvec[8] = (unsigned long)arg8; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "lwz 3,4(11)\n\t" /* arg1->r3 */ \
+ "lwz 4,8(11)\n\t" \
+ "lwz 5,12(11)\n\t" \
+ "lwz 6,16(11)\n\t" /* arg4->r6 */ \
+ "lwz 7,20(11)\n\t" \
+ "lwz 8,24(11)\n\t" \
+ "lwz 9,28(11)\n\t" \
+ "lwz 10,32(11)\n\t" /* arg8->r10 */ \
+ "lwz 11,0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr %0,3" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[10]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ _argvec[2] = (unsigned long)arg2; \
+ _argvec[3] = (unsigned long)arg3; \
+ _argvec[4] = (unsigned long)arg4; \
+ _argvec[5] = (unsigned long)arg5; \
+ _argvec[6] = (unsigned long)arg6; \
+ _argvec[7] = (unsigned long)arg7; \
+ _argvec[8] = (unsigned long)arg8; \
+ _argvec[9] = (unsigned long)arg9; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "addi 1,1,-16\n\t" \
+ /* arg9 */ \
+ "lwz 3,36(11)\n\t" \
+ "stw 3,8(1)\n\t" \
+ /* args1-8 */ \
+ "lwz 3,4(11)\n\t" /* arg1->r3 */ \
+ "lwz 4,8(11)\n\t" \
+ "lwz 5,12(11)\n\t" \
+ "lwz 6,16(11)\n\t" /* arg4->r6 */ \
+ "lwz 7,20(11)\n\t" \
+ "lwz 8,24(11)\n\t" \
+ "lwz 9,28(11)\n\t" \
+ "lwz 10,32(11)\n\t" /* arg8->r10 */ \
+ "lwz 11,0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "addi 1,1,16\n\t" \
+ "mr %0,3" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9,arg10) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[11]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ _argvec[2] = (unsigned long)arg2; \
+ _argvec[3] = (unsigned long)arg3; \
+ _argvec[4] = (unsigned long)arg4; \
+ _argvec[5] = (unsigned long)arg5; \
+ _argvec[6] = (unsigned long)arg6; \
+ _argvec[7] = (unsigned long)arg7; \
+ _argvec[8] = (unsigned long)arg8; \
+ _argvec[9] = (unsigned long)arg9; \
+ _argvec[10] = (unsigned long)arg10; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "addi 1,1,-16\n\t" \
+ /* arg10 */ \
+ "lwz 3,40(11)\n\t" \
+ "stw 3,12(1)\n\t" \
+ /* arg9 */ \
+ "lwz 3,36(11)\n\t" \
+ "stw 3,8(1)\n\t" \
+ /* args1-8 */ \
+ "lwz 3,4(11)\n\t" /* arg1->r3 */ \
+ "lwz 4,8(11)\n\t" \
+ "lwz 5,12(11)\n\t" \
+ "lwz 6,16(11)\n\t" /* arg4->r6 */ \
+ "lwz 7,20(11)\n\t" \
+ "lwz 8,24(11)\n\t" \
+ "lwz 9,28(11)\n\t" \
+ "lwz 10,32(11)\n\t" /* arg8->r10 */ \
+ "lwz 11,0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "addi 1,1,16\n\t" \
+ "mr %0,3" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9,arg10,arg11) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[12]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ _argvec[2] = (unsigned long)arg2; \
+ _argvec[3] = (unsigned long)arg3; \
+ _argvec[4] = (unsigned long)arg4; \
+ _argvec[5] = (unsigned long)arg5; \
+ _argvec[6] = (unsigned long)arg6; \
+ _argvec[7] = (unsigned long)arg7; \
+ _argvec[8] = (unsigned long)arg8; \
+ _argvec[9] = (unsigned long)arg9; \
+ _argvec[10] = (unsigned long)arg10; \
+ _argvec[11] = (unsigned long)arg11; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "addi 1,1,-32\n\t" \
+ /* arg11 */ \
+ "lwz 3,44(11)\n\t" \
+ "stw 3,16(1)\n\t" \
+ /* arg10 */ \
+ "lwz 3,40(11)\n\t" \
+ "stw 3,12(1)\n\t" \
+ /* arg9 */ \
+ "lwz 3,36(11)\n\t" \
+ "stw 3,8(1)\n\t" \
+ /* args1-8 */ \
+ "lwz 3,4(11)\n\t" /* arg1->r3 */ \
+ "lwz 4,8(11)\n\t" \
+ "lwz 5,12(11)\n\t" \
+ "lwz 6,16(11)\n\t" /* arg4->r6 */ \
+ "lwz 7,20(11)\n\t" \
+ "lwz 8,24(11)\n\t" \
+ "lwz 9,28(11)\n\t" \
+ "lwz 10,32(11)\n\t" /* arg8->r10 */ \
+ "lwz 11,0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "addi 1,1,32\n\t" \
+ "mr %0,3" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9,arg10,arg11,arg12) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[13]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ _argvec[2] = (unsigned long)arg2; \
+ _argvec[3] = (unsigned long)arg3; \
+ _argvec[4] = (unsigned long)arg4; \
+ _argvec[5] = (unsigned long)arg5; \
+ _argvec[6] = (unsigned long)arg6; \
+ _argvec[7] = (unsigned long)arg7; \
+ _argvec[8] = (unsigned long)arg8; \
+ _argvec[9] = (unsigned long)arg9; \
+ _argvec[10] = (unsigned long)arg10; \
+ _argvec[11] = (unsigned long)arg11; \
+ _argvec[12] = (unsigned long)arg12; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "addi 1,1,-32\n\t" \
+ /* arg12 */ \
+ "lwz 3,48(11)\n\t" \
+ "stw 3,20(1)\n\t" \
+ /* arg11 */ \
+ "lwz 3,44(11)\n\t" \
+ "stw 3,16(1)\n\t" \
+ /* arg10 */ \
+ "lwz 3,40(11)\n\t" \
+ "stw 3,12(1)\n\t" \
+ /* arg9 */ \
+ "lwz 3,36(11)\n\t" \
+ "stw 3,8(1)\n\t" \
+ /* args1-8 */ \
+ "lwz 3,4(11)\n\t" /* arg1->r3 */ \
+ "lwz 4,8(11)\n\t" \
+ "lwz 5,12(11)\n\t" \
+ "lwz 6,16(11)\n\t" /* arg4->r6 */ \
+ "lwz 7,20(11)\n\t" \
+ "lwz 8,24(11)\n\t" \
+ "lwz 9,28(11)\n\t" \
+ "lwz 10,32(11)\n\t" /* arg8->r10 */ \
+ "lwz 11,0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "addi 1,1,32\n\t" \
+ "mr %0,3" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#endif /* PLAT_ppc32_linux */
+
+/* ------------------------ ppc64-linux ------------------------ */
+
+#if defined(PLAT_ppc64_linux)
+
+/* ARGREGS: r3 r4 r5 r6 r7 r8 r9 r10 (the rest on stack somewhere) */
+
+/* These regs are trashed by the hidden call. */
+#define __CALLER_SAVED_REGS \
+ "lr", "ctr", "xer", \
+ "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", \
+ "r0", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", \
+ "r11", "r12", "r13"
+
+/* These CALL_FN_ macros assume that on ppc64-linux, sizeof(unsigned
+ long) == 8. */
+
+#define CALL_FN_W_v(lval, orig) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+0]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)" /* restore tocptr */ \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_W(lval, orig, arg1) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+1]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)" /* restore tocptr */ \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WW(lval, orig, arg1,arg2) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+2]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 4, 16(11)\n\t" /* arg2->r4 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)" /* restore tocptr */ \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+3]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 4, 16(11)\n\t" /* arg2->r4 */ \
+ "ld 5, 24(11)\n\t" /* arg3->r5 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)" /* restore tocptr */ \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+4]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 4, 16(11)\n\t" /* arg2->r4 */ \
+ "ld 5, 24(11)\n\t" /* arg3->r5 */ \
+ "ld 6, 32(11)\n\t" /* arg4->r6 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)" /* restore tocptr */ \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+5]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ _argvec[2+5] = (unsigned long)arg5; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 4, 16(11)\n\t" /* arg2->r4 */ \
+ "ld 5, 24(11)\n\t" /* arg3->r5 */ \
+ "ld 6, 32(11)\n\t" /* arg4->r6 */ \
+ "ld 7, 40(11)\n\t" /* arg5->r7 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)" /* restore tocptr */ \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+6]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ _argvec[2+5] = (unsigned long)arg5; \
+ _argvec[2+6] = (unsigned long)arg6; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 4, 16(11)\n\t" /* arg2->r4 */ \
+ "ld 5, 24(11)\n\t" /* arg3->r5 */ \
+ "ld 6, 32(11)\n\t" /* arg4->r6 */ \
+ "ld 7, 40(11)\n\t" /* arg5->r7 */ \
+ "ld 8, 48(11)\n\t" /* arg6->r8 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)" /* restore tocptr */ \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+7]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ _argvec[2+5] = (unsigned long)arg5; \
+ _argvec[2+6] = (unsigned long)arg6; \
+ _argvec[2+7] = (unsigned long)arg7; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 4, 16(11)\n\t" /* arg2->r4 */ \
+ "ld 5, 24(11)\n\t" /* arg3->r5 */ \
+ "ld 6, 32(11)\n\t" /* arg4->r6 */ \
+ "ld 7, 40(11)\n\t" /* arg5->r7 */ \
+ "ld 8, 48(11)\n\t" /* arg6->r8 */ \
+ "ld 9, 56(11)\n\t" /* arg7->r9 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)" /* restore tocptr */ \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+8]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ _argvec[2+5] = (unsigned long)arg5; \
+ _argvec[2+6] = (unsigned long)arg6; \
+ _argvec[2+7] = (unsigned long)arg7; \
+ _argvec[2+8] = (unsigned long)arg8; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 4, 16(11)\n\t" /* arg2->r4 */ \
+ "ld 5, 24(11)\n\t" /* arg3->r5 */ \
+ "ld 6, 32(11)\n\t" /* arg4->r6 */ \
+ "ld 7, 40(11)\n\t" /* arg5->r7 */ \
+ "ld 8, 48(11)\n\t" /* arg6->r8 */ \
+ "ld 9, 56(11)\n\t" /* arg7->r9 */ \
+ "ld 10, 64(11)\n\t" /* arg8->r10 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)" /* restore tocptr */ \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+9]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ _argvec[2+5] = (unsigned long)arg5; \
+ _argvec[2+6] = (unsigned long)arg6; \
+ _argvec[2+7] = (unsigned long)arg7; \
+ _argvec[2+8] = (unsigned long)arg8; \
+ _argvec[2+9] = (unsigned long)arg9; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "addi 1,1,-128\n\t" /* expand stack frame */ \
+ /* arg9 */ \
+ "ld 3,72(11)\n\t" \
+ "std 3,112(1)\n\t" \
+ /* args1-8 */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 4, 16(11)\n\t" /* arg2->r4 */ \
+ "ld 5, 24(11)\n\t" /* arg3->r5 */ \
+ "ld 6, 32(11)\n\t" /* arg4->r6 */ \
+ "ld 7, 40(11)\n\t" /* arg5->r7 */ \
+ "ld 8, 48(11)\n\t" /* arg6->r8 */ \
+ "ld 9, 56(11)\n\t" /* arg7->r9 */ \
+ "ld 10, 64(11)\n\t" /* arg8->r10 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)\n\t" /* restore tocptr */ \
+ "addi 1,1,128" /* restore frame */ \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9,arg10) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+10]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ _argvec[2+5] = (unsigned long)arg5; \
+ _argvec[2+6] = (unsigned long)arg6; \
+ _argvec[2+7] = (unsigned long)arg7; \
+ _argvec[2+8] = (unsigned long)arg8; \
+ _argvec[2+9] = (unsigned long)arg9; \
+ _argvec[2+10] = (unsigned long)arg10; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "addi 1,1,-128\n\t" /* expand stack frame */ \
+ /* arg10 */ \
+ "ld 3,80(11)\n\t" \
+ "std 3,120(1)\n\t" \
+ /* arg9 */ \
+ "ld 3,72(11)\n\t" \
+ "std 3,112(1)\n\t" \
+ /* args1-8 */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 4, 16(11)\n\t" /* arg2->r4 */ \
+ "ld 5, 24(11)\n\t" /* arg3->r5 */ \
+ "ld 6, 32(11)\n\t" /* arg4->r6 */ \
+ "ld 7, 40(11)\n\t" /* arg5->r7 */ \
+ "ld 8, 48(11)\n\t" /* arg6->r8 */ \
+ "ld 9, 56(11)\n\t" /* arg7->r9 */ \
+ "ld 10, 64(11)\n\t" /* arg8->r10 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)\n\t" /* restore tocptr */ \
+ "addi 1,1,128" /* restore frame */ \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9,arg10,arg11) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+11]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ _argvec[2+5] = (unsigned long)arg5; \
+ _argvec[2+6] = (unsigned long)arg6; \
+ _argvec[2+7] = (unsigned long)arg7; \
+ _argvec[2+8] = (unsigned long)arg8; \
+ _argvec[2+9] = (unsigned long)arg9; \
+ _argvec[2+10] = (unsigned long)arg10; \
+ _argvec[2+11] = (unsigned long)arg11; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "addi 1,1,-144\n\t" /* expand stack frame */ \
+ /* arg11 */ \
+ "ld 3,88(11)\n\t" \
+ "std 3,128(1)\n\t" \
+ /* arg10 */ \
+ "ld 3,80(11)\n\t" \
+ "std 3,120(1)\n\t" \
+ /* arg9 */ \
+ "ld 3,72(11)\n\t" \
+ "std 3,112(1)\n\t" \
+ /* args1-8 */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 4, 16(11)\n\t" /* arg2->r4 */ \
+ "ld 5, 24(11)\n\t" /* arg3->r5 */ \
+ "ld 6, 32(11)\n\t" /* arg4->r6 */ \
+ "ld 7, 40(11)\n\t" /* arg5->r7 */ \
+ "ld 8, 48(11)\n\t" /* arg6->r8 */ \
+ "ld 9, 56(11)\n\t" /* arg7->r9 */ \
+ "ld 10, 64(11)\n\t" /* arg8->r10 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)\n\t" /* restore tocptr */ \
+ "addi 1,1,144" /* restore frame */ \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9,arg10,arg11,arg12) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+12]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ _argvec[2+5] = (unsigned long)arg5; \
+ _argvec[2+6] = (unsigned long)arg6; \
+ _argvec[2+7] = (unsigned long)arg7; \
+ _argvec[2+8] = (unsigned long)arg8; \
+ _argvec[2+9] = (unsigned long)arg9; \
+ _argvec[2+10] = (unsigned long)arg10; \
+ _argvec[2+11] = (unsigned long)arg11; \
+ _argvec[2+12] = (unsigned long)arg12; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "addi 1,1,-144\n\t" /* expand stack frame */ \
+ /* arg12 */ \
+ "ld 3,96(11)\n\t" \
+ "std 3,136(1)\n\t" \
+ /* arg11 */ \
+ "ld 3,88(11)\n\t" \
+ "std 3,128(1)\n\t" \
+ /* arg10 */ \
+ "ld 3,80(11)\n\t" \
+ "std 3,120(1)\n\t" \
+ /* arg9 */ \
+ "ld 3,72(11)\n\t" \
+ "std 3,112(1)\n\t" \
+ /* args1-8 */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 4, 16(11)\n\t" /* arg2->r4 */ \
+ "ld 5, 24(11)\n\t" /* arg3->r5 */ \
+ "ld 6, 32(11)\n\t" /* arg4->r6 */ \
+ "ld 7, 40(11)\n\t" /* arg5->r7 */ \
+ "ld 8, 48(11)\n\t" /* arg6->r8 */ \
+ "ld 9, 56(11)\n\t" /* arg7->r9 */ \
+ "ld 10, 64(11)\n\t" /* arg8->r10 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)\n\t" /* restore tocptr */ \
+ "addi 1,1,144" /* restore frame */ \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#endif /* PLAT_ppc64_linux */
+
+/* ------------------------ ppc32-aix5 ------------------------- */
+
+#if defined(PLAT_ppc32_aix5)
+
+/* ARGREGS: r3 r4 r5 r6 r7 r8 r9 r10 (the rest on stack somewhere) */
+
+/* These regs are trashed by the hidden call. */
+#define __CALLER_SAVED_REGS \
+ "lr", "ctr", "xer", \
+ "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", \
+ "r0", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", \
+ "r11", "r12", "r13"
+
+/* Expand the stack frame, copying enough info that unwinding
+ still works. Trashes r3. */
+
+#define VG_EXPAND_FRAME_BY_trashes_r3(_n_fr) \
+ "addi 1,1,-" #_n_fr "\n\t" \
+ "lwz 3," #_n_fr "(1)\n\t" \
+ "stw 3,0(1)\n\t"
+
+#define VG_CONTRACT_FRAME_BY(_n_fr) \
+ "addi 1,1," #_n_fr "\n\t"
+
+/* These CALL_FN_ macros assume that on ppc32-aix5, sizeof(unsigned
+ long) == 4. */
+
+#define CALL_FN_W_v(lval, orig) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+0]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ VG_EXPAND_FRAME_BY_trashes_r3(512) \
+ "stw 2,-8(11)\n\t" /* save tocptr */ \
+ "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \
+ "lwz 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "lwz 2,-8(11)\n\t" /* restore tocptr */ \
+ VG_CONTRACT_FRAME_BY(512) \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_W(lval, orig, arg1) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+1]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ VG_EXPAND_FRAME_BY_trashes_r3(512) \
+ "stw 2,-8(11)\n\t" /* save tocptr */ \
+ "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \
+ "lwz 3, 4(11)\n\t" /* arg1->r3 */ \
+ "lwz 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "lwz 2,-8(11)\n\t" /* restore tocptr */ \
+ VG_CONTRACT_FRAME_BY(512) \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WW(lval, orig, arg1,arg2) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+2]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ VG_EXPAND_FRAME_BY_trashes_r3(512) \
+ "stw 2,-8(11)\n\t" /* save tocptr */ \
+ "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \
+ "lwz 3, 4(11)\n\t" /* arg1->r3 */ \
+ "lwz 4, 8(11)\n\t" /* arg2->r4 */ \
+ "lwz 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "lwz 2,-8(11)\n\t" /* restore tocptr */ \
+ VG_CONTRACT_FRAME_BY(512) \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+3]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ VG_EXPAND_FRAME_BY_trashes_r3(512) \
+ "stw 2,-8(11)\n\t" /* save tocptr */ \
+ "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \
+ "lwz 3, 4(11)\n\t" /* arg1->r3 */ \
+ "lwz 4, 8(11)\n\t" /* arg2->r4 */ \
+ "lwz 5, 12(11)\n\t" /* arg3->r5 */ \
+ "lwz 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "lwz 2,-8(11)\n\t" /* restore tocptr */ \
+ VG_CONTRACT_FRAME_BY(512) \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+4]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ VG_EXPAND_FRAME_BY_trashes_r3(512) \
+ "stw 2,-8(11)\n\t" /* save tocptr */ \
+ "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \
+ "lwz 3, 4(11)\n\t" /* arg1->r3 */ \
+ "lwz 4, 8(11)\n\t" /* arg2->r4 */ \
+ "lwz 5, 12(11)\n\t" /* arg3->r5 */ \
+ "lwz 6, 16(11)\n\t" /* arg4->r6 */ \
+ "lwz 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "lwz 2,-8(11)\n\t" /* restore tocptr */ \
+ VG_CONTRACT_FRAME_BY(512) \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+5]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ _argvec[2+5] = (unsigned long)arg5; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ VG_EXPAND_FRAME_BY_trashes_r3(512) \
+ "stw 2,-8(11)\n\t" /* save tocptr */ \
+ "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \
+ "lwz 3, 4(11)\n\t" /* arg1->r3 */ \
+ "lwz 4, 8(11)\n\t" /* arg2->r4 */ \
+ "lwz 5, 12(11)\n\t" /* arg3->r5 */ \
+ "lwz 6, 16(11)\n\t" /* arg4->r6 */ \
+ "lwz 7, 20(11)\n\t" /* arg5->r7 */ \
+ "lwz 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "lwz 2,-8(11)\n\t" /* restore tocptr */ \
+ VG_CONTRACT_FRAME_BY(512) \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+6]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ _argvec[2+5] = (unsigned long)arg5; \
+ _argvec[2+6] = (unsigned long)arg6; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ VG_EXPAND_FRAME_BY_trashes_r3(512) \
+ "stw 2,-8(11)\n\t" /* save tocptr */ \
+ "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \
+ "lwz 3, 4(11)\n\t" /* arg1->r3 */ \
+ "lwz 4, 8(11)\n\t" /* arg2->r4 */ \
+ "lwz 5, 12(11)\n\t" /* arg3->r5 */ \
+ "lwz 6, 16(11)\n\t" /* arg4->r6 */ \
+ "lwz 7, 20(11)\n\t" /* arg5->r7 */ \
+ "lwz 8, 24(11)\n\t" /* arg6->r8 */ \
+ "lwz 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "lwz 2,-8(11)\n\t" /* restore tocptr */ \
+ VG_CONTRACT_FRAME_BY(512) \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+7]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ _argvec[2+5] = (unsigned long)arg5; \
+ _argvec[2+6] = (unsigned long)arg6; \
+ _argvec[2+7] = (unsigned long)arg7; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ VG_EXPAND_FRAME_BY_trashes_r3(512) \
+ "stw 2,-8(11)\n\t" /* save tocptr */ \
+ "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \
+ "lwz 3, 4(11)\n\t" /* arg1->r3 */ \
+ "lwz 4, 8(11)\n\t" /* arg2->r4 */ \
+ "lwz 5, 12(11)\n\t" /* arg3->r5 */ \
+ "lwz 6, 16(11)\n\t" /* arg4->r6 */ \
+ "lwz 7, 20(11)\n\t" /* arg5->r7 */ \
+ "lwz 8, 24(11)\n\t" /* arg6->r8 */ \
+ "lwz 9, 28(11)\n\t" /* arg7->r9 */ \
+ "lwz 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "lwz 2,-8(11)\n\t" /* restore tocptr */ \
+ VG_CONTRACT_FRAME_BY(512) \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+8]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ _argvec[2+5] = (unsigned long)arg5; \
+ _argvec[2+6] = (unsigned long)arg6; \
+ _argvec[2+7] = (unsigned long)arg7; \
+ _argvec[2+8] = (unsigned long)arg8; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ VG_EXPAND_FRAME_BY_trashes_r3(512) \
+ "stw 2,-8(11)\n\t" /* save tocptr */ \
+ "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \
+ "lwz 3, 4(11)\n\t" /* arg1->r3 */ \
+ "lwz 4, 8(11)\n\t" /* arg2->r4 */ \
+ "lwz 5, 12(11)\n\t" /* arg3->r5 */ \
+ "lwz 6, 16(11)\n\t" /* arg4->r6 */ \
+ "lwz 7, 20(11)\n\t" /* arg5->r7 */ \
+ "lwz 8, 24(11)\n\t" /* arg6->r8 */ \
+ "lwz 9, 28(11)\n\t" /* arg7->r9 */ \
+ "lwz 10, 32(11)\n\t" /* arg8->r10 */ \
+ "lwz 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "lwz 2,-8(11)\n\t" /* restore tocptr */ \
+ VG_CONTRACT_FRAME_BY(512) \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+9]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ _argvec[2+5] = (unsigned long)arg5; \
+ _argvec[2+6] = (unsigned long)arg6; \
+ _argvec[2+7] = (unsigned long)arg7; \
+ _argvec[2+8] = (unsigned long)arg8; \
+ _argvec[2+9] = (unsigned long)arg9; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ VG_EXPAND_FRAME_BY_trashes_r3(512) \
+ "stw 2,-8(11)\n\t" /* save tocptr */ \
+ "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \
+ VG_EXPAND_FRAME_BY_trashes_r3(64) \
+ /* arg9 */ \
+ "lwz 3,36(11)\n\t" \
+ "stw 3,56(1)\n\t" \
+ /* args1-8 */ \
+ "lwz 3, 4(11)\n\t" /* arg1->r3 */ \
+ "lwz 4, 8(11)\n\t" /* arg2->r4 */ \
+ "lwz 5, 12(11)\n\t" /* arg3->r5 */ \
+ "lwz 6, 16(11)\n\t" /* arg4->r6 */ \
+ "lwz 7, 20(11)\n\t" /* arg5->r7 */ \
+ "lwz 8, 24(11)\n\t" /* arg6->r8 */ \
+ "lwz 9, 28(11)\n\t" /* arg7->r9 */ \
+ "lwz 10, 32(11)\n\t" /* arg8->r10 */ \
+ "lwz 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "lwz 2,-8(11)\n\t" /* restore tocptr */ \
+ VG_CONTRACT_FRAME_BY(64) \
+ VG_CONTRACT_FRAME_BY(512) \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9,arg10) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+10]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ _argvec[2+5] = (unsigned long)arg5; \
+ _argvec[2+6] = (unsigned long)arg6; \
+ _argvec[2+7] = (unsigned long)arg7; \
+ _argvec[2+8] = (unsigned long)arg8; \
+ _argvec[2+9] = (unsigned long)arg9; \
+ _argvec[2+10] = (unsigned long)arg10; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ VG_EXPAND_FRAME_BY_trashes_r3(512) \
+ "stw 2,-8(11)\n\t" /* save tocptr */ \
+ "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \
+ VG_EXPAND_FRAME_BY_trashes_r3(64) \
+ /* arg10 */ \
+ "lwz 3,40(11)\n\t" \
+ "stw 3,60(1)\n\t" \
+ /* arg9 */ \
+ "lwz 3,36(11)\n\t" \
+ "stw 3,56(1)\n\t" \
+ /* args1-8 */ \
+ "lwz 3, 4(11)\n\t" /* arg1->r3 */ \
+ "lwz 4, 8(11)\n\t" /* arg2->r4 */ \
+ "lwz 5, 12(11)\n\t" /* arg3->r5 */ \
+ "lwz 6, 16(11)\n\t" /* arg4->r6 */ \
+ "lwz 7, 20(11)\n\t" /* arg5->r7 */ \
+ "lwz 8, 24(11)\n\t" /* arg6->r8 */ \
+ "lwz 9, 28(11)\n\t" /* arg7->r9 */ \
+ "lwz 10, 32(11)\n\t" /* arg8->r10 */ \
+ "lwz 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "lwz 2,-8(11)\n\t" /* restore tocptr */ \
+ VG_CONTRACT_FRAME_BY(64) \
+ VG_CONTRACT_FRAME_BY(512) \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9,arg10,arg11) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+11]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ _argvec[2+5] = (unsigned long)arg5; \
+ _argvec[2+6] = (unsigned long)arg6; \
+ _argvec[2+7] = (unsigned long)arg7; \
+ _argvec[2+8] = (unsigned long)arg8; \
+ _argvec[2+9] = (unsigned long)arg9; \
+ _argvec[2+10] = (unsigned long)arg10; \
+ _argvec[2+11] = (unsigned long)arg11; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ VG_EXPAND_FRAME_BY_trashes_r3(512) \
+ "stw 2,-8(11)\n\t" /* save tocptr */ \
+ "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \
+ VG_EXPAND_FRAME_BY_trashes_r3(72) \
+ /* arg11 */ \
+ "lwz 3,44(11)\n\t" \
+ "stw 3,64(1)\n\t" \
+ /* arg10 */ \
+ "lwz 3,40(11)\n\t" \
+ "stw 3,60(1)\n\t" \
+ /* arg9 */ \
+ "lwz 3,36(11)\n\t" \
+ "stw 3,56(1)\n\t" \
+ /* args1-8 */ \
+ "lwz 3, 4(11)\n\t" /* arg1->r3 */ \
+ "lwz 4, 8(11)\n\t" /* arg2->r4 */ \
+ "lwz 5, 12(11)\n\t" /* arg3->r5 */ \
+ "lwz 6, 16(11)\n\t" /* arg4->r6 */ \
+ "lwz 7, 20(11)\n\t" /* arg5->r7 */ \
+ "lwz 8, 24(11)\n\t" /* arg6->r8 */ \
+ "lwz 9, 28(11)\n\t" /* arg7->r9 */ \
+ "lwz 10, 32(11)\n\t" /* arg8->r10 */ \
+ "lwz 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "lwz 2,-8(11)\n\t" /* restore tocptr */ \
+ VG_CONTRACT_FRAME_BY(72) \
+ VG_CONTRACT_FRAME_BY(512) \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9,arg10,arg11,arg12) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+12]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ _argvec[2+5] = (unsigned long)arg5; \
+ _argvec[2+6] = (unsigned long)arg6; \
+ _argvec[2+7] = (unsigned long)arg7; \
+ _argvec[2+8] = (unsigned long)arg8; \
+ _argvec[2+9] = (unsigned long)arg9; \
+ _argvec[2+10] = (unsigned long)arg10; \
+ _argvec[2+11] = (unsigned long)arg11; \
+ _argvec[2+12] = (unsigned long)arg12; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ VG_EXPAND_FRAME_BY_trashes_r3(512) \
+ "stw 2,-8(11)\n\t" /* save tocptr */ \
+ "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \
+ VG_EXPAND_FRAME_BY_trashes_r3(72) \
+ /* arg12 */ \
+ "lwz 3,48(11)\n\t" \
+ "stw 3,68(1)\n\t" \
+ /* arg11 */ \
+ "lwz 3,44(11)\n\t" \
+ "stw 3,64(1)\n\t" \
+ /* arg10 */ \
+ "lwz 3,40(11)\n\t" \
+ "stw 3,60(1)\n\t" \
+ /* arg9 */ \
+ "lwz 3,36(11)\n\t" \
+ "stw 3,56(1)\n\t" \
+ /* args1-8 */ \
+ "lwz 3, 4(11)\n\t" /* arg1->r3 */ \
+ "lwz 4, 8(11)\n\t" /* arg2->r4 */ \
+ "lwz 5, 12(11)\n\t" /* arg3->r5 */ \
+ "lwz 6, 16(11)\n\t" /* arg4->r6 */ \
+ "lwz 7, 20(11)\n\t" /* arg5->r7 */ \
+ "lwz 8, 24(11)\n\t" /* arg6->r8 */ \
+ "lwz 9, 28(11)\n\t" /* arg7->r9 */ \
+ "lwz 10, 32(11)\n\t" /* arg8->r10 */ \
+ "lwz 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "lwz 2,-8(11)\n\t" /* restore tocptr */ \
+ VG_CONTRACT_FRAME_BY(72) \
+ VG_CONTRACT_FRAME_BY(512) \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#endif /* PLAT_ppc32_aix5 */
+
+/* ------------------------ ppc64-aix5 ------------------------- */
+
+#if defined(PLAT_ppc64_aix5)
+
+/* ARGREGS: r3 r4 r5 r6 r7 r8 r9 r10 (the rest on stack somewhere) */
+
+/* These regs are trashed by the hidden call. */
+#define __CALLER_SAVED_REGS \
+ "lr", "ctr", "xer", \
+ "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", \
+ "r0", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", \
+ "r11", "r12", "r13"
+
+/* Expand the stack frame, copying enough info that unwinding
+ still works. Trashes r3. */
+
+#define VG_EXPAND_FRAME_BY_trashes_r3(_n_fr) \
+ "addi 1,1,-" #_n_fr "\n\t" \
+ "ld 3," #_n_fr "(1)\n\t" \
+ "std 3,0(1)\n\t"
+
+#define VG_CONTRACT_FRAME_BY(_n_fr) \
+ "addi 1,1," #_n_fr "\n\t"
+
+/* These CALL_FN_ macros assume that on ppc64-aix5, sizeof(unsigned
+ long) == 8. */
+
+#define CALL_FN_W_v(lval, orig) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+0]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ VG_EXPAND_FRAME_BY_trashes_r3(512) \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)\n\t" /* restore tocptr */ \
+ VG_CONTRACT_FRAME_BY(512) \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_W(lval, orig, arg1) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+1]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ VG_EXPAND_FRAME_BY_trashes_r3(512) \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)\n\t" /* restore tocptr */ \
+ VG_CONTRACT_FRAME_BY(512) \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WW(lval, orig, arg1,arg2) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+2]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ VG_EXPAND_FRAME_BY_trashes_r3(512) \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 4, 16(11)\n\t" /* arg2->r4 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)\n\t" /* restore tocptr */ \
+ VG_CONTRACT_FRAME_BY(512) \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+3]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ VG_EXPAND_FRAME_BY_trashes_r3(512) \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 4, 16(11)\n\t" /* arg2->r4 */ \
+ "ld 5, 24(11)\n\t" /* arg3->r5 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)\n\t" /* restore tocptr */ \
+ VG_CONTRACT_FRAME_BY(512) \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+4]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ VG_EXPAND_FRAME_BY_trashes_r3(512) \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 4, 16(11)\n\t" /* arg2->r4 */ \
+ "ld 5, 24(11)\n\t" /* arg3->r5 */ \
+ "ld 6, 32(11)\n\t" /* arg4->r6 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)\n\t" /* restore tocptr */ \
+ VG_CONTRACT_FRAME_BY(512) \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+5]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ _argvec[2+5] = (unsigned long)arg5; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ VG_EXPAND_FRAME_BY_trashes_r3(512) \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 4, 16(11)\n\t" /* arg2->r4 */ \
+ "ld 5, 24(11)\n\t" /* arg3->r5 */ \
+ "ld 6, 32(11)\n\t" /* arg4->r6 */ \
+ "ld 7, 40(11)\n\t" /* arg5->r7 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)\n\t" /* restore tocptr */ \
+ VG_CONTRACT_FRAME_BY(512) \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+6]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ _argvec[2+5] = (unsigned long)arg5; \
+ _argvec[2+6] = (unsigned long)arg6; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ VG_EXPAND_FRAME_BY_trashes_r3(512) \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 4, 16(11)\n\t" /* arg2->r4 */ \
+ "ld 5, 24(11)\n\t" /* arg3->r5 */ \
+ "ld 6, 32(11)\n\t" /* arg4->r6 */ \
+ "ld 7, 40(11)\n\t" /* arg5->r7 */ \
+ "ld 8, 48(11)\n\t" /* arg6->r8 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)\n\t" /* restore tocptr */ \
+ VG_CONTRACT_FRAME_BY(512) \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+7]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ _argvec[2+5] = (unsigned long)arg5; \
+ _argvec[2+6] = (unsigned long)arg6; \
+ _argvec[2+7] = (unsigned long)arg7; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ VG_EXPAND_FRAME_BY_trashes_r3(512) \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 4, 16(11)\n\t" /* arg2->r4 */ \
+ "ld 5, 24(11)\n\t" /* arg3->r5 */ \
+ "ld 6, 32(11)\n\t" /* arg4->r6 */ \
+ "ld 7, 40(11)\n\t" /* arg5->r7 */ \
+ "ld 8, 48(11)\n\t" /* arg6->r8 */ \
+ "ld 9, 56(11)\n\t" /* arg7->r9 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)\n\t" /* restore tocptr */ \
+ VG_CONTRACT_FRAME_BY(512) \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+8]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ _argvec[2+5] = (unsigned long)arg5; \
+ _argvec[2+6] = (unsigned long)arg6; \
+ _argvec[2+7] = (unsigned long)arg7; \
+ _argvec[2+8] = (unsigned long)arg8; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ VG_EXPAND_FRAME_BY_trashes_r3(512) \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 4, 16(11)\n\t" /* arg2->r4 */ \
+ "ld 5, 24(11)\n\t" /* arg3->r5 */ \
+ "ld 6, 32(11)\n\t" /* arg4->r6 */ \
+ "ld 7, 40(11)\n\t" /* arg5->r7 */ \
+ "ld 8, 48(11)\n\t" /* arg6->r8 */ \
+ "ld 9, 56(11)\n\t" /* arg7->r9 */ \
+ "ld 10, 64(11)\n\t" /* arg8->r10 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)\n\t" /* restore tocptr */ \
+ VG_CONTRACT_FRAME_BY(512) \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+9]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ _argvec[2+5] = (unsigned long)arg5; \
+ _argvec[2+6] = (unsigned long)arg6; \
+ _argvec[2+7] = (unsigned long)arg7; \
+ _argvec[2+8] = (unsigned long)arg8; \
+ _argvec[2+9] = (unsigned long)arg9; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ VG_EXPAND_FRAME_BY_trashes_r3(512) \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ VG_EXPAND_FRAME_BY_trashes_r3(128) \
+ /* arg9 */ \
+ "ld 3,72(11)\n\t" \
+ "std 3,112(1)\n\t" \
+ /* args1-8 */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 4, 16(11)\n\t" /* arg2->r4 */ \
+ "ld 5, 24(11)\n\t" /* arg3->r5 */ \
+ "ld 6, 32(11)\n\t" /* arg4->r6 */ \
+ "ld 7, 40(11)\n\t" /* arg5->r7 */ \
+ "ld 8, 48(11)\n\t" /* arg6->r8 */ \
+ "ld 9, 56(11)\n\t" /* arg7->r9 */ \
+ "ld 10, 64(11)\n\t" /* arg8->r10 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)\n\t" /* restore tocptr */ \
+ VG_CONTRACT_FRAME_BY(128) \
+ VG_CONTRACT_FRAME_BY(512) \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9,arg10) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+10]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ _argvec[2+5] = (unsigned long)arg5; \
+ _argvec[2+6] = (unsigned long)arg6; \
+ _argvec[2+7] = (unsigned long)arg7; \
+ _argvec[2+8] = (unsigned long)arg8; \
+ _argvec[2+9] = (unsigned long)arg9; \
+ _argvec[2+10] = (unsigned long)arg10; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ VG_EXPAND_FRAME_BY_trashes_r3(512) \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ VG_EXPAND_FRAME_BY_trashes_r3(128) \
+ /* arg10 */ \
+ "ld 3,80(11)\n\t" \
+ "std 3,120(1)\n\t" \
+ /* arg9 */ \
+ "ld 3,72(11)\n\t" \
+ "std 3,112(1)\n\t" \
+ /* args1-8 */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 4, 16(11)\n\t" /* arg2->r4 */ \
+ "ld 5, 24(11)\n\t" /* arg3->r5 */ \
+ "ld 6, 32(11)\n\t" /* arg4->r6 */ \
+ "ld 7, 40(11)\n\t" /* arg5->r7 */ \
+ "ld 8, 48(11)\n\t" /* arg6->r8 */ \
+ "ld 9, 56(11)\n\t" /* arg7->r9 */ \
+ "ld 10, 64(11)\n\t" /* arg8->r10 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)\n\t" /* restore tocptr */ \
+ VG_CONTRACT_FRAME_BY(128) \
+ VG_CONTRACT_FRAME_BY(512) \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9,arg10,arg11) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+11]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ _argvec[2+5] = (unsigned long)arg5; \
+ _argvec[2+6] = (unsigned long)arg6; \
+ _argvec[2+7] = (unsigned long)arg7; \
+ _argvec[2+8] = (unsigned long)arg8; \
+ _argvec[2+9] = (unsigned long)arg9; \
+ _argvec[2+10] = (unsigned long)arg10; \
+ _argvec[2+11] = (unsigned long)arg11; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ VG_EXPAND_FRAME_BY_trashes_r3(512) \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ VG_EXPAND_FRAME_BY_trashes_r3(144) \
+ /* arg11 */ \
+ "ld 3,88(11)\n\t" \
+ "std 3,128(1)\n\t" \
+ /* arg10 */ \
+ "ld 3,80(11)\n\t" \
+ "std 3,120(1)\n\t" \
+ /* arg9 */ \
+ "ld 3,72(11)\n\t" \
+ "std 3,112(1)\n\t" \
+ /* args1-8 */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 4, 16(11)\n\t" /* arg2->r4 */ \
+ "ld 5, 24(11)\n\t" /* arg3->r5 */ \
+ "ld 6, 32(11)\n\t" /* arg4->r6 */ \
+ "ld 7, 40(11)\n\t" /* arg5->r7 */ \
+ "ld 8, 48(11)\n\t" /* arg6->r8 */ \
+ "ld 9, 56(11)\n\t" /* arg7->r9 */ \
+ "ld 10, 64(11)\n\t" /* arg8->r10 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)\n\t" /* restore tocptr */ \
+ VG_CONTRACT_FRAME_BY(144) \
+ VG_CONTRACT_FRAME_BY(512) \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9,arg10,arg11,arg12) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+12]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ _argvec[2+5] = (unsigned long)arg5; \
+ _argvec[2+6] = (unsigned long)arg6; \
+ _argvec[2+7] = (unsigned long)arg7; \
+ _argvec[2+8] = (unsigned long)arg8; \
+ _argvec[2+9] = (unsigned long)arg9; \
+ _argvec[2+10] = (unsigned long)arg10; \
+ _argvec[2+11] = (unsigned long)arg11; \
+ _argvec[2+12] = (unsigned long)arg12; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ VG_EXPAND_FRAME_BY_trashes_r3(512) \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ VG_EXPAND_FRAME_BY_trashes_r3(144) \
+ /* arg12 */ \
+ "ld 3,96(11)\n\t" \
+ "std 3,136(1)\n\t" \
+ /* arg11 */ \
+ "ld 3,88(11)\n\t" \
+ "std 3,128(1)\n\t" \
+ /* arg10 */ \
+ "ld 3,80(11)\n\t" \
+ "std 3,120(1)\n\t" \
+ /* arg9 */ \
+ "ld 3,72(11)\n\t" \
+ "std 3,112(1)\n\t" \
+ /* args1-8 */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 4, 16(11)\n\t" /* arg2->r4 */ \
+ "ld 5, 24(11)\n\t" /* arg3->r5 */ \
+ "ld 6, 32(11)\n\t" /* arg4->r6 */ \
+ "ld 7, 40(11)\n\t" /* arg5->r7 */ \
+ "ld 8, 48(11)\n\t" /* arg6->r8 */ \
+ "ld 9, 56(11)\n\t" /* arg7->r9 */ \
+ "ld 10, 64(11)\n\t" /* arg8->r10 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)\n\t" /* restore tocptr */ \
+ VG_CONTRACT_FRAME_BY(144) \
+ VG_CONTRACT_FRAME_BY(512) \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#endif /* PLAT_ppc64_aix5 */
+
+
+/* ------------------------------------------------------------------ */
+/* ARCHITECTURE INDEPENDENT MACROS for CLIENT REQUESTS. */
+/* */
+/* ------------------------------------------------------------------ */
+
+/* Some request codes. There are many more of these, but most are not
+ exposed to end-user view. These are the public ones, all of the
+ form 0x1000 + small_number.
+
+ Core ones are in the range 0x00000000--0x0000ffff. The non-public
+ ones start at 0x2000.
+*/
+
+/* These macros are used by tools -- they must be public, but don't
+ embed them into other programs. */
+#define VG_USERREQ_TOOL_BASE(a,b) \
+ ((unsigned int)(((a)&0xff) << 24 | ((b)&0xff) << 16))
+#define VG_IS_TOOL_USERREQ(a, b, v) \
+ (VG_USERREQ_TOOL_BASE(a,b) == ((v) & 0xffff0000))
+
+/* !! ABIWARNING !! ABIWARNING !! ABIWARNING !! ABIWARNING !!
+ This enum comprises an ABI exported by Valgrind to programs
+ which use client requests. DO NOT CHANGE THE ORDER OF THESE
+ ENTRIES, NOR DELETE ANY -- add new ones at the end. */
+typedef
+ enum { VG_USERREQ__RUNNING_ON_VALGRIND = 0x1001,
+ VG_USERREQ__DISCARD_TRANSLATIONS = 0x1002,
+
+ /* These allow any function to be called from the simulated
+ CPU but run on the real CPU. Nb: the first arg passed to
+ the function is always the ThreadId of the running
+ thread! So CLIENT_CALL0 actually requires a 1 arg
+ function, etc. */
+ VG_USERREQ__CLIENT_CALL0 = 0x1101,
+ VG_USERREQ__CLIENT_CALL1 = 0x1102,
+ VG_USERREQ__CLIENT_CALL2 = 0x1103,
+ VG_USERREQ__CLIENT_CALL3 = 0x1104,
+
+ /* Can be useful in regression testing suites -- eg. can
+ send Valgrind's output to /dev/null and still count
+ errors. */
+ VG_USERREQ__COUNT_ERRORS = 0x1201,
+
+ /* These are useful and can be interpreted by any tool that
+ tracks malloc() et al, by using vg_replace_malloc.c. */
+ VG_USERREQ__MALLOCLIKE_BLOCK = 0x1301,
+ VG_USERREQ__FREELIKE_BLOCK = 0x1302,
+ /* Memory pool support. */
+ VG_USERREQ__CREATE_MEMPOOL = 0x1303,
+ VG_USERREQ__DESTROY_MEMPOOL = 0x1304,
+ VG_USERREQ__MEMPOOL_ALLOC = 0x1305,
+ VG_USERREQ__MEMPOOL_FREE = 0x1306,
+ VG_USERREQ__MEMPOOL_TRIM = 0x1307,
+ VG_USERREQ__MOVE_MEMPOOL = 0x1308,
+ VG_USERREQ__MEMPOOL_CHANGE = 0x1309,
+ VG_USERREQ__MEMPOOL_EXISTS = 0x130a,
+
+ /* Allow printfs to valgrind log. */
+ VG_USERREQ__PRINTF = 0x1401,
+ VG_USERREQ__PRINTF_BACKTRACE = 0x1402,
+
+ /* Stack support. */
+ VG_USERREQ__STACK_REGISTER = 0x1501,
+ VG_USERREQ__STACK_DEREGISTER = 0x1502,
+ VG_USERREQ__STACK_CHANGE = 0x1503
+ } Vg_ClientRequest;
+
+#if !defined(__GNUC__)
+# define __extension__ /* */
+#endif
+
+/* Returns the number of Valgrinds this code is running under. That
+ is, 0 if running natively, 1 if running under Valgrind, 2 if
+ running under Valgrind which is running under another Valgrind,
+ etc. */
+#define RUNNING_ON_VALGRIND __extension__ \
+ ({unsigned int _qzz_res; \
+ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0 /* if not */, \
+ VG_USERREQ__RUNNING_ON_VALGRIND, \
+ 0, 0, 0, 0, 0); \
+ _qzz_res; \
+ })
+
+
+/* Discard translation of code in the range [_qzz_addr .. _qzz_addr +
+ _qzz_len - 1]. Useful if you are debugging a JITter or some such,
+ since it provides a way to make sure valgrind will retranslate the
+ invalidated area. Returns no value. */
+#define VALGRIND_DISCARD_TRANSLATIONS(_qzz_addr,_qzz_len) \
+ {unsigned int _qzz_res; \
+ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
+ VG_USERREQ__DISCARD_TRANSLATIONS, \
+ _qzz_addr, _qzz_len, 0, 0, 0); \
+ }
+
+
+/* These requests are for getting Valgrind itself to print something.
+ Possibly with a backtrace. This is a really ugly hack. */
+
+#if defined(NVALGRIND)
+
+# define VALGRIND_PRINTF(...)
+# define VALGRIND_PRINTF_BACKTRACE(...)
+
+#else /* NVALGRIND */
+
+/* Modern GCC will optimize the static routine out if unused,
+ and unused attribute will shut down warnings about it. */
+static int VALGRIND_PRINTF(const char *format, ...)
+ __attribute__((format(__printf__, 1, 2), __unused__));
+static int
+VALGRIND_PRINTF(const char *format, ...)
+{
+ unsigned long _qzz_res;
+ va_list vargs;
+ va_start(vargs, format);
+ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, VG_USERREQ__PRINTF,
+ (unsigned long)format, (unsigned long)vargs,
+ 0, 0, 0);
+ va_end(vargs);
+ return (int)_qzz_res;
+}
+
+static int VALGRIND_PRINTF_BACKTRACE(const char *format, ...)
+ __attribute__((format(__printf__, 1, 2), __unused__));
+static int
+VALGRIND_PRINTF_BACKTRACE(const char *format, ...)
+{
+ unsigned long _qzz_res;
+ va_list vargs;
+ va_start(vargs, format);
+ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, VG_USERREQ__PRINTF_BACKTRACE,
+ (unsigned long)format, (unsigned long)vargs,
+ 0, 0, 0);
+ va_end(vargs);
+ return (int)_qzz_res;
+}
+
+#endif /* NVALGRIND */
+
+
+/* These requests allow control to move from the simulated CPU to the
+ real CPU, calling an arbitary function.
+
+ Note that the current ThreadId is inserted as the first argument.
+ So this call:
+
+ VALGRIND_NON_SIMD_CALL2(f, arg1, arg2)
+
+ requires f to have this signature:
+
+ Word f(Word tid, Word arg1, Word arg2)
+
+ where "Word" is a word-sized type.
+
+ Note that these client requests are not entirely reliable. For example,
+ if you call a function with them that subsequently calls printf(),
+ there's a high chance Valgrind will crash. Generally, your prospects of
+ these working are made higher if the called function does not refer to
+ any global variables, and does not refer to any libc or other functions
+ (printf et al). Any kind of entanglement with libc or dynamic linking is
+ likely to have a bad outcome, for tricky reasons which we've grappled
+ with a lot in the past.
+*/
+#define VALGRIND_NON_SIMD_CALL0(_qyy_fn) \
+ __extension__ \
+ ({unsigned long _qyy_res; \
+ VALGRIND_DO_CLIENT_REQUEST(_qyy_res, 0 /* default return */, \
+ VG_USERREQ__CLIENT_CALL0, \
+ _qyy_fn, \
+ 0, 0, 0, 0); \
+ _qyy_res; \
+ })
+
+#define VALGRIND_NON_SIMD_CALL1(_qyy_fn, _qyy_arg1) \
+ __extension__ \
+ ({unsigned long _qyy_res; \
+ VALGRIND_DO_CLIENT_REQUEST(_qyy_res, 0 /* default return */, \
+ VG_USERREQ__CLIENT_CALL1, \
+ _qyy_fn, \
+ _qyy_arg1, 0, 0, 0); \
+ _qyy_res; \
+ })
+
+#define VALGRIND_NON_SIMD_CALL2(_qyy_fn, _qyy_arg1, _qyy_arg2) \
+ __extension__ \
+ ({unsigned long _qyy_res; \
+ VALGRIND_DO_CLIENT_REQUEST(_qyy_res, 0 /* default return */, \
+ VG_USERREQ__CLIENT_CALL2, \
+ _qyy_fn, \
+ _qyy_arg1, _qyy_arg2, 0, 0); \
+ _qyy_res; \
+ })
+
+#define VALGRIND_NON_SIMD_CALL3(_qyy_fn, _qyy_arg1, _qyy_arg2, _qyy_arg3) \
+ __extension__ \
+ ({unsigned long _qyy_res; \
+ VALGRIND_DO_CLIENT_REQUEST(_qyy_res, 0 /* default return */, \
+ VG_USERREQ__CLIENT_CALL3, \
+ _qyy_fn, \
+ _qyy_arg1, _qyy_arg2, \
+ _qyy_arg3, 0); \
+ _qyy_res; \
+ })
+
+
+/* Counts the number of errors that have been recorded by a tool. Nb:
+ the tool must record the errors with VG_(maybe_record_error)() or
+ VG_(unique_error)() for them to be counted. */
+#define VALGRIND_COUNT_ERRORS \
+ __extension__ \
+ ({unsigned int _qyy_res; \
+ VALGRIND_DO_CLIENT_REQUEST(_qyy_res, 0 /* default return */, \
+ VG_USERREQ__COUNT_ERRORS, \
+ 0, 0, 0, 0, 0); \
+ _qyy_res; \
+ })
+
+/* Mark a block of memory as having been allocated by a malloc()-like
+ function. `addr' is the start of the usable block (ie. after any
+ redzone) `rzB' is redzone size if the allocator can apply redzones;
+ use '0' if not. Adding redzones makes it more likely Valgrind will spot
+ block overruns. `is_zeroed' indicates if the memory is zeroed, as it is
+ for calloc(). Put it immediately after the point where a block is
+ allocated.
+
+ If you're using Memcheck: If you're allocating memory via superblocks,
+ and then handing out small chunks of each superblock, if you don't have
+ redzones on your small blocks, it's worth marking the superblock with
+ VALGRIND_MAKE_MEM_NOACCESS when it's created, so that block overruns are
+ detected. But if you can put redzones on, it's probably better to not do
+ this, so that messages for small overruns are described in terms of the
+ small block rather than the superblock (but if you have a big overrun
+ that skips over a redzone, you could miss an error this way). See
+ memcheck/tests/custom_alloc.c for an example.
+
+ WARNING: if your allocator uses malloc() or 'new' to allocate
+ superblocks, rather than mmap() or brk(), this will not work properly --
+ you'll likely get assertion failures during leak detection. This is
+ because Valgrind doesn't like seeing overlapping heap blocks. Sorry.
+
+ Nb: block must be freed via a free()-like function specified
+ with VALGRIND_FREELIKE_BLOCK or mismatch errors will occur. */
+#define VALGRIND_MALLOCLIKE_BLOCK(addr, sizeB, rzB, is_zeroed) \
+ {unsigned int _qzz_res; \
+ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
+ VG_USERREQ__MALLOCLIKE_BLOCK, \
+ addr, sizeB, rzB, is_zeroed, 0); \
+ }
+
+/* Mark a block of memory as having been freed by a free()-like function.
+ `rzB' is redzone size; it must match that given to
+ VALGRIND_MALLOCLIKE_BLOCK. Memory not freed will be detected by the leak
+ checker. Put it immediately after the point where the block is freed. */
+#define VALGRIND_FREELIKE_BLOCK(addr, rzB) \
+ {unsigned int _qzz_res; \
+ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
+ VG_USERREQ__FREELIKE_BLOCK, \
+ addr, rzB, 0, 0, 0); \
+ }
+
+/* Create a memory pool. */
+#define VALGRIND_CREATE_MEMPOOL(pool, rzB, is_zeroed) \
+ {unsigned int _qzz_res; \
+ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
+ VG_USERREQ__CREATE_MEMPOOL, \
+ pool, rzB, is_zeroed, 0, 0); \
+ }
+
+/* Destroy a memory pool. */
+#define VALGRIND_DESTROY_MEMPOOL(pool) \
+ {unsigned int _qzz_res; \
+ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
+ VG_USERREQ__DESTROY_MEMPOOL, \
+ pool, 0, 0, 0, 0); \
+ }
+
+/* Associate a piece of memory with a memory pool. */
+#define VALGRIND_MEMPOOL_ALLOC(pool, addr, size) \
+ {unsigned int _qzz_res; \
+ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
+ VG_USERREQ__MEMPOOL_ALLOC, \
+ pool, addr, size, 0, 0); \
+ }
+
+/* Disassociate a piece of memory from a memory pool. */
+#define VALGRIND_MEMPOOL_FREE(pool, addr) \
+ {unsigned int _qzz_res; \
+ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
+ VG_USERREQ__MEMPOOL_FREE, \
+ pool, addr, 0, 0, 0); \
+ }
+
+/* Disassociate any pieces outside a particular range. */
+#define VALGRIND_MEMPOOL_TRIM(pool, addr, size) \
+ {unsigned int _qzz_res; \
+ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
+ VG_USERREQ__MEMPOOL_TRIM, \
+ pool, addr, size, 0, 0); \
+ }
+
+/* Resize and/or move a piece associated with a memory pool. */
+#define VALGRIND_MOVE_MEMPOOL(poolA, poolB) \
+ {unsigned int _qzz_res; \
+ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
+ VG_USERREQ__MOVE_MEMPOOL, \
+ poolA, poolB, 0, 0, 0); \
+ }
+
+/* Resize and/or move a piece associated with a memory pool. */
+#define VALGRIND_MEMPOOL_CHANGE(pool, addrA, addrB, size) \
+ {unsigned int _qzz_res; \
+ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
+ VG_USERREQ__MEMPOOL_CHANGE, \
+ pool, addrA, addrB, size, 0); \
+ }
+
+/* Return 1 if a mempool exists, else 0. */
+#define VALGRIND_MEMPOOL_EXISTS(pool) \
+ __extension__ \
+ ({unsigned int _qzz_res; \
+ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
+ VG_USERREQ__MEMPOOL_EXISTS, \
+ pool, 0, 0, 0, 0); \
+ _qzz_res; \
+ })
+
+/* Mark a piece of memory as being a stack. Returns a stack id. */
+#define VALGRIND_STACK_REGISTER(start, end) \
+ __extension__ \
+ ({unsigned int _qzz_res; \
+ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
+ VG_USERREQ__STACK_REGISTER, \
+ start, end, 0, 0, 0); \
+ _qzz_res; \
+ })
+
+/* Unmark the piece of memory associated with a stack id as being a
+ stack. */
+#define VALGRIND_STACK_DEREGISTER(id) \
+ {unsigned int _qzz_res; \
+ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
+ VG_USERREQ__STACK_DEREGISTER, \
+ id, 0, 0, 0, 0); \
+ }
+
+/* Change the start and end address of the stack id. */
+#define VALGRIND_STACK_CHANGE(id, start, end) \
+ {unsigned int _qzz_res; \
+ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
+ VG_USERREQ__STACK_CHANGE, \
+ id, start, end, 0, 0); \
+ }
+
+
+#undef PLAT_x86_linux
+#undef PLAT_amd64_linux
+#undef PLAT_ppc32_linux
+#undef PLAT_ppc64_linux
+#undef PLAT_ppc32_aix5
+#undef PLAT_ppc64_aix5
+
+#endif /* __VALGRIND_H */
diff --git a/src/test/bench/shootout/ackermann.rs b/src/test/bench/shootout/ackermann.rs
new file mode 100644
index 00000000..27b4c3c0
--- /dev/null
+++ b/src/test/bench/shootout/ackermann.rs
@@ -0,0 +1,25 @@
+// -*- rust -*-
+
+fn ack(int m, int n) -> int {
+ if (m == 0) {
+ ret n+1;
+ } else {
+ if (n == 0) {
+ ret ack(m-1, 1);
+ } else {
+ ret ack(m-1, ack(m, n-1));
+ }
+ }
+}
+
+fn main() {
+ check (ack(0,0) == 1);
+ check (ack(3,2) == 29);
+ check (ack(3,4) == 125);
+
+ // This takes a while; but a comparison may amuse: on win32 at least, the
+ // posted C version of the 'benchmark' running ack(4,1) overruns its stack
+ // segment and crashes. We just grow our stack (to 4mb) as we go.
+
+ // check (ack(4,1) == 65533);
+} \ No newline at end of file
diff --git a/src/test/bench/shootout/binary-trees.rs b/src/test/bench/shootout/binary-trees.rs
new file mode 100644
index 00000000..bb3ab602
--- /dev/null
+++ b/src/test/bench/shootout/binary-trees.rs
@@ -0,0 +1,15 @@
+type tree = tag(nil(), node(@tree, @tree, int));
+
+fn item_check(&tree t) -> int {
+ alt (t) {
+ case (nil()) {
+ ret 0;
+ }
+ case (node(@tree left, @tree right, int item)) {
+ ret item + item_check(left) - item_check(right);
+ }
+ }
+}
+
+fn main() {
+} \ No newline at end of file
diff --git a/src/test/bench/shootout/fibo.rs b/src/test/bench/shootout/fibo.rs
new file mode 100644
index 00000000..9045f381
--- /dev/null
+++ b/src/test/bench/shootout/fibo.rs
@@ -0,0 +1,22 @@
+// -*- rust -*-
+
+fn fib(int n) -> int {
+ // Several of the posted 'benchmark' versions of this compute the
+ // wrong Fibonacci numbers, of course.
+ if (n == 0) {
+ ret 0;
+ } else {
+ if (n <= 2) {
+ ret 1;
+ } else {
+ ret fib(n-1) + fib(n-2);
+ }
+ }
+}
+
+fn main() {
+ check (fib(8) == 21);
+ check (fib(15) == 610);
+ log fib(8);
+ log fib(15);
+}
diff --git a/src/test/compile-fail/arg-count-mismatch.rs b/src/test/compile-fail/arg-count-mismatch.rs
new file mode 100644
index 00000000..18f4104e
--- /dev/null
+++ b/src/test/compile-fail/arg-count-mismatch.rs
@@ -0,0 +1,9 @@
+// error-pattern: mismatched types
+
+fn f(int x) {
+}
+
+fn main() {
+ let () i;
+ i = f();
+}
diff --git a/src/test/compile-fail/arg-type-mismatch.rs b/src/test/compile-fail/arg-type-mismatch.rs
new file mode 100644
index 00000000..3a61992a
--- /dev/null
+++ b/src/test/compile-fail/arg-type-mismatch.rs
@@ -0,0 +1,10 @@
+
+// error-pattern: mismatched types
+
+fn f(int x) {
+}
+
+fn main() {
+ let () i;
+ i = f(());
+} \ No newline at end of file
diff --git a/src/test/compile-fail/bad-env-capture.rs b/src/test/compile-fail/bad-env-capture.rs
new file mode 100644
index 00000000..013bb56e
--- /dev/null
+++ b/src/test/compile-fail/bad-env-capture.rs
@@ -0,0 +1,10 @@
+// error-pattern: attempted dynamic environment-capture
+fn foo() {
+ let int x;
+ fn bar() {
+ log x;
+ }
+}
+fn main() {
+ foo();
+} \ No newline at end of file
diff --git a/src/test/compile-fail/bad-main.rs b/src/test/compile-fail/bad-main.rs
new file mode 100644
index 00000000..8e3fa503
--- /dev/null
+++ b/src/test/compile-fail/bad-main.rs
@@ -0,0 +1,4 @@
+// error-pattern: bad type signature
+
+fn main(int x) {
+}
diff --git a/src/test/compile-fail/bad-name.rs b/src/test/compile-fail/bad-name.rs
new file mode 100644
index 00000000..44a30219
--- /dev/null
+++ b/src/test/compile-fail/bad-name.rs
@@ -0,0 +1,6 @@
+
+// error-pattern: malformed name
+
+fn main() {
+ let x.y[int].z foo;
+}
diff --git a/src/test/compile-fail/bad-type-env-capture.rs b/src/test/compile-fail/bad-type-env-capture.rs
new file mode 100644
index 00000000..e18e63a7
--- /dev/null
+++ b/src/test/compile-fail/bad-type-env-capture.rs
@@ -0,0 +1,3 @@
+// error-pattern: attempted dynamic environment-capture
+fn foo[T]() { obj bar(T b) {} }
+fn main() {} \ No newline at end of file
diff --git a/src/test/compile-fail/bogus-tag.rs b/src/test/compile-fail/bogus-tag.rs
new file mode 100644
index 00000000..35c5736c
--- /dev/null
+++ b/src/test/compile-fail/bogus-tag.rs
@@ -0,0 +1,19 @@
+// -*- rust -*-
+
+type color = tag(
+ rgb(int, int, int),
+ rgba(int, int, int, int)
+);
+
+fn main() -> () {
+ let color red = rgb(255, 0, 0);
+ alt (red) {
+ case (rgb(int r, int g, int b)) {
+ log "rgb";
+ }
+ case (hsl(int h, int s, int l)) {
+ log "hsl";
+ }
+ }
+}
+
diff --git a/src/test/compile-fail/comm-makes-io.rs b/src/test/compile-fail/comm-makes-io.rs
new file mode 100644
index 00000000..50f87d0c
--- /dev/null
+++ b/src/test/compile-fail/comm-makes-io.rs
@@ -0,0 +1,6 @@
+// error-pattern: calculated effect is 'io'
+
+fn main() {
+ let chan[int] c = chan();
+ c <| 10;
+} \ No newline at end of file
diff --git a/src/test/compile-fail/dead-code-be.rs b/src/test/compile-fail/dead-code-be.rs
new file mode 100644
index 00000000..060b466a
--- /dev/null
+++ b/src/test/compile-fail/dead-code-be.rs
@@ -0,0 +1,11 @@
+// -*- rust -*-
+
+fn f(str caller) {
+ log caller;
+}
+
+fn main() {
+ be f("main");
+ log "Paul is dead";
+}
+
diff --git a/src/test/compile-fail/dead-code-ret.rs b/src/test/compile-fail/dead-code-ret.rs
new file mode 100644
index 00000000..7fbdcb0e
--- /dev/null
+++ b/src/test/compile-fail/dead-code-ret.rs
@@ -0,0 +1,11 @@
+// -*- rust -*-
+
+fn f(str caller) {
+ log caller;
+}
+
+fn main() {
+ ret f("main");
+ log "Paul is dead";
+}
+
diff --git a/src/test/compile-fail/direct-obj-fn-call.rs b/src/test/compile-fail/direct-obj-fn-call.rs
new file mode 100644
index 00000000..e13db876
--- /dev/null
+++ b/src/test/compile-fail/direct-obj-fn-call.rs
@@ -0,0 +1,12 @@
+
+// error-pattern: mismatched types
+
+obj x() {
+ fn hello() {
+ log "hello";
+ }
+}
+
+fn main() {
+ x.hello();
+} \ No newline at end of file
diff --git a/src/test/compile-fail/export.rs b/src/test/compile-fail/export.rs
new file mode 100644
index 00000000..7a00f221
--- /dev/null
+++ b/src/test/compile-fail/export.rs
@@ -0,0 +1,14 @@
+// error-pattern: unknown module item
+mod foo {
+ export x;
+ fn x(int y) {
+ log y;
+ }
+ fn z(int y) {
+ log y;
+ }
+}
+
+fn main() {
+ foo.z(10);
+}
diff --git a/src/test/compile-fail/fru-extra-field.rs b/src/test/compile-fail/fru-extra-field.rs
new file mode 100644
index 00000000..2762b54f
--- /dev/null
+++ b/src/test/compile-fail/fru-extra-field.rs
@@ -0,0 +1,9 @@
+// -*- rust -*-
+
+type point = rec(int x, int y);
+
+fn main() {
+ let point origin = rec(x=0, y=0);
+
+ let point origin3d = rec(z=0 with origin);
+}
diff --git a/src/test/compile-fail/fru-typestate.rs b/src/test/compile-fail/fru-typestate.rs
new file mode 100644
index 00000000..c15683c8
--- /dev/null
+++ b/src/test/compile-fail/fru-typestate.rs
@@ -0,0 +1,10 @@
+// -*- rust -*-
+
+type point = rec(int x, int y);
+
+fn main() {
+ let point origin;
+
+ let point right = rec(x=10 with origin);
+ origin = rec(x=0, y=0);
+}
diff --git a/src/test/compile-fail/impure-pred.rs b/src/test/compile-fail/impure-pred.rs
new file mode 100644
index 00000000..811d595a
--- /dev/null
+++ b/src/test/compile-fail/impure-pred.rs
@@ -0,0 +1,19 @@
+// -*- rust -*-
+
+// error-pattern: impure function used in constraint
+
+fn f(int a, int b) : lt(a,b) {
+}
+
+io fn lt(int a, int b) -> bool {
+ let port[int] p = port();
+ let chan[int] c = chan(p);
+ c <| 10;
+}
+
+fn main() {
+ let int a = 10;
+ let int b = 23;
+ check lt(a,b);
+ f(a,b);
+}
diff --git a/src/test/compile-fail/infinite-tag-type-recursion.rs b/src/test/compile-fail/infinite-tag-type-recursion.rs
new file mode 100644
index 00000000..19aea090
--- /dev/null
+++ b/src/test/compile-fail/infinite-tag-type-recursion.rs
@@ -0,0 +1,9 @@
+// -*- rust -*-
+
+// error-pattern: Infinite type recursion
+
+type mlist = tag(cons(int,mlist), nil());
+
+fn main() {
+ auto a = cons(10, cons(11, nil()));
+}
diff --git a/src/test/compile-fail/infinite-vec-type-recursion.rs b/src/test/compile-fail/infinite-vec-type-recursion.rs
new file mode 100644
index 00000000..7c82700a
--- /dev/null
+++ b/src/test/compile-fail/infinite-vec-type-recursion.rs
@@ -0,0 +1,9 @@
+// -*- rust -*-
+
+// error-pattern: Infinite type recursion
+
+type x = vec[x];
+
+fn main() {
+ let x b = vec();
+}
diff --git a/src/test/compile-fail/io-infects-caller.rs b/src/test/compile-fail/io-infects-caller.rs
new file mode 100644
index 00000000..2f5f003f
--- /dev/null
+++ b/src/test/compile-fail/io-infects-caller.rs
@@ -0,0 +1,10 @@
+// error-pattern: calculated effect is 'io'
+
+io fn foo() {
+ let chan[int] c = chan();
+ c <| 10;
+}
+
+fn main() {
+ foo();
+} \ No newline at end of file
diff --git a/src/test/compile-fail/log-type-error.rs b/src/test/compile-fail/log-type-error.rs
new file mode 100644
index 00000000..c8a5df9c
--- /dev/null
+++ b/src/test/compile-fail/log-type-error.rs
@@ -0,0 +1,6 @@
+// error-pattern: mismatched types
+
+fn main() {
+ log main;
+}
+
diff --git a/src/test/compile-fail/native-makes-unsafe.rs b/src/test/compile-fail/native-makes-unsafe.rs
new file mode 100644
index 00000000..d6e77b0d
--- /dev/null
+++ b/src/test/compile-fail/native-makes-unsafe.rs
@@ -0,0 +1,9 @@
+// error-pattern: calculated effect is 'unsafe'
+
+native mod foo {
+ fn naughty();
+}
+
+fn main() {
+ foo.naughty();
+} \ No newline at end of file
diff --git a/src/test/compile-fail/not-a-pred.rs b/src/test/compile-fail/not-a-pred.rs
new file mode 100644
index 00000000..4a899512
--- /dev/null
+++ b/src/test/compile-fail/not-a-pred.rs
@@ -0,0 +1,16 @@
+// -*- rust -*-
+
+// error-pattern: mismatched types
+
+fn f(int a, int b) : lt(a,b) {
+}
+
+obj lt(int a, int b) {
+}
+
+fn main() {
+ let int a = 10;
+ let int b = 23;
+ check lt(a,b);
+ f(a,b);
+}
diff --git a/src/test/compile-fail/output-type-mismatch.rs b/src/test/compile-fail/output-type-mismatch.rs
new file mode 100644
index 00000000..c979f065
--- /dev/null
+++ b/src/test/compile-fail/output-type-mismatch.rs
@@ -0,0 +1,9 @@
+// error-pattern: mismatched types
+
+fn f() {
+}
+
+fn main() {
+ let int i;
+ i = f();
+}
diff --git a/src/test/compile-fail/pred-on-wrong-slots.rs b/src/test/compile-fail/pred-on-wrong-slots.rs
new file mode 100644
index 00000000..cf31b8bd
--- /dev/null
+++ b/src/test/compile-fail/pred-on-wrong-slots.rs
@@ -0,0 +1,20 @@
+// -*- rust -*-
+
+// error-pattern: Unsatisfied .* lt(a, c)
+
+fn f(int a, int b) : lt(a,b) {
+}
+
+fn lt(int a, int b) -> bool {
+ ret a < b;
+}
+
+fn main() {
+ let int a = 10;
+ let int b = 23;
+ let int c = 77;
+ check lt(a,b);
+ check lt(b,c);
+ f(a,b);
+ f(a,c);
+}
diff --git a/src/test/compile-fail/rec-missing-fields.rs b/src/test/compile-fail/rec-missing-fields.rs
new file mode 100644
index 00000000..83736d5e
--- /dev/null
+++ b/src/test/compile-fail/rec-missing-fields.rs
@@ -0,0 +1,10 @@
+// -*- rust -*-
+
+// Issue #51.
+
+type point = rec(int x, int y);
+
+fn main() {
+ let point p = rec(x=10);
+ log p.y;
+}
diff --git a/src/test/compile-fail/return-uninit.rs b/src/test/compile-fail/return-uninit.rs
new file mode 100644
index 00000000..37117169
--- /dev/null
+++ b/src/test/compile-fail/return-uninit.rs
@@ -0,0 +1,10 @@
+// error-pattern: precondition constraint
+
+fn f() -> int {
+ let int x;
+ ret x;
+}
+
+fn main() {
+ f();
+} \ No newline at end of file
diff --git a/src/test/compile-fail/slot-as-pred.rs b/src/test/compile-fail/slot-as-pred.rs
new file mode 100644
index 00000000..1da8df84
--- /dev/null
+++ b/src/test/compile-fail/slot-as-pred.rs
@@ -0,0 +1,14 @@
+// -*- rust -*-
+
+// error-pattern: mismatched types
+
+fn f(int a, int b) : lt(a,b) {
+}
+
+fn main() {
+ let int lt;
+ let int a = 10;
+ let int b = 23;
+ check lt(a,b);
+ f(a,b);
+}
diff --git a/src/test/compile-fail/spawn-non-nil-fn.rs b/src/test/compile-fail/spawn-non-nil-fn.rs
new file mode 100644
index 00000000..4d869bba
--- /dev/null
+++ b/src/test/compile-fail/spawn-non-nil-fn.rs
@@ -0,0 +1,9 @@
+// error-pattern: mismatched types
+
+fn f(int x) -> int {
+ ret x;
+}
+
+fn main() {
+ spawn f(10);
+}
diff --git a/src/test/compile-fail/type-shadow.rs b/src/test/compile-fail/type-shadow.rs
new file mode 100644
index 00000000..8f8aff9a
--- /dev/null
+++ b/src/test/compile-fail/type-shadow.rs
@@ -0,0 +1,12 @@
+// -*- rust -*-
+
+// error-pattern: mismatched types
+
+fn main() {
+ type X = int;
+ type Y = X;
+ if (true) {
+ type X = str;
+ let Y y = "hello";
+ }
+}
diff --git a/src/test/compile-fail/unnecessary-io.rs b/src/test/compile-fail/unnecessary-io.rs
new file mode 100644
index 00000000..45a93798
--- /dev/null
+++ b/src/test/compile-fail/unnecessary-io.rs
@@ -0,0 +1,4 @@
+// error-pattern: calculated effect is ''
+io fn main() {
+ log "hi";
+} \ No newline at end of file
diff --git a/src/test/compile-fail/unnecessary-unsafe.rs b/src/test/compile-fail/unnecessary-unsafe.rs
new file mode 100644
index 00000000..6705bf34
--- /dev/null
+++ b/src/test/compile-fail/unnecessary-unsafe.rs
@@ -0,0 +1,4 @@
+// error-pattern: calculated effect is ''
+unsafe fn main() {
+ log "hi";
+} \ No newline at end of file
diff --git a/src/test/compile-fail/unsafe-infects-caller.rs b/src/test/compile-fail/unsafe-infects-caller.rs
new file mode 100644
index 00000000..28daea6c
--- /dev/null
+++ b/src/test/compile-fail/unsafe-infects-caller.rs
@@ -0,0 +1,13 @@
+// error-pattern: calculated effect is 'unsafe'
+
+native mod foo {
+ fn naughty();
+}
+
+unsafe fn bar() {
+ foo.naughty();
+}
+
+fn main() {
+ bar();
+} \ No newline at end of file
diff --git a/src/test/compile-fail/while-bypass.rs b/src/test/compile-fail/while-bypass.rs
new file mode 100644
index 00000000..1de89e90
--- /dev/null
+++ b/src/test/compile-fail/while-bypass.rs
@@ -0,0 +1,13 @@
+// error-pattern: precondition constraint
+
+fn f() -> int {
+ let int x;
+ while(true) {
+ x = 10;
+ }
+ ret x;
+}
+
+fn main() {
+ f();
+}
diff --git a/src/test/compile-fail/while-expr.rs b/src/test/compile-fail/while-expr.rs
new file mode 100644
index 00000000..9077c18f
--- /dev/null
+++ b/src/test/compile-fail/while-expr.rs
@@ -0,0 +1,7 @@
+// error-pattern: precondition constraint
+
+fn main() {
+ let bool x;
+ while(x) {
+ }
+}
diff --git a/src/test/compile-fail/while-type-error.rs b/src/test/compile-fail/while-type-error.rs
new file mode 100644
index 00000000..07d7867e
--- /dev/null
+++ b/src/test/compile-fail/while-type-error.rs
@@ -0,0 +1,7 @@
+// error-pattern: mismatched types
+
+fn main() {
+ while (main) {
+ }
+}
+
diff --git a/src/test/compile-fail/writing-through-read-alias.rs b/src/test/compile-fail/writing-through-read-alias.rs
new file mode 100644
index 00000000..b3d21521
--- /dev/null
+++ b/src/test/compile-fail/writing-through-read-alias.rs
@@ -0,0 +1,14 @@
+// -*- rust -*-
+
+// error-pattern: writing to non-mutable slot
+
+type point = rec(int x, int y, int z);
+
+fn f(&point p) {
+ p.x = 13;
+}
+
+fn main() {
+ let point x = rec(x=10, y=11, z=12);
+ f(x);
+}
diff --git a/src/test/run-fail/explicit-fail.rs b/src/test/run-fail/explicit-fail.rs
new file mode 100644
index 00000000..cb0e37e5
--- /dev/null
+++ b/src/test/run-fail/explicit-fail.rs
@@ -0,0 +1,5 @@
+// error-pattern:explicit
+
+fn main() {
+ fail;
+}
diff --git a/src/test/run-fail/fail.rs b/src/test/run-fail/fail.rs
new file mode 100644
index 00000000..8808b8c8
--- /dev/null
+++ b/src/test/run-fail/fail.rs
@@ -0,0 +1,5 @@
+// error-pattern:1 == 2
+
+fn main() {
+ check (1 == 2);
+}
diff --git a/src/test/run-fail/linked-failure.rs b/src/test/run-fail/linked-failure.rs
new file mode 100644
index 00000000..419fa0f3
--- /dev/null
+++ b/src/test/run-fail/linked-failure.rs
@@ -0,0 +1,14 @@
+// -*- rust -*-
+
+// error-pattern:1 == 2
+
+fn child() {
+ check (1 == 2);
+}
+
+io fn main() {
+ let port[int] p = port();
+ spawn child();
+ let int x;
+ x <- p;
+}
diff --git a/src/test/run-fail/pred.rs b/src/test/run-fail/pred.rs
new file mode 100644
index 00000000..e5456a5e
--- /dev/null
+++ b/src/test/run-fail/pred.rs
@@ -0,0 +1,17 @@
+// -*- rust -*-
+
+// error-pattern:predicate check
+
+fn f(int a, int b) : lt(a,b) {
+}
+
+fn lt(int a, int b) -> bool {
+ ret a < b;
+}
+
+fn main() {
+ let int a = 10;
+ let int b = 23;
+ check lt(b,a);
+ f(b,a);
+}
diff --git a/src/test/run-fail/str-overrun.rs b/src/test/run-fail/str-overrun.rs
new file mode 100644
index 00000000..7d5a12cb
--- /dev/null
+++ b/src/test/run-fail/str-overrun.rs
@@ -0,0 +1,16 @@
+// -*- rust -*-
+
+// error-pattern:bounds check
+
+fn main() {
+ let str s = "hello";
+ let int x = 0;
+ check (s.(x) == u8(0x68));
+
+ // NB: at the moment a string always has a trailing NULL,
+ // so the largest index value on the string above is 5, not
+ // 4. Possibly change this.
+
+ // Bounds-check failure.
+ check (s.(x + 6) == u8(0x0));
+}
diff --git a/src/test/run-fail/vec-overrun.rs b/src/test/run-fail/vec-overrun.rs
new file mode 100644
index 00000000..e646a107
--- /dev/null
+++ b/src/test/run-fail/vec-overrun.rs
@@ -0,0 +1,11 @@
+// -*- rust -*-
+
+// error-pattern:bounds check
+
+fn main() {
+ let vec[int] v = vec(10);
+ let int x = 0;
+ check (v.(x) == 10);
+ // Bounds-check failure.
+ check (v.(x + 2) == 20);
+}
diff --git a/src/test/run-fail/vec-underrun.rs b/src/test/run-fail/vec-underrun.rs
new file mode 100644
index 00000000..c9073030
--- /dev/null
+++ b/src/test/run-fail/vec-underrun.rs
@@ -0,0 +1,11 @@
+// -*- rust -*-
+
+// error-pattern:bounds check
+
+fn main() {
+ let vec[int] v = vec(10, 20);
+ let int x = 0;
+ check (v.(x) == 10);
+ // Bounds-check failure.
+ check (v.(x-1) == 20);
+}
diff --git a/src/test/run-pass/acyclic-unwind.rs b/src/test/run-pass/acyclic-unwind.rs
new file mode 100644
index 00000000..b549cffe
--- /dev/null
+++ b/src/test/run-pass/acyclic-unwind.rs
@@ -0,0 +1,30 @@
+// -*- rust -*-
+
+io fn f(chan[int] c)
+{
+ type t = tup(int,int,int);
+
+ // Allocate an exterior.
+ let @t x = tup(1,2,3);
+
+ // Signal parent that we've allocated an exterior.
+ c <| 1;
+
+ while (true) {
+ // spin waiting for the parent to kill us.
+ log "child waiting to die...";
+ c <| 1;
+ }
+}
+
+
+io fn main() {
+ let port[int] p = port();
+ spawn f(chan(p));
+ let int i;
+
+ // synchronize on event from child.
+ i <- p;
+
+ log "parent exiting, killing child";
+}
diff --git a/src/test/run-pass/alt-tag.rs b/src/test/run-pass/alt-tag.rs
new file mode 100644
index 00000000..d40c4eec
--- /dev/null
+++ b/src/test/run-pass/alt-tag.rs
@@ -0,0 +1,39 @@
+// -*- rust -*-
+
+type color = tag(
+ rgb(int, int, int),
+ rgba(int, int, int, int),
+ hsl(int, int, int)
+);
+
+fn process(color c) -> int {
+ let int x;
+ alt (c) {
+ case (rgb(r, _, _)) {
+ log "rgb";
+ log r;
+ x = r;
+ }
+ case (rgba(_, _, _, a)) {
+ log "rgba";
+ log a;
+ x = a;
+ }
+ case (hsl(_, s, _)) {
+ log "hsl";
+ log s;
+ x = s;
+ }
+ }
+ ret x;
+}
+
+fn main() {
+ let color gray = rgb(127, 127, 127);
+ let color clear = rgba(50, 150, 250, 0);
+ let color red = hsl(0, 255, 255);
+ check (process(gray) == 127);
+ check (process(clear) == 0);
+ check (process(red) == 255);
+}
+
diff --git a/src/test/run-pass/argv.rs b/src/test/run-pass/argv.rs
new file mode 100644
index 00000000..92d5fcc6
--- /dev/null
+++ b/src/test/run-pass/argv.rs
@@ -0,0 +1,9 @@
+fn main(vec[str] args) {
+ let vec[str] vs = vec("hi", "there", "this", "is", "a", "vec");
+ let vec[vec[str]] vvs = vec(args, vs);
+ for (vec[str] vs in vvs) {
+ for (str s in vs) {
+ log s;
+ }
+ }
+}
diff --git a/src/test/run-pass/basic.rs b/src/test/run-pass/basic.rs
new file mode 100644
index 00000000..95e4bff8
--- /dev/null
+++ b/src/test/run-pass/basic.rs
@@ -0,0 +1,50 @@
+// -*- rust -*-
+
+io fn a(chan[int] c) {
+ if (true) {
+ log "task a";
+ log "task a";
+ log "task a";
+ log "task a";
+ log "task a";
+ }
+ c <| 10;
+}
+
+fn k(int x) -> int {
+ ret 15;
+}
+
+fn g(int x, str y) -> int {
+ log x;
+ log y;
+ let int z = k(1);
+ ret z;
+}
+
+io fn main() {
+ let int n = 2 + 3 * 7;
+ let str s = "hello there";
+ let port[int] p = port();
+ spawn a(chan(p));
+ spawn b(chan(p));
+ let int x = 10;
+ x = g(n,s);
+ log x;
+ n <- p;
+ n <- p;
+ // FIXME: use signal-channel for this.
+ log "children finished, root finishing";
+}
+
+io fn b(chan[int] c) {
+ if (true) {
+ log "task b";
+ log "task b";
+ log "task b";
+ log "task b";
+ log "task b";
+ log "task b";
+ }
+ c <| 10;
+}
diff --git a/src/test/run-pass/bind-obj-ctor.rs b/src/test/run-pass/bind-obj-ctor.rs
new file mode 100644
index 00000000..8780b22a
--- /dev/null
+++ b/src/test/run-pass/bind-obj-ctor.rs
@@ -0,0 +1,17 @@
+fn main() {
+ // Testcase for issue #59.
+ obj simple(int x, int y) {
+ fn sum() -> int {
+ ret x + y;
+ }
+ }
+
+ auto obj0 = simple(1,2);
+ auto ctor0 = bind simple(1, _);
+ auto ctor1 = bind simple(_, 2);
+ auto obj1 = ctor0(2);
+ auto obj2 = ctor1(1);
+ check (obj0.sum() == 3);
+ check (obj1.sum() == 3);
+ check (obj2.sum() == 3);
+}
diff --git a/src/test/run-pass/bind-thunk.rs b/src/test/run-pass/bind-thunk.rs
new file mode 100644
index 00000000..be6e1b24
--- /dev/null
+++ b/src/test/run-pass/bind-thunk.rs
@@ -0,0 +1,11 @@
+// -*- rust -*-
+
+fn f() -> int {
+ ret 42;
+}
+
+fn main() {
+ let fn() -> int g = bind f();
+ let int i = g();
+ check(i == 42);
+}
diff --git a/src/test/run-pass/bind-trivial.rs b/src/test/run-pass/bind-trivial.rs
new file mode 100644
index 00000000..fbd6e78d
--- /dev/null
+++ b/src/test/run-pass/bind-trivial.rs
@@ -0,0 +1,11 @@
+// -*- rust -*-
+
+fn f(int n) -> int {
+ ret n;
+}
+
+fn main() {
+ let fn(int) -> int g = bind f(_);
+ let int i = g(42);
+ check(i == 42);
+}
diff --git a/src/test/run-pass/bitwise.rs b/src/test/run-pass/bitwise.rs
new file mode 100644
index 00000000..36b58a91
--- /dev/null
+++ b/src/test/run-pass/bitwise.rs
@@ -0,0 +1,21 @@
+// -*- rust -*-
+
+fn main() {
+ let int a = 1;
+ let int b = 2;
+ a ^= b;
+ b ^= a;
+ a = a ^ b;
+ log a;
+ log b;
+ check (b == 1);
+ check (a == 2);
+
+ check (~(0xf0) & 0xff == 0xf);
+ check (0xf0 | 0xf == 0xff);
+ check (0xf << 4 == 0xf0);
+ check (0xf0 >> 4 == 0xf);
+ check (-16 >>> 2 == -4);
+ check (0b1010_1010 | 0b0101_0101 == 0xff);
+}
+
diff --git a/src/test/run-pass/box-unbox.rs b/src/test/run-pass/box-unbox.rs
new file mode 100644
index 00000000..821ac74c
--- /dev/null
+++ b/src/test/run-pass/box-unbox.rs
@@ -0,0 +1,10 @@
+type box[T] = tup(@T);
+
+fn unbox[T](box[T] b) -> T { ret b._0; }
+
+fn main() {
+ let int foo = 17;
+ let box[int] bfoo = tup(foo);
+ log "see what's in our box";
+ check (unbox[int](bfoo) == foo);
+}
diff --git a/src/test/run-pass/cast.rs b/src/test/run-pass/cast.rs
new file mode 100644
index 00000000..ee2fb186
--- /dev/null
+++ b/src/test/run-pass/cast.rs
@@ -0,0 +1,16 @@
+// -*- rust -*-
+
+
+fn main() {
+ let int i = int('Q');
+ check (i == 0x51);
+ let u32 u = u32(i);
+ check (u == u32(0x51));
+ check (u == u32('Q'));
+ check (i8(i) == i8('Q'));
+ check (i8(u8(i)) == i8(u8('Q')));
+ check (char(0x51) == 'Q');
+
+ check (true == bool(1));
+ check (u32(0) == u32(false));
+}
diff --git a/src/test/run-pass/char.rs b/src/test/run-pass/char.rs
new file mode 100644
index 00000000..123f2eb2
--- /dev/null
+++ b/src/test/run-pass/char.rs
@@ -0,0 +1,12 @@
+fn main() {
+ let char c = 'x';
+ let char d = 'x';
+ check(c == 'x');
+ check('x' == c);
+ check(c == c);
+ check(c == d);
+ check(d == c);
+ check (d == 'x');
+ check('x' == d);
+}
+
diff --git a/src/test/run-pass/clone-with-exterior.rs b/src/test/run-pass/clone-with-exterior.rs
new file mode 100644
index 00000000..7de90425
--- /dev/null
+++ b/src/test/run-pass/clone-with-exterior.rs
@@ -0,0 +1,10 @@
+fn f(@rec(int a, int b) x) {
+ check (x.a == 10);
+ check (x.b == 12);
+}
+
+fn main() {
+ let @rec(int a, int b) z = rec(a=10, b=12);
+ let task p = spawn thread f(z);
+ join p;
+} \ No newline at end of file
diff --git a/src/test/run-pass/comm.rs b/src/test/run-pass/comm.rs
new file mode 100644
index 00000000..129b3bdc
--- /dev/null
+++ b/src/test/run-pass/comm.rs
@@ -0,0 +1,16 @@
+// -*- rust -*-
+
+io fn main() {
+ let port[int] p = port();
+ spawn child(chan(p));
+ let int y;
+ y <- p;
+ log "received";
+ log y;
+ check (y == 10);
+}
+
+io fn child(chan[int] c) {
+ c <| 10;
+}
+
diff --git a/src/test/run-pass/command-line-args.rs b/src/test/run-pass/command-line-args.rs
new file mode 100644
index 00000000..5801f34c
--- /dev/null
+++ b/src/test/run-pass/command-line-args.rs
@@ -0,0 +1,3 @@
+fn main(vec[str] args) {
+ log args.(0);
+}
diff --git a/src/test/run-pass/complex.rs b/src/test/run-pass/complex.rs
new file mode 100644
index 00000000..3a6c13f3
--- /dev/null
+++ b/src/test/run-pass/complex.rs
@@ -0,0 +1,32 @@
+// -*- rust -*-
+
+type t = int;
+fn putstr(str s) {}
+fn putint(int i) {
+ let int i = 33;
+ while (i < 36) {
+ putstr("hi"); i = i + 1;
+ }
+}
+fn zerg(int i) -> int { ret i; }
+fn foo(int x) -> int {
+ let t y = x + 2;
+ putstr("hello");
+ while (y < 10) {
+ putint(y);
+ if (y * 3 == 4) {
+ y = y + 2;
+ }
+ }
+ let t z;
+ z = 0x55;
+ foo(z);
+}
+
+fn main() {
+ let int x = 2 + 2;
+ log x;
+ log "hello, world";
+ log 10;
+}
+
diff --git a/src/test/run-pass/dead-code-one-arm-if.rs b/src/test/run-pass/dead-code-one-arm-if.rs
new file mode 100644
index 00000000..208d62e4
--- /dev/null
+++ b/src/test/run-pass/dead-code-one-arm-if.rs
@@ -0,0 +1,8 @@
+// -*- rust -*-
+
+fn main() {
+ if (1 == 1) {
+ ret;
+ }
+ log "Paul is dead";
+}
diff --git a/src/test/run-pass/deep.rs b/src/test/run-pass/deep.rs
new file mode 100644
index 00000000..5131c42b
--- /dev/null
+++ b/src/test/run-pass/deep.rs
@@ -0,0 +1,14 @@
+// -*- rust -*-
+
+fn f(int x) -> int {
+ if (x == 1) {
+ ret 1;
+ } else {
+ let int y = 1 + f(x-1);
+ ret y;
+ }
+}
+
+fn main() {
+ check (f(5000) == 5000);
+}
diff --git a/src/test/run-pass/div-mod.rs b/src/test/run-pass/div-mod.rs
new file mode 100644
index 00000000..cfa0fbe9
--- /dev/null
+++ b/src/test/run-pass/div-mod.rs
@@ -0,0 +1,17 @@
+// -*- rust -*-
+
+fn main() {
+ let int x = 15;
+ let int y = 5;
+ check(x / 5 == 3);
+ check(x / 4 == 3);
+ check(x / 3 == 5);
+ check(x / y == 3);
+ check(15 / y == 3);
+
+ check(x % 5 == 0);
+ check(x % 4 == 3);
+ check(x % 3 == 0);
+ check(x % y == 0);
+ check(15 % y == 0);
+}
diff --git a/src/test/run-pass/drop-on-ret.rs b/src/test/run-pass/drop-on-ret.rs
new file mode 100644
index 00000000..9ebbe3ae
--- /dev/null
+++ b/src/test/run-pass/drop-on-ret.rs
@@ -0,0 +1,12 @@
+// -*- rust -*-
+
+fn f() -> int {
+ if (true) {
+ let str s = "should not leak";
+ ret 1;
+ }
+ ret 0;
+}
+fn main() {
+ f();
+}
diff --git a/src/test/run-pass/else-if.rs b/src/test/run-pass/else-if.rs
new file mode 100644
index 00000000..9e3eac14
--- /dev/null
+++ b/src/test/run-pass/else-if.rs
@@ -0,0 +1,19 @@
+fn main() {
+ if (1 == 2) {
+ check(false);
+ } else if (2 == 3) {
+ check(false);
+ } else if (3 == 4) {
+ check(false);
+ } else {
+ check(true);
+ }
+
+
+ if (1 == 2) {
+ check(false);
+ } else if (2 == 2) {
+ check(true);
+ }
+
+} \ No newline at end of file
diff --git a/src/test/run-pass/export-non-interference.rs b/src/test/run-pass/export-non-interference.rs
new file mode 100644
index 00000000..c0f1843f
--- /dev/null
+++ b/src/test/run-pass/export-non-interference.rs
@@ -0,0 +1,6 @@
+export foo;
+
+type list_cell[T] = tag(cons(@list_cell[T]));
+
+fn main() {
+}
diff --git a/src/test/run-pass/exterior.rs b/src/test/run-pass/exterior.rs
new file mode 100644
index 00000000..bb0b91eb
--- /dev/null
+++ b/src/test/run-pass/exterior.rs
@@ -0,0 +1,18 @@
+// -*- rust -*-
+
+type point = rec(int x, int y, mutable int z);
+
+fn f(@point p) {
+ check (p.z == 12);
+ p.z = 13;
+ check (p.z == 13);
+}
+
+fn main() {
+ let point a = rec(x=10, y=11, z=mutable 12);
+ let @point b = a;
+ check (b.z == 12);
+ f(b);
+ check (a.z == 12);
+ check (b.z == 13);
+}
diff --git a/src/test/run-pass/fact.rs b/src/test/run-pass/fact.rs
new file mode 100644
index 00000000..91cf099c
--- /dev/null
+++ b/src/test/run-pass/fact.rs
@@ -0,0 +1,21 @@
+// -*- rust -*-
+
+fn f(int x) -> int {
+ // log "in f:";
+ log x;
+ if (x == 1) {
+ // log "bottoming out";
+ ret 1;
+ } else {
+ // log "recurring";
+ let int y = x * f(x-1);
+ // log "returned";
+ log y;
+ ret y;
+ }
+}
+fn main () {
+ check (f(5) == 120);
+ // log "all done";
+}
+
diff --git a/src/test/run-pass/foreach-put-structured.rs b/src/test/run-pass/foreach-put-structured.rs
new file mode 100644
index 00000000..43d8b5c3
--- /dev/null
+++ b/src/test/run-pass/foreach-put-structured.rs
@@ -0,0 +1,22 @@
+iter pairs() -> tup(int,int) {
+ let int i = 0;
+ let int j = 0;
+ while (i < 10) {
+ put tup(i, j);
+ i += 1;
+ j += i;
+ }
+}
+
+fn main() {
+ let int i = 10;
+ let int j = 0;
+ for each (tup(int,int) p in pairs()) {
+ log p._0;
+ log p._1;
+ check (p._0 + 10 == i);
+ i += 1;
+ j = p._1;
+ }
+ check(j == 45);
+} \ No newline at end of file
diff --git a/src/test/run-pass/foreach-simple-outer-slot.rs b/src/test/run-pass/foreach-simple-outer-slot.rs
new file mode 100644
index 00000000..efc6e8ca
--- /dev/null
+++ b/src/test/run-pass/foreach-simple-outer-slot.rs
@@ -0,0 +1,22 @@
+// -*- rust -*-
+
+fn main() {
+ let int sum = 0;
+ for each (int i in first_ten()) {
+ log "main";
+ log i;
+ sum = sum + i;
+ }
+ log "sum";
+ log sum;
+ check (sum == 45);
+}
+
+iter first_ten() -> int {
+ let int i = 0;
+ while (i < 10) {
+ log "first_ten";
+ put i;
+ i = i + 1;
+ }
+}
diff --git a/src/test/run-pass/foreach-simple.rs b/src/test/run-pass/foreach-simple.rs
new file mode 100644
index 00000000..df0551aa
--- /dev/null
+++ b/src/test/run-pass/foreach-simple.rs
@@ -0,0 +1,17 @@
+// -*- rust -*-
+
+fn main() {
+ for each (int i in first_ten()) {
+ log "main";
+ }
+}
+
+iter first_ten() -> int {
+ let int i = 90;
+ while (i < 100) {
+ log "first_ten";
+ log i;
+ put i;
+ i = i + 1;
+ }
+}
diff --git a/src/test/run-pass/fun-call-variants.rs b/src/test/run-pass/fun-call-variants.rs
new file mode 100644
index 00000000..59446b4a
--- /dev/null
+++ b/src/test/run-pass/fun-call-variants.rs
@@ -0,0 +1,19 @@
+// -*- rust -*-
+
+fn ho(fn(int) -> int f) -> int {
+ let int n = f(3);
+ ret n;
+}
+
+fn direct(int x) -> int {
+ ret x + 1;
+}
+
+fn main() {
+ let int a = direct(3); // direct
+ //let int b = ho(direct); // indirect unbound
+ let int c = ho(bind direct(_)); // indirect bound
+ //check(a == b);
+ //check(b == c);
+}
+
diff --git a/src/test/run-pass/fun-indirect-call.rs b/src/test/run-pass/fun-indirect-call.rs
new file mode 100644
index 00000000..10c2cf40
--- /dev/null
+++ b/src/test/run-pass/fun-indirect-call.rs
@@ -0,0 +1,11 @@
+// -*- rust -*-
+
+fn f() -> int {
+ ret 42;
+}
+
+fn main() {
+ let fn() -> int g = f;
+ let int i = g();
+ check(i == 42);
+}
diff --git a/src/test/run-pass/generic-derived-type.rs b/src/test/run-pass/generic-derived-type.rs
new file mode 100644
index 00000000..9ed493a2
--- /dev/null
+++ b/src/test/run-pass/generic-derived-type.rs
@@ -0,0 +1,17 @@
+fn g[X](X x) -> X {
+ ret x;
+}
+
+fn f[T](T t) -> tup(T,T) {
+ type pair = tup(T,T);
+ let pair x = tup(t,t);
+ ret g[pair](x);
+}
+
+fn main() {
+ auto b = f[int](10);
+ log b._0;
+ log b._1;
+ check (b._0 == 10);
+ check (b._1 == 10);
+}
diff --git a/src/test/run-pass/generic-drop-glue.rs b/src/test/run-pass/generic-drop-glue.rs
new file mode 100644
index 00000000..3b439b81
--- /dev/null
+++ b/src/test/run-pass/generic-drop-glue.rs
@@ -0,0 +1,9 @@
+fn f[T](T t) {
+ log "dropping";
+}
+
+fn main() {
+ type r = rec(@int x, @int y);
+ auto x = rec(x=@10, y=@12);
+ f[r](x);
+} \ No newline at end of file
diff --git a/src/test/run-pass/generic-exterior-box.rs b/src/test/run-pass/generic-exterior-box.rs
new file mode 100644
index 00000000..797b0f6f
--- /dev/null
+++ b/src/test/run-pass/generic-exterior-box.rs
@@ -0,0 +1,13 @@
+type tupbox[T] = tup(@T);
+type recbox[T] = rec(@T x);
+
+fn tuplift[T](T t) -> tupbox[T] { ret tup(@t); }
+fn reclift[T](T t) -> recbox[T] { ret rec(x=@t); }
+
+fn main() {
+ let int foo = 17;
+ let tupbox[int] tbfoo = tuplift[int](foo);
+ let recbox[int] rbfoo = reclift[int](foo);
+ check (tbfoo._0 == foo);
+ check (rbfoo.x == foo);
+}
diff --git a/src/test/run-pass/generic-fn-infer.rs b/src/test/run-pass/generic-fn-infer.rs
new file mode 100644
index 00000000..e24cf845
--- /dev/null
+++ b/src/test/run-pass/generic-fn-infer.rs
@@ -0,0 +1,13 @@
+// -*- rust -*-
+
+// Issue #45: infer type parameters in function applications
+
+fn id[T](T x) -> T {
+ ret x;
+}
+
+fn main() {
+ let int x = 42;
+ let int y = id(x);
+ check (x == y);
+}
diff --git a/src/test/run-pass/generic-fn.rs b/src/test/run-pass/generic-fn.rs
new file mode 100644
index 00000000..68e5fa5b
--- /dev/null
+++ b/src/test/run-pass/generic-fn.rs
@@ -0,0 +1,32 @@
+// -*- rust -*-
+
+fn id[T](T x) -> T {
+ ret x;
+}
+
+type triple = tup(int,int,int);
+
+fn main() {
+ auto x = 62;
+ auto y = 63;
+ auto a = 'a';
+ auto b = 'b';
+
+ let triple p = tup(65, 66, 67);
+ let triple q = tup(68, 69, 70);
+
+ y = id[int](x);
+ log y;
+ check (x == y);
+
+ b = id[char](a);
+ log b;
+ check (a == b);
+
+ q = id[triple](p);
+ x = p._2;
+ y = q._2;
+ log y;
+ check (x == y);
+
+}
diff --git a/src/test/run-pass/generic-obj-with-derived-type.rs b/src/test/run-pass/generic-obj-with-derived-type.rs
new file mode 100644
index 00000000..c902420e
--- /dev/null
+++ b/src/test/run-pass/generic-obj-with-derived-type.rs
@@ -0,0 +1,17 @@
+obj handle[T](T data) {
+ fn get() -> T {
+ ret data;
+ }
+}
+
+fn main() {
+ type rgb = tup(u8,u8,u8);
+ let handle[rgb] h = handle[rgb](tup(u8(1), u8(2), u8(3)));
+ log "constructed object";
+ log h.get()._0;
+ log h.get()._1;
+ log h.get()._2;
+ check (h.get()._0 == u8(1));
+ check (h.get()._1 == u8(2));
+ check (h.get()._2 == u8(3));
+}
diff --git a/src/test/run-pass/generic-obj.rs b/src/test/run-pass/generic-obj.rs
new file mode 100644
index 00000000..f67fef4f
--- /dev/null
+++ b/src/test/run-pass/generic-obj.rs
@@ -0,0 +1,24 @@
+obj buf[T](tup(T,T,T) data) {
+ fn get(int i) -> T {
+ if (i == 0) {
+ ret data._0;
+ } else {
+ if (i == 1) {
+ ret data._1;
+ } else {
+ ret data._2;
+ }
+ }
+ }
+}
+
+fn main() {
+ let buf[int] b = buf[int](tup(1,2,3));
+ log "constructed object";
+ log b.get(0);
+ log b.get(1);
+ log b.get(2);
+ check (b.get(0) == 1);
+ check (b.get(1) == 2);
+ check (b.get(2) == 3);
+}
diff --git a/src/test/run-pass/generic-recursive-tag.rs b/src/test/run-pass/generic-recursive-tag.rs
new file mode 100644
index 00000000..7cae581b
--- /dev/null
+++ b/src/test/run-pass/generic-recursive-tag.rs
@@ -0,0 +1,5 @@
+type list[T] = tag(cons(@T, @list[T]), nil());
+
+fn main() {
+ let list[int] a = cons[int](10, cons[int](12, cons[int](13, nil[int]())));
+} \ No newline at end of file
diff --git a/src/test/run-pass/generic-tag-alt.rs b/src/test/run-pass/generic-tag-alt.rs
new file mode 100644
index 00000000..1fcf2c38
--- /dev/null
+++ b/src/test/run-pass/generic-tag-alt.rs
@@ -0,0 +1,9 @@
+type foo[T] = tag(arm(T));
+
+fn altfoo[T](foo[T] f) {
+ alt (f) {
+ case (arm(x)) {}
+ }
+}
+
+fn main() {}
diff --git a/src/test/run-pass/generic-tag.rs b/src/test/run-pass/generic-tag.rs
new file mode 100644
index 00000000..9a98ead5
--- /dev/null
+++ b/src/test/run-pass/generic-tag.rs
@@ -0,0 +1,6 @@
+type option[T] = tag(some(@T), none());
+
+fn main() {
+ let option[int] a = some[int](10);
+ a = none[int]();
+} \ No newline at end of file
diff --git a/src/test/run-pass/generic-type-synonym.rs b/src/test/run-pass/generic-type-synonym.rs
new file mode 100644
index 00000000..4ddc8946
--- /dev/null
+++ b/src/test/run-pass/generic-type-synonym.rs
@@ -0,0 +1,4 @@
+type foo[T] = tup(T);
+type bar[T] = foo[T];
+fn takebar[T](bar[T] b) {}
+fn main() {} \ No newline at end of file
diff --git a/src/test/run-pass/generic-type.rs b/src/test/run-pass/generic-type.rs
new file mode 100644
index 00000000..6638cebf
--- /dev/null
+++ b/src/test/run-pass/generic-type.rs
@@ -0,0 +1,6 @@
+type pair[T] = tup(T,T);
+fn main() {
+ let pair[int] x = tup(10,12);
+ check (x._0 == 10);
+ check (x._1 == 12);
+}
diff --git a/src/test/run-pass/hello.rs b/src/test/run-pass/hello.rs
new file mode 100644
index 00000000..8535f74e
--- /dev/null
+++ b/src/test/run-pass/hello.rs
@@ -0,0 +1,6 @@
+// -*- rust -*-
+
+fn main() {
+ log "hello, world.";
+}
+
diff --git a/src/test/run-pass/i32-sub.rs b/src/test/run-pass/i32-sub.rs
new file mode 100644
index 00000000..99bd3936
--- /dev/null
+++ b/src/test/run-pass/i32-sub.rs
@@ -0,0 +1,8 @@
+// -*- rust -*-
+
+fn main() {
+ let i32 x = i32(-400);
+ x = i32(0) - x;
+ check(x == i32(400));
+}
+
diff --git a/src/test/run-pass/i8-incr.rs b/src/test/run-pass/i8-incr.rs
new file mode 100644
index 00000000..57029a13
--- /dev/null
+++ b/src/test/run-pass/i8-incr.rs
@@ -0,0 +1,9 @@
+// -*- rust -*-
+
+fn main() {
+ let i8 x = i8(-12);
+ let i8 y = i8(-12);
+ x = x + i8(1);
+ x = x - i8(1);
+ check(x == y);
+}
diff --git a/src/test/run-pass/import.rs b/src/test/run-pass/import.rs
new file mode 100644
index 00000000..76de0d18
--- /dev/null
+++ b/src/test/run-pass/import.rs
@@ -0,0 +1,14 @@
+mod foo {
+ fn x(int y) {
+ log y;
+ }
+}
+
+mod bar {
+ import foo.x;
+ import z = foo.x;
+ fn main() {
+ x(10);
+ z(10);
+ }
+}
diff --git a/src/test/run-pass/inner-module.rs b/src/test/run-pass/inner-module.rs
new file mode 100644
index 00000000..f5066b6e
--- /dev/null
+++ b/src/test/run-pass/inner-module.rs
@@ -0,0 +1,17 @@
+// -*- rust -*-
+
+mod inner {
+ mod inner2 {
+ fn hello() {
+ log "hello, modular world";
+ }
+ }
+ fn hello() {
+ inner2.hello();
+ }
+}
+
+fn main() {
+ inner.hello();
+ inner.inner2.hello();
+}
diff --git a/src/test/run-pass/int.rs b/src/test/run-pass/int.rs
new file mode 100644
index 00000000..39cd48f0
--- /dev/null
+++ b/src/test/run-pass/int.rs
@@ -0,0 +1,6 @@
+// -*- rust -*-
+
+
+fn main() {
+ let int x = 10;
+}
diff --git a/src/test/run-pass/large-records.rs b/src/test/run-pass/large-records.rs
new file mode 100644
index 00000000..0de2aa1b
--- /dev/null
+++ b/src/test/run-pass/large-records.rs
@@ -0,0 +1,14 @@
+// -*- rust -*-
+
+fn f() {
+ let rec(int a, int b, int c, int d,
+ int e, int f, int g, int h,
+ int i, int j, int k, int l) foo =
+ rec(a=0, b=0, c=0, d=0,
+ e=0, f=0, g=0, h=0,
+ i=0, j=0, k=0, l=0);
+}
+
+fn main() {
+ f();
+}
diff --git a/src/test/run-pass/lazy-and-or.rs b/src/test/run-pass/lazy-and-or.rs
new file mode 100644
index 00000000..81f09843
--- /dev/null
+++ b/src/test/run-pass/lazy-and-or.rs
@@ -0,0 +1,22 @@
+fn incr(mutable &int x) -> bool {
+ x += 1;
+ check (false);
+ ret false;
+}
+
+fn main() {
+
+ auto x = (1 == 2) || (3 == 3);
+ check (x);
+
+ let int y = 10;
+ log x || incr(y);
+ check (y == 10);
+
+ if (true && x) {
+ check (true);
+ } else {
+ check (false);
+ }
+
+} \ No newline at end of file
diff --git a/src/test/run-pass/lazychan.rs b/src/test/run-pass/lazychan.rs
new file mode 100644
index 00000000..9d560bd9
--- /dev/null
+++ b/src/test/run-pass/lazychan.rs
@@ -0,0 +1,23 @@
+// -*- rust -*-
+
+io fn main() {
+ let port[int] p = port();
+ auto c = chan(p);
+ let int y;
+
+ spawn child(c);
+ y <- p;
+ log "received 1";
+ log y;
+ check (y == 10);
+
+ spawn child(c);
+ y <- p;
+ log "received 2";
+ log y;
+ check (y == 10);
+}
+
+io fn child(chan[int] c) {
+ c <| 10;
+}
diff --git a/src/test/run-pass/linear-for-loop.rs b/src/test/run-pass/linear-for-loop.rs
new file mode 100644
index 00000000..4312aea8
--- /dev/null
+++ b/src/test/run-pass/linear-for-loop.rs
@@ -0,0 +1,38 @@
+fn main() {
+ auto x = vec(1,2,3);
+ auto y = 0;
+ for (int i in x) {
+ log i;
+ y += i;
+ }
+ log y;
+ check (y == 6);
+
+ auto s = "hello there";
+ let int i = 0;
+ for (u8 c in s) {
+ if (i == 0) {
+ check (c == u8('h'));
+ }
+ if (i == 1) {
+ check (c == u8('e'));
+ }
+ if (i == 2) {
+ check (c == u8('l'));
+ }
+ if (i == 3) {
+ check (c == u8('l'));
+ }
+ if (i == 4) {
+ check (c == u8('o'));
+ }
+ // ...
+ if (i == 12) {
+ check (c == u8(0));
+ }
+ i += 1;
+ log i;
+ log c;
+ }
+ check(i == 12);
+}
diff --git a/src/test/run-pass/list.rs b/src/test/run-pass/list.rs
new file mode 100644
index 00000000..38601f8f
--- /dev/null
+++ b/src/test/run-pass/list.rs
@@ -0,0 +1,7 @@
+// -*- rust -*-
+
+type list = tag(cons(int,@list), nil());
+
+fn main() {
+ cons(10, cons(11, cons(12, nil())));
+}
diff --git a/src/test/run-pass/many.rs b/src/test/run-pass/many.rs
new file mode 100644
index 00000000..3776d38e
--- /dev/null
+++ b/src/test/run-pass/many.rs
@@ -0,0 +1,19 @@
+// -*- rust -*-
+
+io fn sub(chan[int] parent, int id) {
+ if (id == 0) {
+ parent <| 0;
+ } else {
+ let port[int] p = port();
+ auto child = spawn sub(chan(p), id-1);
+ let int y <- p;
+ parent <| y + 1;
+ }
+}
+
+io fn main() {
+ let port[int] p = port();
+ auto child = spawn sub(chan(p), 500);
+ let int y <- p;
+ check (y == 500);
+}
diff --git a/src/test/run-pass/mlist-cycle.rs b/src/test/run-pass/mlist-cycle.rs
new file mode 100644
index 00000000..3875c5c2
--- /dev/null
+++ b/src/test/run-pass/mlist-cycle.rs
@@ -0,0 +1,10 @@
+// -*- rust -*-
+
+type pair = rec(int head, mutable @mlist tail);
+type mlist = tag(cons(@pair), nil());
+
+fn main() {
+ let @pair p = rec(head=10, tail=mutable nil());
+ let @mlist cycle = cons(p);
+ //p.tail = cycle;
+}
diff --git a/src/test/run-pass/mlist.rs b/src/test/run-pass/mlist.rs
new file mode 100644
index 00000000..ba71aa58
--- /dev/null
+++ b/src/test/run-pass/mlist.rs
@@ -0,0 +1,7 @@
+// -*- rust -*-
+
+type mlist = tag(cons(int,mutable @mlist), nil());
+
+fn main() {
+ cons(10, cons(11, cons(12, nil())));
+}
diff --git a/src/test/run-pass/mutable-vec-drop.rs b/src/test/run-pass/mutable-vec-drop.rs
new file mode 100644
index 00000000..df3b55ee
--- /dev/null
+++ b/src/test/run-pass/mutable-vec-drop.rs
@@ -0,0 +1,4 @@
+fn main() {
+ // This just tests whether the vec leaks its members.
+ let vec[mutable @tup(int,int)] pvec = vec(tup(1,2),tup(3,4),tup(5,6));
+}
diff --git a/src/test/run-pass/mutual-recursion-group.rs b/src/test/run-pass/mutual-recursion-group.rs
new file mode 100644
index 00000000..850858a3
--- /dev/null
+++ b/src/test/run-pass/mutual-recursion-group.rs
@@ -0,0 +1,11 @@
+// -*- rust -*-
+
+type colour = tag(red(), green(), blue());
+type tree = tag(children(@list), leaf(colour));
+type list = tag(cons(@tree, @list), nil());
+
+type small_list = tag(kons(int,@small_list), neel());
+
+fn main() {
+}
+
diff --git a/src/test/run-pass/native-mod-src/inner.rs b/src/test/run-pass/native-mod-src/inner.rs
new file mode 100644
index 00000000..546b2291
--- /dev/null
+++ b/src/test/run-pass/native-mod-src/inner.rs
@@ -0,0 +1,12 @@
+// -*- rust -*-
+
+unsafe fn main() {
+ auto f = "Makefile";
+ auto s = rustrt.str_buf(f);
+ auto buf = libc.malloc(1024);
+ auto fd = libc.open(s, 0, 0);
+ libc.read(fd, buf, 1024);
+ libc.write(1, buf, 1024);
+ libc.close(fd);
+ libc.free(buf);
+}
diff --git a/src/test/run-pass/native-mod.rc b/src/test/run-pass/native-mod.rc
new file mode 100644
index 00000000..4fcf4499
--- /dev/null
+++ b/src/test/run-pass/native-mod.rc
@@ -0,0 +1,16 @@
+// -*- rust -*-
+
+native mod libc = target_libc {
+ fn open(int name, int flags, int mode) -> int;
+ fn close(int fd) -> int;
+ fn read(int fd, int buf, int count) -> int;
+ fn write(int fd, int buf, int count) -> int;
+ fn malloc(int sz) -> int;
+ fn free(int p) -> ();
+}
+
+native "rust" mod rustrt {
+ fn str_buf(str s) -> int;
+}
+
+mod inner = "native-mod-src/inner.rs";
diff --git a/src/test/run-pass/native-opaque-type.rs b/src/test/run-pass/native-opaque-type.rs
new file mode 100644
index 00000000..19c2c074
--- /dev/null
+++ b/src/test/run-pass/native-opaque-type.rs
@@ -0,0 +1,7 @@
+native mod libc {
+ type file_handle;
+}
+
+fn main() {
+ check (true);
+}
diff --git a/src/test/run-pass/native-src/native.rs b/src/test/run-pass/native-src/native.rs
new file mode 100644
index 00000000..22658095
--- /dev/null
+++ b/src/test/run-pass/native-src/native.rs
@@ -0,0 +1,7 @@
+// -*- rust -*-
+
+unsafe fn main() {
+ libc.puts(rustrt.str_buf("hello, native world 1"));
+ libc.puts(rustrt.str_buf("hello, native world 2"));
+ libc.puts(rustrt.str_buf("hello, native world 3"));
+}
diff --git a/src/test/run-pass/native.rc b/src/test/run-pass/native.rc
new file mode 100644
index 00000000..c0f019c5
--- /dev/null
+++ b/src/test/run-pass/native.rc
@@ -0,0 +1,12 @@
+// -*- rust -*-
+
+native "rust" mod rustrt {
+ fn str_buf(str s) -> int;
+}
+
+
+native mod libc = target_libc {
+ fn puts(int s) -> ();
+}
+
+mod user = "native-src/native.rs";
diff --git a/src/test/run-pass/obj-as.rs b/src/test/run-pass/obj-as.rs
new file mode 100644
index 00000000..62eda294
--- /dev/null
+++ b/src/test/run-pass/obj-as.rs
@@ -0,0 +1,21 @@
+
+obj big() {
+ fn one() -> int { ret 1; }
+ fn two() -> int { ret 2; }
+ fn three() -> int { ret 3; }
+}
+
+type small = obj {
+ fn one() -> int;
+ };
+
+fn main() {
+
+ let big b = big();
+ check (b.one() == 1);
+ check (b.two() == 2);
+ check (b.three() == 3);
+
+ let small s = b as small;
+ check (s.one() == 1);
+} \ No newline at end of file
diff --git a/src/test/run-pass/obj-drop.rs b/src/test/run-pass/obj-drop.rs
new file mode 100644
index 00000000..6d4ca3d4
--- /dev/null
+++ b/src/test/run-pass/obj-drop.rs
@@ -0,0 +1,6 @@
+fn main() {
+ obj handle(@int i) {
+ }
+ // This just tests whether the obj leaks its exterior state members.
+ auto ob = handle(0xf00f00);
+} \ No newline at end of file
diff --git a/src/test/run-pass/obj-dtor.rs b/src/test/run-pass/obj-dtor.rs
new file mode 100644
index 00000000..8b79047b
--- /dev/null
+++ b/src/test/run-pass/obj-dtor.rs
@@ -0,0 +1,33 @@
+obj worker(chan[int] c) {
+ drop {
+ log "in dtor";
+ c <| 10;
+ }
+}
+
+io fn do_work(chan[int] c) {
+ log "in child task";
+ {
+ let worker w = worker(c);
+ log "constructed worker";
+ }
+ log "destructed worker";
+ while(true) {
+ // Deadlock-condition not handled properly yet, need to avoid
+ // exiting the child early.
+ c <| 11;
+ yield;
+ }
+}
+
+io fn main() {
+ let port[int] p = port();
+ log "spawning worker";
+ auto w = spawn do_work(chan(p));
+ let int i;
+ log "parent waiting for shutdown";
+ i <- p;
+ log "received int";
+ check (i == 10);
+ log "int is OK, child-dtor ran as expected";
+} \ No newline at end of file
diff --git a/src/test/run-pass/obj-with-vec.rs b/src/test/run-pass/obj-with-vec.rs
new file mode 100644
index 00000000..169889a3
--- /dev/null
+++ b/src/test/run-pass/obj-with-vec.rs
@@ -0,0 +1,11 @@
+fn main() {
+
+ obj buf(vec[u8] data) {
+ fn get(int i) -> u8 {
+ ret data.(i);
+ }
+ }
+ auto b = buf(vec(u8(1), u8(2), u8(3)));
+ log b.get(1);
+ check (b.get(1) == u8(2));
+} \ No newline at end of file
diff --git a/src/test/run-pass/opeq.rs b/src/test/run-pass/opeq.rs
new file mode 100644
index 00000000..d99ebb04
--- /dev/null
+++ b/src/test/run-pass/opeq.rs
@@ -0,0 +1,22 @@
+// -*- rust -*-
+
+fn main() {
+ let int x = 1;
+
+ x *= 2;
+ log x;
+ check (x == 2);
+
+ x += 3;
+ log x;
+ check (x == 5);
+
+ x *= x;
+ log x;
+ check (x == 25);
+
+ x /= 5;
+ log x;
+ check (x == 5);
+}
+
diff --git a/src/test/run-pass/pred.rs b/src/test/run-pass/pred.rs
new file mode 100644
index 00000000..b3338f38
--- /dev/null
+++ b/src/test/run-pass/pred.rs
@@ -0,0 +1,18 @@
+// -*- rust -*-
+
+fn f(int a, int b) : lt(a,b) {
+}
+
+fn lt(int a, int b) -> bool {
+ ret a < b;
+}
+
+fn main() {
+ let int a = 10;
+ let int b = 23;
+ let int c = 77;
+ check lt(a,b);
+ check lt(a,c);
+ f(a,b);
+ f(a,c);
+}
diff --git a/src/test/run-pass/preempt.rs b/src/test/run-pass/preempt.rs
new file mode 100644
index 00000000..00fc29ca
--- /dev/null
+++ b/src/test/run-pass/preempt.rs
@@ -0,0 +1,26 @@
+// This checks that preemption works.
+
+io fn starve_main(chan[int] alive) {
+ log "signalling main";
+ alive <| 1;
+ log "starving main";
+ let int i = 0;
+ while (true) {
+ i += 1;
+ }
+}
+
+io fn main() {
+ let port[int] alive = port();
+ log "main started";
+ let task s = spawn starve_main(chan(alive));
+ let int i;
+ log "main waiting for alive signal";
+ i <- alive;
+ log "main got alive signal";
+ while (i < 1000) {
+ log "main iterated";
+ i += 1;
+ }
+ log "main completed";
+} \ No newline at end of file
diff --git a/src/test/run-pass/readalias.rs b/src/test/run-pass/readalias.rs
new file mode 100644
index 00000000..15fa142a
--- /dev/null
+++ b/src/test/run-pass/readalias.rs
@@ -0,0 +1,12 @@
+// -*- rust -*-
+
+type point = rec(int x, int y, int z);
+
+fn f(&point p) {
+ check (p.z == 12);
+}
+
+fn main() {
+ let point x = rec(x=10, y=11, z=12);
+ f(x);
+}
diff --git a/src/test/run-pass/rec-auto.rs b/src/test/run-pass/rec-auto.rs
new file mode 100644
index 00000000..01390acd
--- /dev/null
+++ b/src/test/run-pass/rec-auto.rs
@@ -0,0 +1,9 @@
+// -*- rust -*-
+
+// Issue #50.
+
+fn main() {
+ auto x = rec(foo = "hello", bar = "world");
+ log x.foo;
+ log x.bar;
+}
diff --git a/src/test/run-pass/rec-extend.rs b/src/test/run-pass/rec-extend.rs
new file mode 100644
index 00000000..db81278b
--- /dev/null
+++ b/src/test/run-pass/rec-extend.rs
@@ -0,0 +1,19 @@
+// -*- rust -*-
+
+type point = rec(int x, int y);
+
+fn main() {
+ let point origin = rec(x=0, y=0);
+
+ let point right = rec(x=origin.x + 10 with origin);
+ let point up = rec(y=origin.y + 10 with origin);
+
+ check(origin.x == 0);
+ check(origin.y == 0);
+
+ check(right.x == 10);
+ check(right.y == 0);
+
+ check(up.x == 0);
+ check(up.y == 10);
+}
diff --git a/src/test/run-pass/rec-tup.rs b/src/test/run-pass/rec-tup.rs
new file mode 100644
index 00000000..e25439a9
--- /dev/null
+++ b/src/test/run-pass/rec-tup.rs
@@ -0,0 +1,25 @@
+// -*- rust -*-
+
+type point = rec(int x, int y);
+type rect = tup(point, point);
+
+fn f(rect r, int x1, int y1, int x2, int y2) {
+ check (r._0.x == x1);
+ check (r._0.y == y1);
+ check (r._1.x == x2);
+ check (r._1.y == y2);
+}
+
+fn main() {
+ let rect r = tup( rec(x=10, y=20),
+ rec(x=11, y=22) );
+ check (r._0.x == 10);
+ check (r._0.y == 20);
+ check (r._1.x == 11);
+ check (r._1.y == 22);
+ let rect r2 = r;
+ let int x = r2._0.x;
+ check (x == 10);
+ f(r, 10, 20, 11, 22);
+ f(r2, 10, 20, 11, 22);
+}
diff --git a/src/test/run-pass/rec.rs b/src/test/run-pass/rec.rs
new file mode 100644
index 00000000..0f6b7d79
--- /dev/null
+++ b/src/test/run-pass/rec.rs
@@ -0,0 +1,23 @@
+// -*- rust -*-
+
+type rect = rec(int x, int y, int w, int h);
+
+fn f(rect r, int x, int y, int w, int h) {
+ check (r.x == x);
+ check (r.y == y);
+ check (r.w == w);
+ check (r.h == h);
+}
+
+fn main() {
+ let rect r = rec(x=10, y=20, w=100, h=200);
+ check (r.x == 10);
+ check (r.y == 20);
+ check (r.w == 100);
+ check (r.h == 200);
+ let rect r2 = r;
+ let int x = r2.x;
+ check (x == 10);
+ f(r, 10, 20, 100, 200);
+ f(r2, 10, 20, 100, 200);
+}
diff --git a/src/test/run-pass/return-nil.rs b/src/test/run-pass/return-nil.rs
new file mode 100644
index 00000000..c3c8a085
--- /dev/null
+++ b/src/test/run-pass/return-nil.rs
@@ -0,0 +1,8 @@
+fn f() {
+ let () x = ();
+ ret x;
+}
+
+fn main() {
+ auto x = f();
+} \ No newline at end of file
diff --git a/src/test/run-pass/simple-obj.rs b/src/test/run-pass/simple-obj.rs
new file mode 100644
index 00000000..b465a7d3
--- /dev/null
+++ b/src/test/run-pass/simple-obj.rs
@@ -0,0 +1,12 @@
+// -*- rust -*-
+
+obj x() {
+ fn hello() {
+ log "hello, object world";
+ }
+}
+
+fn main() {
+ auto mx = x();
+ mx.hello();
+}
diff --git a/src/test/run-pass/spawn-fn.rs b/src/test/run-pass/spawn-fn.rs
new file mode 100644
index 00000000..894a8321
--- /dev/null
+++ b/src/test/run-pass/spawn-fn.rs
@@ -0,0 +1,18 @@
+// -*- rust -*-
+
+fn x(str s, int n) {
+ log s;
+ log n;
+}
+
+fn main() {
+ spawn x("hello from first spawned fn", 65);
+ spawn x("hello from second spawned fn", 66);
+ spawn x("hello from third spawned fn", 67);
+ let int i = 30;
+ while (i > 0) {
+ i = i - 1;
+ log "parent sleeping";
+ yield;
+ }
+}
diff --git a/src/test/run-pass/spawn.rs b/src/test/run-pass/spawn.rs
new file mode 100644
index 00000000..765d4c9e
--- /dev/null
+++ b/src/test/run-pass/spawn.rs
@@ -0,0 +1,10 @@
+// -*- rust -*-
+
+fn main() {
+ spawn child(10);
+}
+
+fn child(int i) {
+ log i;
+}
+
diff --git a/src/test/run-pass/stateful-obj.rs b/src/test/run-pass/stateful-obj.rs
new file mode 100644
index 00000000..c1d96cc3
--- /dev/null
+++ b/src/test/run-pass/stateful-obj.rs
@@ -0,0 +1,23 @@
+// -*- rust -*-
+
+obj counter(mutable int x) {
+ fn hello() -> int {
+ ret 12345;
+ }
+ fn incr() {
+ x = x + 1;
+ }
+ fn get() -> int {
+ ret x;
+ }
+}
+
+fn main() {
+ auto y = counter(0);
+ check (y.hello() == 12345);
+ log y.get();
+ y.incr();
+ y.incr();
+ log y.get();
+ check (y.get() == 2);
+}
diff --git a/src/test/run-pass/str-append.rs b/src/test/run-pass/str-append.rs
new file mode 100644
index 00000000..92d8ab89
--- /dev/null
+++ b/src/test/run-pass/str-append.rs
@@ -0,0 +1,8 @@
+// -*- rust -*-
+
+fn main() {
+ let str s = "hello";
+ s += "world";
+ log s;
+ check(s.(9) == u8('d'));
+}
diff --git a/src/test/run-pass/str-concat.rs b/src/test/run-pass/str-concat.rs
new file mode 100644
index 00000000..874a379b
--- /dev/null
+++ b/src/test/run-pass/str-concat.rs
@@ -0,0 +1,9 @@
+// -*- rust -*-
+
+fn main() {
+ let str a = "hello";
+ let str b = "world";
+ let str s = a + b;
+ log s;
+ check(s.(9) == u8('d'));
+}
diff --git a/src/test/run-pass/str-idx.rs b/src/test/run-pass/str-idx.rs
new file mode 100644
index 00000000..2f39dea9
--- /dev/null
+++ b/src/test/run-pass/str-idx.rs
@@ -0,0 +1,7 @@
+
+fn main() {
+ auto s = "hello";
+ let u8 c = s.(4);
+ log c;
+ check (c == u8(0x6f));
+}
diff --git a/src/test/run-pass/syntax-extension.rs b/src/test/run-pass/syntax-extension.rs
new file mode 100644
index 00000000..35f0f2d8
--- /dev/null
+++ b/src/test/run-pass/syntax-extension.rs
@@ -0,0 +1,4 @@
+fn main() {
+ auto s = #shell { uname -a && hg identify };
+ log s;
+}
diff --git a/src/test/run-pass/tag.rs b/src/test/run-pass/tag.rs
new file mode 100644
index 00000000..0d345b2d
--- /dev/null
+++ b/src/test/run-pass/tag.rs
@@ -0,0 +1,14 @@
+// -*- rust -*-
+
+type colour = tag(red(int,int), green());
+
+fn f() {
+ auto x = red(1,2);
+ auto y = green();
+ // FIXME: needs structural equality test working.
+ // check (x != y);
+}
+
+fn main() {
+ f();
+}
diff --git a/src/test/run-pass/tail-cps.rs b/src/test/run-pass/tail-cps.rs
new file mode 100644
index 00000000..795a105a
--- /dev/null
+++ b/src/test/run-pass/tail-cps.rs
@@ -0,0 +1,34 @@
+// -*- rust -*-
+
+fn checktrue(bool res) -> bool {
+ check(res);
+ ret true;
+}
+
+fn main() {
+ auto k = checktrue;
+ evenk(42, k);
+ oddk(45, k);
+}
+
+fn evenk(int n, fn(bool) -> bool k) -> bool {
+ log "evenk";
+ log n;
+ if (n == 0) {
+ be k(true);
+ }
+ else {
+ be oddk(n - 1, k);
+ }
+}
+
+fn oddk(int n, fn(bool) -> bool k) -> bool {
+ log "oddk";
+ log n;
+ if (n == 0) {
+ be k(false);
+ }
+ else {
+ be evenk(n - 1, k);
+ }
+}
diff --git a/src/test/run-pass/tail-direct.rs b/src/test/run-pass/tail-direct.rs
new file mode 100644
index 00000000..345a322e
--- /dev/null
+++ b/src/test/run-pass/tail-direct.rs
@@ -0,0 +1,24 @@
+// -*- rust -*-
+
+fn main() {
+ check(even(42));
+ check(odd(45));
+}
+
+fn even(int n) -> bool {
+ if (n == 0) {
+ ret true;
+ }
+ else {
+ be odd(n - 1);
+ }
+}
+
+fn odd(int n) -> bool {
+ if (n == 0) {
+ ret false;
+ }
+ else {
+ be even(n - 1);
+ }
+}
diff --git a/src/test/run-pass/task-comm.rs b/src/test/run-pass/task-comm.rs
new file mode 100644
index 00000000..4a21b4e4
--- /dev/null
+++ b/src/test/run-pass/task-comm.rs
@@ -0,0 +1,127 @@
+
+
+io fn main() -> () {
+ test00(true);
+ // test01();
+ // test02();
+ // test03();
+ // test04();
+}
+
+io fn test00_start(chan[int] ch, int message, int count) {
+ log "Starting test00_start";
+ let int i = 0;
+ while (i < count) {
+ ch <| message;
+ i = i + 1;
+ }
+ log "Ending test00_start";
+}
+
+io fn test00(bool is_multithreaded) {
+ let int number_of_tasks = 4;
+ let int number_of_messages = 64;
+ log "Creating tasks";
+
+ let port[int] po = port();
+ let chan[int] ch = chan(po);
+
+ let int i = 0;
+
+ 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));
+ }
+ }
+
+ let int sum = 0;
+ for (task t in tasks) {
+ i = 0;
+ while (i < number_of_messages) {
+ let int value <- po;
+ sum += value;
+ i = i + 1;
+ }
+ }
+
+ for (task t in tasks) {
+ join t;
+ }
+
+ log "Completed: Final number is: ";
+ check (sum == number_of_messages *
+ (number_of_tasks * number_of_tasks + number_of_tasks) / 2);
+}
+
+io fn test01() {
+ let port[int] p = port();
+ log "Reading from a port that is never written to.";
+ let int value <- p;
+ log value;
+}
+
+io fn test02() {
+ let port[int] p = port();
+ let chan[int] c = chan(p);
+ log "Writing to a local task channel.";
+ c <| 42;
+ log "Reading from a local task port.";
+ let int value <- p;
+ log value;
+}
+
+obj vector(mutable int x, int y) {
+ fn length() -> int {
+ x = x + 2;
+ ret x + y;
+ }
+}
+
+fn test03() {
+ log "Creating object ...";
+ let mutable vector v = vector(1, 2);
+ log "created object ...";
+ let mutable vector t = v;
+ log v.length();
+}
+
+fn test04_start() {
+ log "Started Task";
+ let int i = 1024 * 1024 * 64;
+ while (i > 0) {
+ i = i - 1;
+ }
+ log "Finished Task";
+}
+
+fn test04() {
+ log "Spawning lots of tasks.";
+ let int i = 64;
+ while (i > 0) {
+ i = i - 1;
+ spawn thread test04_start();
+ }
+ log "Finishing up.";
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/test/run-pass/threads.rs b/src/test/run-pass/threads.rs
new file mode 100644
index 00000000..b0fee65f
--- /dev/null
+++ b/src/test/run-pass/threads.rs
@@ -0,0 +1,16 @@
+// -*- rust -*-
+
+fn main() {
+ let port[int] p = port();
+ let int i = 10;
+ while (i > 0) {
+ spawn thread child(i);
+ i = i - 1;
+ }
+ log "main thread exiting";
+}
+
+fn child(int x) {
+ log x;
+}
+
diff --git a/src/test/run-pass/tup.rs b/src/test/run-pass/tup.rs
new file mode 100644
index 00000000..23406646
--- /dev/null
+++ b/src/test/run-pass/tup.rs
@@ -0,0 +1,19 @@
+// -*- rust -*-
+
+type point = tup(int, int);
+
+fn f(point p, int x, int y) {
+ check (p._0 == x);
+ check (p._1 == y);
+}
+
+fn main() {
+ let point p = tup(10, 20);
+ check (p._0 == 10);
+ check (p._1 == 20);
+ let point p2 = p;
+ let int x = p2._0;
+ check (x == 10);
+ f(p, 10, 20);
+ f(p2, 10, 20);
+}
diff --git a/src/test/run-pass/type-sizes.rs b/src/test/run-pass/type-sizes.rs
new file mode 100644
index 00000000..40f9dbad
--- /dev/null
+++ b/src/test/run-pass/type-sizes.rs
@@ -0,0 +1,20 @@
+
+import size_of = std.sys.rustrt.size_of;
+
+use std;
+
+fn main() {
+ check (size_of[u8]() == uint(1));
+ check (size_of[u32]() == uint(4));
+ check (size_of[char]() == uint(4));
+ check (size_of[i8]() == uint(1));
+ check (size_of[i32]() == uint(4));
+ check (size_of[tup(u8,i8)]() == uint(2));
+ check (size_of[tup(u8,i8,u8)]() == uint(3));
+ // Alignment causes padding before the char and the u32.
+ check (size_of[tup(u8,i8,tup(char,u8),u32)]() == uint(16));
+ check (size_of[int]() == size_of[uint]());
+ check (size_of[tup(int,())]() == size_of[int]());
+ check (size_of[tup(int,(),())]() == size_of[int]());
+ check (size_of[int]() == size_of[rec(int x)]());
+}
diff --git a/src/test/run-pass/u32-decr.rs b/src/test/run-pass/u32-decr.rs
new file mode 100644
index 00000000..15d5bcaa
--- /dev/null
+++ b/src/test/run-pass/u32-decr.rs
@@ -0,0 +1,8 @@
+// -*- rust -*-
+
+fn main() {
+ let u32 word = u32(200000);
+ word = word - u32(1);
+ check(word == u32(199999));
+}
+
diff --git a/src/test/run-pass/u8-incr-decr.rs b/src/test/run-pass/u8-incr-decr.rs
new file mode 100644
index 00000000..e8c29d4f
--- /dev/null
+++ b/src/test/run-pass/u8-incr-decr.rs
@@ -0,0 +1,12 @@
+// -*- rust -*-
+
+// These constants were chosen because they aren't used anywhere
+// in the rest of the generated code so they're easily grep-able.
+
+fn main() {
+ let u8 x = u8(19); // 0x13
+ let u8 y = u8(35); // 0x23
+ x = x + u8(7); // 0x7
+ y = y - u8(9); // 0x9
+ check(x == y);
+}
diff --git a/src/test/run-pass/u8-incr.rs b/src/test/run-pass/u8-incr.rs
new file mode 100644
index 00000000..c3c1aef4
--- /dev/null
+++ b/src/test/run-pass/u8-incr.rs
@@ -0,0 +1,12 @@
+// -*- rust -*-
+
+fn main() {
+ let u8 x = u8(12);
+ let u8 y = u8(12);
+ x = x + u8(1);
+ x = x - u8(1);
+ check(x == y);
+ //x = u8(14);
+ //x = x + u8(1);
+}
+
diff --git a/src/test/run-pass/uint.rs b/src/test/run-pass/uint.rs
new file mode 100644
index 00000000..924ff469
--- /dev/null
+++ b/src/test/run-pass/uint.rs
@@ -0,0 +1,6 @@
+// -*- rust -*-
+
+
+fn main() {
+ let uint x = uint(10);
+}
diff --git a/src/test/run-pass/unit.rs b/src/test/run-pass/unit.rs
new file mode 100644
index 00000000..ce24eabe
--- /dev/null
+++ b/src/test/run-pass/unit.rs
@@ -0,0 +1,13 @@
+// -*- rust -*-
+
+fn f(() u) -> () {
+ ret u;
+}
+
+fn main() -> () {
+ let () u1 = ();
+ let () u2 = f(u1);
+ u2 = ();
+ ret ();
+}
+
diff --git a/src/test/run-pass/user.rs b/src/test/run-pass/user.rs
new file mode 100644
index 00000000..82d3234a
--- /dev/null
+++ b/src/test/run-pass/user.rs
@@ -0,0 +1,14 @@
+// -*- rust -*-
+
+use std (name = "std",
+ url = "http://rust-lang.org/src/std",
+ uuid = _, ver = _);
+
+fn main() {
+ auto s = std._str.alloc(10);
+ s += "hello ";
+ log s;
+ s += "there";
+ log s;
+ auto z = std._vec.alloc[int](10);
+}
diff --git a/src/test/run-pass/utf8.rs b/src/test/run-pass/utf8.rs
new file mode 100644
index 00000000..fd70423a
--- /dev/null
+++ b/src/test/run-pass/utf8.rs
@@ -0,0 +1,48 @@
+fn main() {
+ let char yen = '¥'; // 0xa5
+ let char c_cedilla = 'ç'; // 0xe7
+ let char thorn = 'þ'; // 0xfe
+ let char y_diaeresis = 'ÿ'; // 0xff
+ let char pi = 'Π'; // 0x3a0
+
+ check (int(yen) == 0xa5);
+ check (int(c_cedilla) == 0xe7);
+ check (int(thorn) == 0xfe);
+ check (int(y_diaeresis) == 0xff);
+ check (int(pi) == 0x3a0);
+
+ check (int(pi) == int('\u03a0'));
+ check (int('\x0a') == int('\n'));
+
+ let str bhutan = "འབྲུག་ཡུལ།";
+ let str japan = "日本";
+ let str uzbekistan = "Ўзбекистон";
+ let str austria = "Österreich";
+
+ let str bhutan_e =
+ "\u0f60\u0f56\u0fb2\u0f74\u0f42\u0f0b\u0f61\u0f74\u0f63\u0f0d";
+ let str japan_e = "\u65e5\u672c";
+ let str uzbekistan_e =
+ "\u040e\u0437\u0431\u0435\u043a\u0438\u0441\u0442\u043e\u043d";
+ let str austria_e = "\u00d6sterreich";
+
+ let char oo = 'Ö';
+ check (int(oo) == 0xd6);
+
+ fn check_str_eq(str a, str b) {
+ let int i = 0;
+ for (u8 ab in a) {
+ log i;
+ log ab;
+ let u8 bb = b.(i);
+ log bb;
+ check(ab == bb);
+ i += 1;
+ }
+ }
+
+ check_str_eq(bhutan, bhutan_e);
+ check_str_eq(japan, japan_e);
+ check_str_eq(uzbekistan, uzbekistan_e);
+ check_str_eq(austria, austria_e);
+} \ No newline at end of file
diff --git a/src/test/run-pass/vec-append.rs b/src/test/run-pass/vec-append.rs
new file mode 100644
index 00000000..4324ee25
--- /dev/null
+++ b/src/test/run-pass/vec-append.rs
@@ -0,0 +1,10 @@
+// -*- rust -*-
+
+fn main() {
+ let vec[int] v = vec(1,2,3,4,5);
+ v += vec(6,7,8,9,0);
+ log v.(9);
+ check(v.(0) == 1);
+ check(v.(7) == 8);
+ check(v.(9) == 0);
+}
diff --git a/src/test/run-pass/vec-concat.rs b/src/test/run-pass/vec-concat.rs
new file mode 100644
index 00000000..b6c52c3e
--- /dev/null
+++ b/src/test/run-pass/vec-concat.rs
@@ -0,0 +1,11 @@
+// -*- rust -*-
+
+fn main() {
+ let vec[int] a = vec(1,2,3,4,5);
+ let vec[int] b = vec(6,7,8,9,0);
+ let vec[int] v = a + b;
+ log v.(9);
+ check(v.(0) == 1);
+ check(v.(7) == 8);
+ check(v.(9) == 0);
+}
diff --git a/src/test/run-pass/vec-drop.rs b/src/test/run-pass/vec-drop.rs
new file mode 100644
index 00000000..267c7a78
--- /dev/null
+++ b/src/test/run-pass/vec-drop.rs
@@ -0,0 +1,4 @@
+fn main() {
+ // This just tests whether the vec leaks its members.
+ let vec[@tup(int,int)] pvec = vec(tup(1,2),tup(3,4),tup(5,6));
+}
diff --git a/src/test/run-pass/vec-slice.rs b/src/test/run-pass/vec-slice.rs
new file mode 100644
index 00000000..332eff34
--- /dev/null
+++ b/src/test/run-pass/vec-slice.rs
@@ -0,0 +1,6 @@
+fn main() {
+ let vec[int] v = vec(1,2,3,4,5);
+ auto v2 = v.(1,2);
+ check (v2.(0) == 2);
+ check (v2.(1) == 3);
+} \ No newline at end of file
diff --git a/src/test/run-pass/vec.rs b/src/test/run-pass/vec.rs
new file mode 100644
index 00000000..67a41eab
--- /dev/null
+++ b/src/test/run-pass/vec.rs
@@ -0,0 +1,13 @@
+// -*- rust -*-
+
+fn main() {
+ let vec[int] v = vec(10, 20);
+ check (v.(0) == 10);
+ check (v.(1) == 20);
+ let int x = 0;
+ check (v.(x) == 10);
+ check (v.(x + 1) == 20);
+ x = x + 1;
+ check (v.(x) == 20);
+ check (v.(x-1) == 10);
+}
diff --git a/src/test/run-pass/writealias.rs b/src/test/run-pass/writealias.rs
new file mode 100644
index 00000000..96b2a9d7
--- /dev/null
+++ b/src/test/run-pass/writealias.rs
@@ -0,0 +1,13 @@
+// -*- rust -*-
+
+type point = rec(int x, int y, mutable int z);
+
+fn f(mutable &point p) {
+ p.z = 13;
+}
+
+fn main() {
+ let point x = rec(x=10, y=11, z=mutable 12);
+ f(x);
+ check (x.z == 13);
+}
diff --git a/src/test/run-pass/yield.rs b/src/test/run-pass/yield.rs
new file mode 100644
index 00000000..d2ae592a
--- /dev/null
+++ b/src/test/run-pass/yield.rs
@@ -0,0 +1,20 @@
+// -*- rust -*-
+
+fn main() {
+ auto other = spawn child();
+ log "1";
+ yield;
+ log "2";
+ yield;
+ log "3";
+ join other;
+}
+
+fn child() {
+ log "4";
+ yield;
+ log "5";
+ yield;
+ log "6";
+}
+
diff --git a/src/test/run-pass/yield2.rs b/src/test/run-pass/yield2.rs
new file mode 100644
index 00000000..11285822
--- /dev/null
+++ b/src/test/run-pass/yield2.rs
@@ -0,0 +1,10 @@
+// -*- rust -*-
+
+fn main() {
+ let int i = 0;
+ while (i < 100) {
+ i = i + 1;
+ log i;
+ yield;
+ }
+}