about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorGraydon Hoare <graydon@mozilla.com>2010-06-23 21:03:09 -0700
committerGraydon Hoare <graydon@mozilla.com>2010-06-23 21:03:09 -0700
commitd6b7c96c3eb29b9244ece0c046d3f372ff432d04 (patch)
treeb425187e232966063ffc2f0d14c04a55d8f004ef /src
parentc01efc669f09508b55eced32d3c88702578a7c3e (diff)
downloadrust-d6b7c96c3eb29b9244ece0c046d3f372ff432d04.tar.gz
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 95d530dfa81..5d4e6aa0fd3 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 00000000000..c51709d0556
--- /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 00000000000..fd9ca750e91
--- /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 00000000000..10b2142aad6
--- /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 00000000000..56905b2a4d4
--- /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 00000000000..e095e627b71
--- /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 00000000000..7fccdfd3548
--- /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 00000000000..d360ddf5a38
--- /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 00000000000..db70b21d0c7
--- /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 00000000000..01b7e29970d
--- /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 00000000000..e0391c65937
--- /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 00000000000..ef5c1c8653b
--- /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 00000000000..c5199a82681
--- /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 00000000000..80253f44050
--- /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 00000000000..4fc7448054d
--- /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 00000000000..bf7a11ff4e9
--- /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 00000000000..6dffdb96d02
--- /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 00000000000..75f86a58947
--- /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 00000000000..fb4d58c567c
--- /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 00000000000..3dda93ac257
--- /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 00000000000..49eeeb5b648
--- /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 00000000000..636e1ac2179
--- /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 00000000000..fd5d92776a6
--- /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 00000000000..56448b07902
--- /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 00000000000..2b229fde12e
--- /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 00000000000..64ea3d37a1e
--- /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 00000000000..7f985d25927
--- /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 00000000000..7009fe10618
--- /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 00000000000..47e5616628f
--- /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 00000000000..9423d4ee7f3
--- /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 00000000000..515cfa21ed5
--- /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 00000000000..6c4567fdae7
--- /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 00000000000..c23c4afd24c
--- /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 00000000000..8f034aee3a3
--- /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 00000000000..b5000ff38ce
--- /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 00000000000..bca15136389
--- /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 00000000000..c430e034ca9
--- /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 00000000000..2d4dd94aa7a
--- /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 00000000000..4671d0f4d0a
--- /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 00000000000..3486bb16421
--- /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 00000000000..3114bd66144
--- /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 00000000000..f33a6ea1649
--- /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 00000000000..35ebba1f6c1
--- /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 00000000000..e69de29bb2d
--- /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 00000000000..e69de29bb2d
--- /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 00000000000..3bf3bbcc9bc
--- /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 00000000000..eff967bf6ce
--- /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 00000000000..f829f2ad234
--- /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 00000000000..1bb6cb45b1e
--- /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 00000000000..1f01c3b3a01
--- /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 00000000000..ac27f294b29
--- /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 00000000000..e1f671e752a
--- /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 00000000000..c938e6fb942
--- /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 00000000000..a775a97ab7c
--- /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 00000000000..8b30c8bc6b0
--- /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 00000000000..3ddfc04cfc8
--- /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 00000000000..84da28f7253
--- /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 00000000000..f770a5de4ed
--- /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 00000000000..b4c48f0376c
--- /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 <jef@mail.acme.com>.
+** 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 00000000000..66d79106f48
--- /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 <jef@mail.acme.com>.
+** 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 00000000000..194ddcb5600
--- /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 <jef@mail.acme.com>.
+** 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 00000000000..c9d3df0b3f4
--- /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 00000000000..018496f676e
--- /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 00000000000..45ec590dd06
--- /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 00000000000..202a5d658c5
--- /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 00000000000..fc50dabfbb0
--- /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 00000000000..8c725bfbc07
--- /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 00000000000..135a1799d32
--- /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 00000000000..71aa644b144
--- /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 00000000000..38f93a7da9f
--- /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 00000000000..a56ba0ca481
--- /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 00000000000..58b9ef4c424
--- /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 00000000000..d609ac64718
--- /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 00000000000..fa10b91b97f
--- /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 00000000000..3c36729ff26
--- /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 00000000000..3b5e23b20c5
--- /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 00000000000..8eff3b8ce2b
--- /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 00000000000..c393b210a30
--- /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 00000000000..102a26237f7
--- /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 00000000000..b0c5fbeca77
--- /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 00000000000..beba11a0931
--- /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 00000000000..897b7730182
--- /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 00000000000..3a17ea1ce9a
--- /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 00000000000..6f34dad928a
--- /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 00000000000..0306ee1df39
--- /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 00000000000..c34c9041657
--- /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 00000000000..9d1081de88b
--- /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 00000000000..fba4aa9ae66
--- /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 00000000000..11a5cb201af
--- /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 00000000000..3684c23a68d
--- /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 00000000000..28021b61611
--- /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 00000000000..a33615e1ba0
--- /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 00000000000..0d112575ce7
--- /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 00000000000..530fa184e52
--- /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 00000000000..27b4c3c05a7
--- /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 00000000000..bb3ab602ef0
--- /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 00000000000..9045f3815b3
--- /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 00000000000..18f4104e33c
--- /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 00000000000..3a61992ad72
--- /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 00000000000..013bb56e445
--- /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 00000000000..8e3fa503eaf
--- /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 00000000000..44a30219f1d
--- /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 00000000000..e18e63a76af
--- /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 00000000000..35c5736c84f
--- /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 00000000000..50f87d0cacb
--- /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 00000000000..060b466ac12
--- /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 00000000000..7fbdcb0e34e
--- /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 00000000000..e13db876ae5
--- /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 00000000000..7a00f22191d
--- /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 00000000000..2762b54f98d
--- /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 00000000000..c15683c85f4
--- /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 00000000000..811d595aade
--- /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 00000000000..19aea09032f
--- /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 00000000000..7c82700accb
--- /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 00000000000..2f5f003fae2
--- /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 00000000000..c8a5df9c325
--- /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 00000000000..d6e77b0d991
--- /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 00000000000..4a899512e31
--- /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 00000000000..c979f065962
--- /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 00000000000..cf31b8bdf0b
--- /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 00000000000..83736d5e166
--- /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 00000000000..3711716927a
--- /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 00000000000..1da8df840c1
--- /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 00000000000..4d869bba239
--- /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 00000000000..8f8aff9a623
--- /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 00000000000..45a93798fed
--- /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 00000000000..6705bf34645
--- /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 00000000000..28daea6c9f5
--- /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 00000000000..1de89e909da
--- /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 00000000000..9077c18fd62
--- /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 00000000000..07d7867e990
--- /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 00000000000..b3d21521abf
--- /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 00000000000..cb0e37e5ce8
--- /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 00000000000..8808b8c890f
--- /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 00000000000..419fa0f3fd4
--- /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 00000000000..e5456a5eebd
--- /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 00000000000..7d5a12cb89a
--- /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 00000000000..e646a107113
--- /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 00000000000..c9073030300
--- /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 00000000000..b549cffeef6
--- /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 00000000000..d40c4eecbc7
--- /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 00000000000..92d5fcc6e66
--- /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 00000000000..95e4bff89e4
--- /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 00000000000..8780b22a002
--- /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 00000000000..be6e1b24e13
--- /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 00000000000..fbd6e78d797
--- /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 00000000000..36b58a91726
--- /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 00000000000..821ac74cc05
--- /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 00000000000..ee2fb186d94
--- /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 00000000000..123f2eb26c4
--- /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 00000000000..7de90425a35
--- /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 00000000000..129b3bdc18e
--- /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 00000000000..5801f34cf15
--- /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 00000000000..3a6c13f3136
--- /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 00000000000..208d62e4296
--- /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 00000000000..5131c42b0fe
--- /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 00000000000..cfa0fbe9c17
--- /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 00000000000..9ebbe3ae47e
--- /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 00000000000..9e3eac149fd
--- /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 00000000000..c0f1843fb15
--- /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 00000000000..bb0b91eb52c
--- /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 00000000000..91cf099c3ac
--- /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 00000000000..43d8b5c33a8
--- /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 00000000000..efc6e8ca702
--- /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 00000000000..df0551aa6a9
--- /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 00000000000..59446b4a771
--- /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 00000000000..10c2cf40505
--- /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 00000000000..9ed493a2322
--- /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 00000000000..3b439b817ba
--- /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 00000000000..797b0f6f28c
--- /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 00000000000..e24cf845f9c
--- /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 00000000000..68e5fa5b379
--- /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 00000000000..c902420e9ce
--- /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 00000000000..f67fef4fe10
--- /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 00000000000..7cae581ba71
--- /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 00000000000..1fcf2c3872e
--- /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 00000000000..9a98ead5073
--- /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 00000000000..4ddc8946ba4
--- /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 00000000000..6638cebfeea
--- /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 00000000000..8535f74eb47
--- /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 00000000000..99bd3936459
--- /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 00000000000..57029a13ae9
--- /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 00000000000..76de0d18707
--- /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 00000000000..f5066b6e038
--- /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 00000000000..39cd48f022f
--- /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 00000000000..0de2aa1bf19
--- /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 00000000000..81f09843826
--- /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 00000000000..9d560bd916a
--- /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 00000000000..4312aea86d8
--- /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 00000000000..38601f8f4e6
--- /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 00000000000..3776d38e112
--- /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 00000000000..3875c5c2502
--- /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 00000000000..ba71aa5837a
--- /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 00000000000..df3b55ee6d7
--- /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 00000000000..850858a3b23
--- /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 00000000000..546b22916ca
--- /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 00000000000..4fcf4499e75
--- /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 00000000000..19c2c074f9d
--- /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 00000000000..226580955d3
--- /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 00000000000..c0f019c5499
--- /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 00000000000..62eda294838
--- /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 00000000000..6d4ca3d457b
--- /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 00000000000..8b79047b16d
--- /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 00000000000..169889a37a8
--- /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 00000000000..d99ebb0449a
--- /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 00000000000..b3338f383d3
--- /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 00000000000..00fc29cae72
--- /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 00000000000..15fa142a77a
--- /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 00000000000..01390acd551
--- /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 00000000000..db81278b35c
--- /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 00000000000..e25439a974d
--- /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 00000000000..0f6b7d79196
--- /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 00000000000..c3c8a085b77
--- /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 00000000000..b465a7d3754
--- /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 00000000000..894a8321528
--- /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 00000000000..765d4c9e25a
--- /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 00000000000..c1d96cc3deb
--- /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 00000000000..92d8ab89c6a
--- /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 00000000000..874a379b9d9
--- /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 00000000000..2f39dea9480
--- /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 00000000000..35f0f2d8dea
--- /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 00000000000..0d345b2d988
--- /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 00000000000..795a105ad92
--- /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 00000000000..345a322eb17
--- /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 00000000000..4a21b4e4388
--- /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 00000000000..b0fee65f8f3
--- /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 00000000000..234066460ef
--- /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 00000000000..40f9dbad997
--- /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 00000000000..15d5bcaa17a
--- /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 00000000000..e8c29d4fe83
--- /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 00000000000..c3c1aef4857
--- /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 00000000000..924ff469f56
--- /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 00000000000..ce24eabe96f
--- /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 00000000000..82d3234a133
--- /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 00000000000..fd70423ac10
--- /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 00000000000..4324ee25282
--- /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 00000000000..b6c52c3e08d
--- /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 00000000000..267c7a7864e
--- /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 00000000000..332eff346f9
--- /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 00000000000..67a41eabcf6
--- /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 00000000000..96b2a9d7f33
--- /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 00000000000..d2ae592a80a
--- /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 00000000000..11285822a7e
--- /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;
+  }
+}