about summary refs log tree commit diff stats
path: root/linux/mu.subx
diff options
context:
space:
mode:
Diffstat (limited to 'linux/mu.subx')
-rw-r--r--linux/mu.subx38350
1 files changed, 38350 insertions, 0 deletions
diff --git a/linux/mu.subx b/linux/mu.subx
new file mode 100644
index 00000000..fa3bda75
--- /dev/null
+++ b/linux/mu.subx
@@ -0,0 +1,38350 @@
+# The Mu computer's level-2 language, also called Mu.
+# http://akkartik.name/post/mu-2019-2
+#
+# To run:
+#   $ ./translate_subx init.linux [0-9]*.subx apps/mu.subx
+#   $ ./a.elf < prog.mu > prog.elf
+
+# == Goals
+# 1. Be memory safe. It should be impossible to corrupt the heap, or to create
+# a bad pointer. (Requires strong type safety.)
+# 2. Do as little as possible to achieve goal 1. The translator should be
+# implementable in machine code.
+#   - minimize impedance mismatch between source language and SubX target
+#     (e.g. programmer manages registers manually)
+#   - checks over syntax
+#     (e.g. programmer's register allocation is checked)
+#   - runtime checks to avoid complex static analysis
+#     (e.g. array indexing always checks bounds)
+
+# == Language description
+# A program is a sequence of function and type definitions.
+#
+# Function example:
+#   fn foo n: int -> _/eax: int {
+#     ...
+#   }
+#
+# Functions consist of a name, optional inputs, optional outputs and a block.
+#
+# Function inputs and outputs are variables. All variables have a type and
+# storage specifier. They can be placed either in memory (on the stack) or in
+# one of 6 named registers.
+#   eax ecx edx ebx esi edi
+# Variables in registers must be primitive 32-bit types.
+# Variables not explicitly placed in a register are on the stack.
+#
+# Function inputs are always passed in memory (on the stack), while outputs
+# are always returned in registers. Outputs can't be named; they use the
+# dummy value '_'.
+#
+# Blocks mostly consist of statements.
+#
+# Statements mostly consist of a name, optional inputs and optional outputs.
+#
+# Statement inputs are variables or literals. Variables need to specify type
+# (and storage) the first time they're mentioned but not later.
+#
+# Statement outputs, like function outputs, must be variables in registers.
+#
+# Statement names must be either primitives or user-defined functions.
+#
+# Primitives can write to any register.
+# User-defined functions only write to hard-coded registers. Outputs of each
+# call must have the same registers as in the function definition.
+#
+# There are some other statement types:
+#   - blocks. Multiple statements surrounded by '{...}' and optionally
+#     prefixed with a label name and ':'
+#       - {
+#           ...
+#         }
+#       - foo: {
+#           ...
+#         }
+#
+#   - variable definitions on the stack. E.g.:
+#       - var foo: int
+#       - var bar: (array int 3)
+#     There's no initializer; variables are automatically initialized.
+#     The type of a local variable is either word-length (4 bytes) or starts with 'ref'.
+#
+#   - variables definitions in a register. E.g.:
+#       - var foo/eax: int <- add bar 1
+#     The initializer is mandatory and must be a valid instruction that writes
+#     a single output to the right register. In practice registers will
+#     usually be either initialized by primitives or copied from eax.
+#       - var eax: int <- foo bar quux
+#         var floo/ecx: int <- copy eax
+#
+# Still todo:
+#   global variables
+#   union types
+#
+# Formal types:
+#   A program is a linked list of functions
+#   A function contains:
+#     name: (handle array byte)
+#     inouts: linked list of vars  <-- 'inouts' is more precise than 'inputs'
+#       data: (handle var)
+#       next: (handle list)
+#     outputs: linked list of vars
+#       data: (handle var)
+#       next: (handle list)
+#     body: (handle block)
+#   A var-type contains:
+#     name: (handle array byte)
+#     type: (handle type-tree)
+#
+#   A statement can be:
+#     tag 0: a block
+#     tag 1: a simple statement (stmt1)
+#     tag 2: a variable defined on the stack
+#     tag 3: a variable defined in a register
+#
+#   A block contains:
+#     tag: 0
+#     statements: (handle list stmt)
+#     name: (handle array byte) -- starting with '$'
+#
+#   A regular statement contains:
+#     tag: 1
+#     operation: (handle array byte)
+#     inouts: (handle list operand)
+#     outputs: (handle list var)
+#
+#   A variable defined on the stack contains:
+#     tag: 2
+#     name: (handle array byte)
+#     type: (handle type-tree)
+#
+#   A variable defined in a register contains:
+#     tag: 3
+#     name: (handle array byte)
+#     type: (handle type-tree)
+#     reg: (handle array byte)
+
+# == Translation: managing the stack
+# Now that we know what the language looks like in the large, let's think
+# about how translation happens from the bottom up. One crucial piece of the
+# puzzle is how Mu will clean up variables defined on the stack for you.
+#
+# Assume that we maintain a 'functions' list while parsing source code. And a
+# 'primitives' list is a global constant. Both these contain enough information
+# to perform type-checking on function calls or primitive statements, respectively.
+#
+# Defining variables pushes them on a stack with the current block depth and
+# enough information about their location (stack offset or register).
+# Starting a block increments the current block id.
+# Each statement now has enough information to emit code for it.
+# Ending a block is where the magic happens:
+#   pop all variables at the current block depth
+#   emit code to restore all register variables introduced at the current depth
+#   emit code to clean up all stack variables at the current depth (just increment esp)
+#   decrement the current block depth
+#
+# Formal types:
+#   live-vars: stack of vars
+#   var:
+#     name: (handle array byte)
+#     type: (handle type-tree)
+#     block: int
+#     stack-offset: int  (added to ebp)
+#     register: (handle array byte)
+#       either usual register names
+#       or '*' to indicate any register
+#   At most one of stack-offset or register-index must be non-zero.
+#   A register of '*' designates a variable _template_. Only legal in formal
+#   parameters for primitives.
+
+# == Translating a single function call
+# This one's easy. Assuming we've already checked things, we just drop the
+# outputs (which use hard-coded registers) and emit inputs in a standard format.
+#
+# out1, out2, out3, ... <- name inout1, inout2, inout3, ...
+# =>
+# (name inout1 inout2 inout3)
+#
+# Formal types:
+#   functions: linked list of info
+#     name: (handle array byte)
+#     inouts: linked list of vars
+#     outputs: linked list of vars
+#     body: block (linked list of statements)
+
+# == Translating a single primitive instruction
+# A second crucial piece of the puzzle is how Mu converts fairly regular
+# primitives with their uniform syntax to SubX instructions with their gnarly
+# x86 details.
+#
+# Mu instructions have inputs and outputs. Primitives can have up to 2 of
+# them.
+# SubX instructions have rm32 and r32 operands.
+# The translation between them covers almost all the possibilities.
+#   Instructions with 1 inout may turn into ones with 1 rm32
+#     (e.g. incrementing a var on the stack)
+#   Instructions with 1 output may turn into ones with 1 rm32
+#     (e.g. incrementing a var in a register)
+#   1 inout and 1 output may turn into 1 rm32 and 1 r32
+#     (e.g. adding a var to a reg)
+#   2 inouts may turn into 1 rm32 and 1 r32
+#     (e.g. adding a reg to a var)
+#   1 inout and 1 literal may turn into 1 rm32 and 1 imm32
+#     (e.g. adding a constant to a var)
+#   1 output and 1 literal may turn into 1 rm32 and 1 imm32
+#     (e.g. adding a constant to a reg)
+#   2 outputs to hardcoded registers and 1 inout may turn into 1 rm32
+#     (special-case: divide edx:eax by a var or reg)
+# Observations:
+#   We always emit rm32. It may be the first inout or the first output.
+#   We may emit r32 or imm32 or neither.
+#   When we emit r32 it may come from first inout or second inout or first output.
+#
+# Accordingly, the formal data structure for a primitive looks like this:
+#   primitives: linked list of info
+#     name: (handle array byte)
+#     mu-inouts: linked list of vars to check
+#     mu-outputs: linked list of vars to check; at most a singleton
+#     subx-name: (handle array byte)
+#     subx-rm32: enum arg-location
+#     subx-r32: enum arg-location
+#     subx-imm32: enum arg-location
+#     subx-imm8: enum arg-location
+#     subx-disp32: enum arg-location
+#     subx-xm32: enum arg-location
+#     subx-x32: enum arg-location
+#   arg-location: enum
+#     0 means none
+#     1 means first inout
+#     2 means second inout
+#     3 means first output
+
+# == Translating a block
+# Emit block name if necessary
+# Emit '{'
+# When you encounter a statement, emit it as above
+# When you encounter a variable declaration
+#   emit any code needed for it (bzeros)
+#   push it on the var stack
+#   update register dict if necessary
+# When you encounter '}'
+#   While popping variables off the var stack until block id changes
+#     Emit code needed to clean up the stack
+#       either increment esp
+#       or pop into appropriate register
+
+# The rest is straightforward.
+
+== data
+
+Program:
+_Program-functions:  # (handle function)
+  0/imm32
+_Program-functions->payload:
+  0/imm32
+_Program-types:  # (handle typeinfo)
+  0/imm32
+_Program-types->payload:
+  0/imm32
+_Program-signatures:  # (handle function)
+  0/imm32
+_Program-signatures->payload:
+  0/imm32
+
+# Some constants for simulating the data structures described above.
+# Many constants here come with a type in a comment.
+#
+# Sometimes the type is of the value at that offset for the given type. For
+# example, if you start at a function record and move forward Function-inouts
+# bytes, you'll find a (handle list var).
+#
+# At other times, the type is of the constant itself. For example, the type of
+# the constant Function-size is (addr int). To get the size of a function,
+# look in *Function-size.
+
+Function-name:  # (handle array byte)
+  0/imm32
+Function-inouts:  # (handle list var)
+  8/imm32
+Function-outputs:  # (handle list var)
+  0x10/imm32
+Function-body:  # (handle block)
+  0x18/imm32
+Function-next:  # (handle function)
+  0x20/imm32
+Function-size:  # (addr int)
+  0x28/imm32/40
+
+Primitive-name:  # (handle array byte)
+  0/imm32
+Primitive-inouts:  # (handle list var)
+  8/imm32
+Primitive-outputs:  # (handle list var)
+  0x10/imm32
+Primitive-subx-name:  # (handle array byte)
+  0x18/imm32
+Primitive-subx-rm32:  # enum arg-location
+  0x20/imm32
+Primitive-subx-r32:  # enum arg-location
+  0x24/imm32
+Primitive-subx-imm32:  # enum arg-location
+  0x28/imm32
+Primitive-subx-imm8:  # enum arg-location  -- only for bit shifts
+  0x2c/imm32
+Primitive-subx-disp32:  # enum arg-location  -- only for branches
+  0x30/imm32
+Primitive-subx-xm32:  # enum arg-location
+  0x34/imm32
+Primitive-subx-x32:  # enum arg-location
+  0x38/imm32
+Primitive-next:  # (handle function)
+  0x3c/imm32
+Primitive-size:  # (addr int)
+  0x44/imm32/68
+
+Stmt-tag:  # int
+  0/imm32
+
+Block-stmts:  # (handle list stmt)
+  4/imm32
+Block-var:  # (handle var)
+  0xc/imm32
+
+Stmt1-operation:  # (handle array byte)
+  4/imm32
+Stmt1-inouts:  # (handle stmt-var)
+  0xc/imm32
+Stmt1-outputs:  # (handle stmt-var)
+  0x14/imm32
+
+Vardef-var:  # (handle var)
+  4/imm32
+
+Regvardef-operation:  # (handle array byte)
+  4/imm32
+Regvardef-inouts:  # (handle stmt-var)
+  0xc/imm32
+Regvardef-outputs:  # (handle stmt-var)  # will have exactly one element
+  0x14/imm32
+
+Stmt-size:  # (addr int)
+  0x1c/imm32
+
+Var-name:  # (handle array byte)
+  0/imm32
+Var-type:  # (handle type-tree)
+  8/imm32
+Var-block-depth:  # int -- not available until code-generation time
+  0x10/imm32
+Var-offset:  # int -- not available until code-generation time
+  0x14/imm32
+Var-register:  # (handle array byte) -- name of a register
+  0x18/imm32
+Var-size:  # (addr int)
+  0x20/imm32
+
+List-value:  # (handle _)
+  0/imm32
+List-next:  # (handle list _)
+  8/imm32
+List-size:  # (addr int)
+  0x10/imm32
+
+# A stmt-var is like a list of vars with call-site specific metadata
+Stmt-var-value:  # (handle var)
+  0/imm32
+Stmt-var-next:  # (handle stmt-var)
+  8/imm32
+Stmt-var-is-deref:  # boolean
+  0x10/imm32
+Stmt-var-size:  # (addr int)
+  0x14/imm32
+
+# A live-var is a var augmented with information needed for tracking live
+# variables.
+Live-var-value:  # (handle var)
+  0/imm32
+Live-var-register-spilled:  # boolean; only used if value is in a register, and only during code-gen
+  8/imm32
+Live-var-size:  # (addr int)
+  0xc/imm32
+
+# Types are expressed as trees (s-expressions) of type-ids (ints).
+
+Type-tree-is-atom:  # boolean
+  0/imm32
+# if is-atom?
+Type-tree-value:  # type-id
+  4/imm32
+Type-tree-value-size:  # int (for static data structure sizes)
+  8/imm32
+Type-tree-parameter-name:  # (handle array byte) for type parameters
+  8/imm32
+# unless is-atom?
+Type-tree-left:  # (addr type-tree)
+  4/imm32
+Type-tree-right:  # (addr type-tree)
+  0xc/imm32
+#
+Type-tree-size:  # (addr int)
+  0x14/imm32
+
+# Types
+
+# TODO: Turn this data structure into valid Mu, with (fake) handles rather than addrs.
+Type-id:  # (stream (addr array byte))
+  0/imm32/write  # initialized later from Primitive-type-ids
+  0/imm32/read
+  0x100/imm32/size
+  # data
+  0/imm32  # 0 reserved for literals; value is just the name
+           # Not to be used directly, so we don't include a name here.
+  "int"/imm32  # 1
+  "addr"/imm32  # 2
+  "array"/imm32  # 3
+  "handle"/imm32  # 4
+  "boolean"/imm32  # 5
+  0/imm32  # 6 reserved for constants; they're like literals, but value is an int in Var-offset
+           # Not to be used directly, so we don't include a name here.
+  "offset"/imm32  # 7: (offset T) is guaranteed to be a 32-bit multiple of size-of(T)
+  # 0x20
+  "byte"/imm32  # 8
+  0/imm32  # 9 reserved for array-capacity; value is in Type-tree-size.
+           # Not to be used directly, so we don't include a name here.
+  0/imm32  # 10 reserved for type parameters; value is (address array byte) in Type-tree-value2.
+           # Not to be used directly, so we don't include a name here.
+  "stream"/imm32  # 11
+  "slice"/imm32  # 12
+  "code-point"/imm32  # 13; smallest scannable unit from a text stream
+  "grapheme"/imm32  # 14; smallest printable unit; will eventually be composed of multiple code-points, but currently corresponds 1:1
+                    # only 4-byte graphemes in utf-8 are currently supported;
+                    # unclear how we should deal with larger clusters.
+  "float"/imm32     # 15
+  # 0x40
+  0/imm32  # 16 reserved for literal strings; value is just the name
+           # Not to be used directly, so we don't include a name here.
+           # TODO: move this up next to literal ints
+  # Keep Primitive-type-ids in sync if you add types here.
+          0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
+  0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
+  0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
+  0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
+  0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
+  0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
+
+Primitive-type-ids:  # (addr int)
+  0x44
+
+# == Type definitions
+# Program->types contains some typeinfo for each type definition.
+# Types contain vars with types, but can't specify registers.
+Typeinfo-id:  # type-id
+  0/imm32
+Typeinfo-fields:  # (handle table (handle array byte) (handle typeinfo-entry))
+  4/imm32
+# Total size must be >= 0
+# During parsing it may take on two additional values:
+#   -2: not yet initialized
+#   -1: in process of being computed
+# See populate-mu-type-sizes for details.
+Typeinfo-total-size-in-bytes:  # int
+  0xc/imm32
+Typeinfo-next:  # (handle typeinfo)
+  0x10/imm32
+Typeinfo-size:  # (addr int)
+  0x18/imm32
+
+# Each entry in the typeinfo->fields table has a pointer to a string and a
+# pointer to a typeinfo-entry.
+Typeinfo-fields-row-size:  # (addr int)
+  0x10/imm32
+
+# typeinfo-entry objects have information about a field in a single record type
+#
+# each field of a type is represented using two var's:
+#   1. the input var: expected type of the field; convenient for creating using parse-var-with-type
+#   2. the output var: a constant containing the byte offset; convenient for code-generation
+# computing the output happens after parsing; in the meantime we preserve the
+# order of fields in the 'index' field.
+Typeinfo-entry-input-var:  # (handle var)
+  0/imm32
+Typeinfo-entry-index:  # int
+  8/imm32
+Typeinfo-entry-output-var:  # (handle var)
+  0xc/imm32
+Typeinfo-entry-size:  # (addr int)
+  0x14/imm32
+
+== code
+
+Entry:
+    # . prologue
+    89/<- %ebp 4/r32/esp
+    (new-segment *Heap-size Heap)
+    # if (argv[1] == "test') run-tests()
+    {
+      # if (argc <= 1) break
+      81 7/subop/compare *ebp 1/imm32
+      7e/jump-if-<= break/disp8
+      # if (argv[1] != "test") break
+      (kernel-string-equal? *(ebp+8) "test")  # => eax
+      3d/compare-eax-and 0/imm32/false
+      74/jump-if-= break/disp8
+      #
+      (run-tests)
+      # syscall(exit, *Num-test-failures)
+      8b/-> *Num-test-failures 3/r32/ebx
+      eb/jump $mu-main:end/disp8
+    }
+    # otherwise convert Stdin
+    (convert-mu Stdin Stdout Stderr 0)
+    (flush Stdout)
+    # syscall(exit, 0)
+    bb/copy-to-ebx 0/imm32
+$mu-main:end:
+    e8/call syscall_exit/disp32
+
+convert-mu:  # in: (addr buffered-file), out: (addr buffered-file), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    # initialize global data structures
+    c7 0/subop/copy *Next-block-index 1/imm32
+    8b/-> *Primitive-type-ids 0/r32/eax
+    89/<- *Type-id 0/r32/eax  # stream-write
+    c7 0/subop/copy *_Program-functions 0/imm32
+    c7 0/subop/copy *_Program-functions->payload 0/imm32
+    c7 0/subop/copy *_Program-types 0/imm32
+    c7 0/subop/copy *_Program-types->payload 0/imm32
+    c7 0/subop/copy *_Program-signatures 0/imm32
+    c7 0/subop/copy *_Program-signatures->payload 0/imm32
+    #
+    (parse-mu *(ebp+8) *(ebp+0x10) *(ebp+0x14))
+    (populate-mu-type-sizes *(ebp+0x10) *(ebp+0x14))
+#?     (dump-typeinfos "=== typeinfos\n")
+    (check-mu-types *(ebp+0x10) *(ebp+0x14))
+    (emit-subx *(ebp+0xc) *(ebp+0x10) *(ebp+0x14))
+$convert-mu:end:
+    # . restore registers
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-empty-input:
+    # empty input => empty output
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+    (check-stream-equal _test-output-stream "" "F - test-convert-empty-input")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-skeleton:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-skeleton/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-skeleton/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-skeleton/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-skeleton/3")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-skeleton/4")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-skeleton/5")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-skeleton/6")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-skeleton/7")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-multiple-function-skeletons:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "}\n")
+    (write _test-input-stream "fn bar {\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check first function
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-multiple-function-skeletons/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-multiple-function-skeletons/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-multiple-function-skeletons/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-multiple-function-skeletons/3")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-multiple-function-skeletons/4")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-multiple-function-skeletons/5")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-multiple-function-skeletons/6")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-multiple-function-skeletons/7")
+    # check second function
+    (check-next-stream-line-equal _test-output-stream "bar:"                    "F - test-convert-multiple-function-skeletons/10")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-multiple-function-skeletons/11")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-multiple-function-skeletons/12")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-multiple-function-skeletons/13")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-multiple-function-skeletons/14")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-multiple-function-skeletons/15")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-multiple-function-skeletons/16")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-multiple-function-skeletons/17")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-with-arg:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo n: int {\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-arg/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-arg/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-arg/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-arg/3")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-arg/4")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-arg/5")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-arg/6")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-arg/7")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-function-with-redefined-name:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "}\n")
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-function-with-redefined-name: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo defined more than once"  "F - test-function-with-redefined-name: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-function-with-redefined-name: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-function-with-redefined-name-2:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "}\n")
+    (write _test-input-stream "sig foo\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-function-with-redefined-name-2: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo defined more than once"  "F - test-function-with-redefined-name-2: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-function-with-redefined-name-2: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-function-with-redefined-name-3:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "sig foo\n")
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-function-with-redefined-name-3: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo defined more than once"  "F - test-function-with-redefined-name-3: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-function-with-redefined-name-3: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-function-with-inout-in-register:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo x/eax: int {\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-function-with-inout-in-register: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: function inout 'x' cannot be in a register"  "F - test-function-with-inout-in-register: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-function-with-inout-in-register: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-function-with-addr-output:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo -> _/eax: (addr int) {\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-function-with-addr-output: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: output cannot have an addr type; that could allow unsafe addresses to escape the function"  "F - test-function-with-addr-output: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-function-with-addr-output: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-function-with-addr-inout:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo a: (addr addr int) {\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-function-with-addr-inout: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: inout 'a' cannot contain 'addr' anywhere in the type except the first word; that could allow unsafe addresses to escape the function"  "F - test-function-with-addr-inout: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-function-with-addr-inout: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-function-with-addr-inout-2:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo a: (addr array addr int) {\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-function-with-addr-inout-2: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: inout 'a' cannot contain 'addr' anywhere in the type except the first word; that could allow unsafe addresses to escape the function"  "F - test-function-with-addr-inout-2: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-function-with-addr-inout-2: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-function-with-addr-inout-3:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo a: (addr array (addr int) 3) {\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-function-with-addr-inout-3: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: inout 'a' cannot contain 'addr' anywhere in the type except the first word; that could allow unsafe addresses to escape the function"  "F - test-function-with-addr-inout-3: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-function-with-addr-inout-3: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-function-with-addr-inout-4:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo a: (array (addr int) 3) {\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-function-with-addr-inout-4: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: inout 'a' cannot contain 'addr' anywhere in the type except the first word; that could allow unsafe addresses to escape the function"  "F - test-function-with-addr-inout-4: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-function-with-addr-inout-4: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+# 'main' is an exception
+test-function-main-with-addr-inout:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn main a: (addr addr int) {\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+    # no errors
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+# 'lookup' is an exception, but only in signatures
+test-signature-lookup-with-addr-inout:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "sig lookup h: (handle _T) -> _/eax: (addr _T)\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+    # no errors
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-with-arg-and-body:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo n: int {\n")
+    (write _test-input-stream "  increment n\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-arg-and-body/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-arg-and-body/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-arg-and-body/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-arg-and-body/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-arg-and-body/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-arg-and-body/5")
+    (check-next-stream-line-equal _test-output-stream "    ff 0/subop/increment *(ebp+0x00000008)"  "F - test-convert-function-with-arg-and-body/6")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-arg-and-body/7")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-arg-and-body/8")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-arg-and-body/9")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-arg-and-body/10")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-arg-and-body/11")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-arg-and-body/12")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-distinguishes-args:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo a: int, b: int {\n")
+    (write _test-input-stream "  increment b\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-distinguishes-args/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-distinguishes-args/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-distinguishes-args/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-distinguishes-args/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-distinguishes-args/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-distinguishes-args/5")
+    (check-next-stream-line-equal _test-output-stream "    ff 0/subop/increment *(ebp+0x0000000c)"  "F - test-convert-function-distinguishes-args/6")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-distinguishes-args/7")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-distinguishes-args/8")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-distinguishes-args/9")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-distinguishes-args/10")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-distinguishes-args/11")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-distinguishes-args/12")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-with-return-literal:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo -> _/eax: int {\n")
+    (write _test-input-stream "  return 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-return-literal/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-return-literal/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-return-literal/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-return-literal/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-return-literal/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-return-literal/5")
+    (check-next-stream-line-equal _test-output-stream "    c7 0/subop/copy %eax 0/imm32"  "F - test-convert-function-with-return-literal/6")
+    (check-next-stream-line-equal _test-output-stream "    e9/jump $foo:0x00000001:break/disp32"  "F - test-convert-function-with-return-literal/7")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-return-literal/8")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-return-literal/9")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-return-literal/10")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-return-literal/11")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-return-literal/12")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-return-literal/13")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-with-return:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo -> _/eax: int {\n")
+    (write _test-input-stream "  var y: int\n")
+    (write _test-input-stream "  return y\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-return/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-return/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-return/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-return/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-return/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-return/5")
+    (check-next-stream-line-equal _test-output-stream "    68/push 0/imm32"     "F - test-convert-function-with-return/6")  # y
+    (check-next-stream-line-equal _test-output-stream "    8b/-> *(ebp+0xfffffffc) 0x00000000/r32" "F - test-convert-function-with-return/7")
+    (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-return/8")
+    (check-next-stream-line-equal _test-output-stream "    e9/jump $foo:0x00000001:break/disp32"  "F - test-convert-function-with-return/9")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-return/10")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-return/11")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-return/12")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-return/13")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-return/14")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-return/15")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-with-return-float:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo -> _/xmm0: float {\n")
+    (write _test-input-stream "  var y: float\n")
+    (write _test-input-stream "  return y\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-return/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-return/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-return/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-return/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-return/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-return/5")
+    (check-next-stream-line-equal _test-output-stream "    68/push 0/imm32"     "F - test-convert-function-with-return/6")  # y
+    (check-next-stream-line-equal _test-output-stream "    f3 0f 10/-> *(ebp+0xfffffffc) 0x00000000/x32" "F - test-convert-function-with-return/7")
+    (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-return/8")
+    (check-next-stream-line-equal _test-output-stream "    e9/jump $foo:0x00000001:break/disp32"  "F - test-convert-function-with-return/9")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-return/10")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-return/11")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-return/12")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-return/13")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-return/14")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-return/15")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-with-return-register:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo -> _/eax: int {\n")
+    (write _test-input-stream "  var y/eax: int <- copy 3\n")
+    (write _test-input-stream "  return y\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-return-register/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-return-register/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-return-register/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-return-register/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-return-register/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-return-register/5")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"  "F - test-convert-function-with-return-register/6")
+    (check-next-stream-line-equal _test-output-stream "    b8/copy-to-eax 3/imm32"  "F - test-convert-function-with-return-register/7")
+    (check-next-stream-line-equal _test-output-stream "    8b/-> %eax 0x00000000/r32" "F - test-convert-function-with-return-register/8")
+    (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 4/imm32"  "F - test-convert-function-with-return-register/9")
+    (check-next-stream-line-equal _test-output-stream "    e9/jump $foo:0x00000001:break/disp32"  "F - test-convert-function-with-return-register/10")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-return-register/11")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-return-register/12")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-return-register/13")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-return-register/14")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-return-register/15")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-return-register/16")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-function-with-output-without-register:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo -> _: int {\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-function-with-output-without-register: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: function output '_' must be in a register, in instruction 'fn foo -> _: int {"  "F - test-function-with-output-without-register: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-function-with-output-without-register: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-function-with-outputs-in-conflicting-registers:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo -> _/eax: int, _/eax: int {\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-function-with-outputs-in-conflicting-registers: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: outputs must be in unique registers"  "F - test-function-with-outputs-in-conflicting-registers: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-function-with-outputs-in-conflicting-registers: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-function-with-named-output:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo -> x/eax: int {\n")
+    (write _test-input-stream "  return 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-function-with-named-output: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: function outputs cannot be named; rename 'x' in the header to '_'"  "F - test-function-with-named-output: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-function-with-named-output: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-return-with-wrong-type:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo -> _/eax: int {\n")
+    (write _test-input-stream "  var x/eax: boolean <- copy 0\n")
+    (write _test-input-stream "  return x\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-return-with-wrong-type: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: return: 'x' has the wrong type"  "F - test-return-with-wrong-type: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-return-with-wrong-type: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-missing-return:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo -> _/eax: int {\n")
+    (write _test-input-stream "  var x/eax: boolean <- copy 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-missing-return: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: final statement should be a 'return'"  "F - test-missing-return: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-missing-return: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-missing-return-2:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo -> _/eax: int {\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-missing-return-2: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: final statement should be a 'return'"  "F - test-missing-return-2: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-missing-return-2: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-early-exit-without-return:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo -> _/eax: int {\n")
+    (write _test-input-stream "  break\n")
+    (write _test-input-stream "  return 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-early-exit-without-return: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo has outputs, so you cannot 'break' out of the outermost block. Use 'return'."  "F - test-early-exit-without-return: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-early-exit-without-return: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-return-with-too-few-inouts:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo -> _/eax: int {\n")
+    (write _test-input-stream "  return\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-return-with-too-few-inouts: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: return: too few inouts"  "F - test-return-with-too-few-inouts: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-return-with-too-few-inouts: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-return-with-too-many-inouts:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo -> _/eax: int {\n")
+    (write _test-input-stream "  return 0, 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-return-with-too-many-inouts: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: return: too many inouts"  "F - test-return-with-too-many-inouts: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-return-with-too-many-inouts: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-return-unavailable-value:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo -> _/eax: int, _/ecx: int {\n")
+    (write _test-input-stream "  var x/eax: int <- copy 0\n")
+    (write _test-input-stream "  var y/ecx: int <- copy 0\n")
+    (write _test-input-stream "  return y, x\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-return-unavailable-value: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: return: 'x' is no longer available"  "F - test-return-unavailable-value: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-return-unavailable-value: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-return-literal-to-float:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo -> _/xmm0: float {\n")
+    (write _test-input-stream "  return 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-return-literal-to-float: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: return: cannot copy literal '0' to float"  "F - test-return-literal-to-float: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-return-literal-to-float: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-return-with-duplicate-values:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo -> _/eax: int, _/ecx: int {\n")
+    (write _test-input-stream "  var x/eax: int <- copy 0x34\n")
+    (write _test-input-stream "  return x, x\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-return-with-duplicate-values/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-return-with-duplicate-values/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-return-with-duplicate-values/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-return-with-duplicate-values/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-return-with-duplicate-values/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-return-with-duplicate-values/5")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"  "F - test-convert-return-with-duplicate-values/6")
+    (check-next-stream-line-equal _test-output-stream "    b8/copy-to-eax 0x34/imm32"  "F - test-convert-return-with-duplicate-values/7")
+    (check-next-stream-line-equal _test-output-stream "    8b/-> %eax 0x00000000/r32"  "F - test-convert-return-with-duplicate-values/8")
+    (check-next-stream-line-equal _test-output-stream "    8b/-> %eax 0x00000001/r32"  "F - test-convert-return-with-duplicate-values/9")
+    (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 4/imm32"  "F - test-convert-return-with-duplicate-values/10")
+    (check-next-stream-line-equal _test-output-stream "    e9/jump $foo:0x00000001:break/disp32"  "F - test-convert-return-with-duplicate-values/11")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-return-with-duplicate-values/12")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-return-with-duplicate-values/13")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-return-with-duplicate-values/14")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-return-with-duplicate-values/15")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-return-with-duplicate-values/16")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-return-with-duplicate-values/17")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-return-with-duplicate-values-2:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo -> _/eax: int, _/ecx: int {\n")
+    (write _test-input-stream "  var x/ecx: int <- copy 0x34\n")
+    (write _test-input-stream "  return x, x\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-return-with-duplicate-values-2/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-return-with-duplicate-values-2/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-return-with-duplicate-values-2/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-return-with-duplicate-values-2/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-return-with-duplicate-values-2/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-return-with-duplicate-values-2/5")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-convert-return-with-duplicate-values-2/6")
+    (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 0x34/imm32"  "F - test-convert-return-with-duplicate-values-2/7")
+    (check-next-stream-line-equal _test-output-stream "    8b/-> %ecx 0x00000000/r32"  "F - test-convert-return-with-duplicate-values-2/8")
+    (check-next-stream-line-equal _test-output-stream "    8b/-> %ecx 0x00000001/r32"  "F - test-convert-return-with-duplicate-values-2/9")
+    (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 4/imm32"  "F - test-convert-return-with-duplicate-values-2/10")
+    (check-next-stream-line-equal _test-output-stream "    e9/jump $foo:0x00000001:break/disp32"  "F - test-convert-return-with-duplicate-values-2/11")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-return-with-duplicate-values-2/12")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-return-with-duplicate-values-2/13")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-return-with-duplicate-values-2/14")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-return-with-duplicate-values-2/15")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-return-with-duplicate-values-2/16")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-return-with-duplicate-values-2/17")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-stmt-with-unknown-var:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  x <- copy 0x34\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-stmt-with-unknown-var: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: unknown variable 'x'"  "F - test-stmt-with-unknown-var: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-stmt-with-unknown-var: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-stmt-with-invalid-identifier:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  1 <- copy 0x34\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-stmt-with-invalid-identifier: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: invalid identifier '1'"  "F - test-stmt-with-invalid-identifier: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-stmt-with-invalid-identifier: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-stmt-with-deref-var:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  *x <- copy 0x34\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-stmt-with-deref-var: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: output '*x' should write to a register, and therefore cannot be dereferenced"  "F - test-stmt-with-deref-var: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-stmt-with-deref-var: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-with-literal-arg:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo a: int, b: int -> _/eax: int {\n")
+    (write _test-input-stream "  var result/eax: int <- copy a\n")
+    (write _test-input-stream "  result <- add 1\n")
+    (write _test-input-stream "  return result\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-literal-arg/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-literal-arg/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-literal-arg/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-literal-arg/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-literal-arg/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-literal-arg/5")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"  "F - test-convert-function-with-literal-arg/6")
+    (check-next-stream-line-equal _test-output-stream "    8b/-> *(ebp+0x00000008) 0x00000000/r32"  "F - test-convert-function-with-literal-arg/7")
+    (check-next-stream-line-equal _test-output-stream "    05/add-to-eax 1/imm32"  "F - test-convert-function-with-literal-arg/8")
+    (check-next-stream-line-equal _test-output-stream "    8b/-> %eax 0x00000000/r32" "F - test-convert-function-with-literal-arg/9")
+    (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 4/imm32"  "F - test-convert-function-with-literal-arg/10")
+    (check-next-stream-line-equal _test-output-stream "    e9/jump $foo:0x00000001:break/disp32"  "F - test-convert-function-with-literal-arg/11")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-literal-arg/12")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-literal-arg/13")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-literal-arg/14")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-literal-arg/15")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-literal-arg/16")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-literal-arg/17")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-with-literal-arg-2:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo a: int, b: int -> _/ebx: int {\n")
+    (write _test-input-stream "  var result/ebx: int <- copy a\n")
+    (write _test-input-stream "  result <- add 1\n")
+    (write _test-input-stream "  return result\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-literal-arg-2/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-literal-arg-2/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-literal-arg-2/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-literal-arg-2/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-literal-arg-2/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-literal-arg-2/5")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ebx"  "F - test-convert-function-with-literal-arg-2/6")
+    (check-next-stream-line-equal _test-output-stream "    8b/-> *(ebp+0x00000008) 0x00000003/r32"  "F - test-convert-function-with-literal-arg-2/7")
+    (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %ebx 1/imm32"  "F - test-convert-function-with-literal-arg-2/8")
+    (check-next-stream-line-equal _test-output-stream "    8b/-> %ebx 0x00000003/r32" "F - test-convert-function-with-literal-arg-2/9")
+    (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 4/imm32"  "F - test-convert-function-with-literal-arg-2/10")
+    (check-next-stream-line-equal _test-output-stream "    e9/jump $foo:0x00000001:break/disp32"  "F - test-convert-function-with-literal-arg-2/11")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-literal-arg-2/12")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-literal-arg-2/13")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-literal-arg-2/14")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-literal-arg-2/15")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-literal-arg-2/16")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-literal-arg-2/17")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-call-with-literal-arg:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn main -> _/ebx: int {\n")
+    (write _test-input-stream "  var result/eax: int <- do-add 3 4\n")
+    (write _test-input-stream "  return result\n")
+    (write _test-input-stream "}\n")
+    (write _test-input-stream "fn do-add a: int, b: int -> _/eax: int {\n")
+    (write _test-input-stream "  var result/eax: int <- copy a\n")
+    (write _test-input-stream "  result <- add b\n")
+    (write _test-input-stream "  return result\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "main:"                   "F - test-convert-function-call-with-literal-arg/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-call-with-literal-arg/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-call-with-literal-arg/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-call-with-literal-arg/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-call-with-literal-arg/4")
+    (check-next-stream-line-equal _test-output-stream "$main:0x00000001:loop:"  "F - test-convert-function-call-with-literal-arg/5")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"  "F - test-convert-function-call-with-literal-arg/6")
+    (check-next-stream-line-equal _test-output-stream "    (do-add 3 4)"        "F - test-convert-function-call-with-literal-arg/7")
+    (check-next-stream-line-equal _test-output-stream "    8b/-> %eax 0x00000003/r32" "F - test-convert-function-call-with-literal-arg/8")
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax" "F - test-convert-function-with-local-var-in-reg/9")
+    (check-next-stream-line-equal _test-output-stream "    e9/jump $main:0x00000001:break/disp32"  "F - test-convert-function-call-with-literal-arg/10")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-call-with-literal-arg/11")
+    (check-next-stream-line-equal _test-output-stream "$main:0x00000001:break:" "F - test-convert-function-call-with-literal-arg/12")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-call-with-literal-arg/13")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-call-with-literal-arg/14")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-call-with-literal-arg/15")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-call-with-literal-arg/16")
+    (check-next-stream-line-equal _test-output-stream "do-add:"                 "F - test-convert-function-call-with-literal-arg/17")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-call-with-literal-arg/18")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-call-with-literal-arg/19")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-call-with-literal-arg/20")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-call-with-literal-arg/21")
+    (check-next-stream-line-equal _test-output-stream "$do-add:0x00000002:loop:"  "F - test-convert-function-call-with-literal-arg/22")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"  "F - test-convert-function-call-with-literal-arg/23")
+    (check-next-stream-line-equal _test-output-stream "    8b/-> *(ebp+0x00000008) 0x00000000/r32"  "F - test-convert-function-call-with-literal-arg/24")
+    (check-next-stream-line-equal _test-output-stream "    03/add *(ebp+0x0000000c) 0x00000000/r32"  "F - test-convert-function-call-with-literal-arg/25")
+    (check-next-stream-line-equal _test-output-stream "    8b/-> %eax 0x00000000/r32" "F - test-convert-function-call-with-literal-arg/26")
+    (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 4/imm32"  "F - test-convert-function-call-with-literal-arg/27")
+    (check-next-stream-line-equal _test-output-stream "    e9/jump $do-add:0x00000002:break/disp32"  "F - test-convert-function-call-with-literal-arg/28")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-call-with-literal-arg/29")
+    (check-next-stream-line-equal _test-output-stream "$do-add:0x00000002:break:"  "F - test-convert-function-call-with-literal-arg/30")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-call-with-literal-arg/31")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-call-with-literal-arg/32")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-call-with-literal-arg/33")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-call-with-literal-arg/34")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-call-with-literal-string-arg:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  string-func \"abc\"\n")
+    (write _test-input-stream "}\n")
+    (write _test-input-stream "sig string-func in: (addr array byte)\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    # no errors
+    # not bothering checking output
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-call-with-null-addr:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  bar 0\n")
+    (write _test-input-stream "}\n")
+    (write _test-input-stream "sig bar in: (addr int)\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    # no errors
+    # not bothering checking output
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-call-with-signature:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn main -> _/ebx: int {\n")
+    (write _test-input-stream "  var result/eax: int <- do-add 3 4\n")
+    (write _test-input-stream "  return result\n")
+    (write _test-input-stream "}\n")
+    (write _test-input-stream "sig do-add a: int, b: int -> _/eax: int\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "main:"                   "F - test-convert-function-call-with-signature/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-call-with-signature/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-call-with-signature/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-call-with-signature/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-call-with-signature/4")
+    (check-next-stream-line-equal _test-output-stream "$main:0x00000001:loop:"  "F - test-convert-function-call-with-signature/5")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"  "F - test-convert-function-call-with-literal-arg/6")
+    (check-next-stream-line-equal _test-output-stream "    (do-add 3 4)"        "F - test-convert-function-call-with-signature/6")
+    (check-next-stream-line-equal _test-output-stream "    8b/-> %eax 0x00000003/r32" "F - test-convert-function-call-with-literal-arg/8")
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax" "F - test-convert-function-with-local-var-in-reg/9")
+    (check-next-stream-line-equal _test-output-stream "    e9/jump $main:0x00000001:break/disp32"  "F - test-convert-function-call-with-literal-arg/10")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-call-with-signature/7")
+    (check-next-stream-line-equal _test-output-stream "$main:0x00000001:break:" "F - test-convert-function-call-with-signature/8")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-call-with-signature/9")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-call-with-signature/10")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-call-with-signature/11")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-call-with-signature/12")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-with-local-var-in-mem:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x: int\n")
+    (write _test-input-stream "  increment x\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-local-var-in-mem/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-local-var-in-mem/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-local-var-in-mem/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-local-var-in-mem/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-local-var-in-mem/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-local-var-in-mem/5")
+    (check-next-stream-line-equal _test-output-stream "    68/push 0/imm32"     "F - test-convert-function-with-local-var-in-mem/6")
+    (check-next-stream-line-equal _test-output-stream "    ff 0/subop/increment *(ebp+0xfffffffc)"  "F - test-convert-function-with-local-var-in-mem/7")
+    (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-local-var-in-mem/8")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-local-var-in-mem/9")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-local-var-in-mem/10")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-local-var-in-mem/11")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-local-var-in-mem/12")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-local-var-in-mem/13")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-local-var-in-mem/14")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-invalid-literal:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  increment 1n\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-convert-invalid-literal: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: variable '1n' cannot begin with a digit (or do you have a typo in a number?)"  "F - test-convert-invalid-literal: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-convert-invalid-literal: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-valid-literal-with-metadata:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x/eax: int <- copy 1/abc\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # no errors
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-local-var-in-mem-has-no-initializer:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x: int <- copy 0\n")
+    (write _test-input-stream "  increment x\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-var-in-mem-has-no-initializer: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: var x: variables on the stack can't take an initializer"  "F - test-var-in-mem-has-no-initializer: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-var-in-mem-has-no-initializer: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-with-local-var-with-compound-type-in-mem:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x: (addr int)\n")
+    (write _test-input-stream "  copy-to x, 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-local-var-with-compound-type-in-mem/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-local-var-with-compound-type-in-mem/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-local-var-with-compound-type-in-mem/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-local-var-with-compound-type-in-mem/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-local-var-with-compound-type-in-mem/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-local-var-with-compound-type-in-mem/5")
+    (check-next-stream-line-equal _test-output-stream "    68/push 0/imm32"     "F - test-convert-function-with-local-var-with-compound-type-in-mem/6")
+    (check-next-stream-line-equal _test-output-stream "    c7 0/subop/copy *(ebp+0xfffffffc) 0/imm32"  "F - test-convert-function-with-local-var-with-compound-type-in-mem/7")
+    (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-local-var-with-compound-type-in-mem/8")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-local-var-with-compound-type-in-mem/9")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-local-var-with-compound-type-in-mem/10")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-local-var-with-compound-type-in-mem/11")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-local-var-with-compound-type-in-mem/12")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-local-var-with-compound-type-in-mem/13")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-local-var-with-compound-type-in-mem/14")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-with-local-var-in-reg:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x/ecx: int <- copy 3\n")
+    (write _test-input-stream "  x <- increment\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-local-var-in-reg/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-local-var-in-reg/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-local-var-in-reg/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-local-var-in-reg/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-local-var-in-reg/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-local-var-in-reg/5")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-convert-function-with-local-var-in-reg/6")
+    (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 3/imm32"  "F - test-convert-function-with-local-var-in-reg/7")
+    (check-next-stream-line-equal _test-output-stream "    41/increment-ecx"    "F - test-convert-function-with-local-var-in-reg/8")
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx" "F - test-convert-function-with-local-var-in-reg/9")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-local-var-in-reg/10")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-local-var-in-reg/11")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-local-var-in-reg/12")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-local-var-in-reg/13")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-local-var-in-reg/14")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-local-var-in-reg/15")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-with-local-var-in-same-reg:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x/ecx: int <- copy 3\n")
+    (write _test-input-stream "  var y/ecx: int <- copy x\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-local-var-in-same-reg/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-local-var-in-same-reg/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-local-var-in-same-reg/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-local-var-in-same-reg/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-local-var-in-same-reg/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-local-var-in-same-reg/5")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-convert-function-with-local-var-in-same-reg/6")
+    (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 3/imm32"  "F - test-convert-function-with-local-var-in-same-reg/7")
+    # optimization: skip the second copy
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx" "F - test-convert-function-with-local-var-in-same-reg/8")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-local-var-in-same-reg/9")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-local-var-in-same-reg/10")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-local-var-in-same-reg/11")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-local-var-in-same-reg/12")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-local-var-in-same-reg/13")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-local-var-in-same-reg/14")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-with-local-var-in-same-reg-dereferenced:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x/ecx: (addr int) <- copy 0\n")
+    (write _test-input-stream "  var y/ecx: int <- copy *x\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-local-var-in-same-reg-dereferenced/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-local-var-in-same-reg-dereferenced/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-local-var-in-same-reg-dereferenced/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-local-var-in-same-reg-dereferenced/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-local-var-in-same-reg-dereferenced/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-local-var-in-same-reg-dereferenced/5")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-convert-function-with-local-var-in-same-reg-dereferenced/6")
+    (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 0/imm32"  "F - test-convert-function-with-local-var-in-same-reg-dereferenced/7")
+    (check-next-stream-line-equal _test-output-stream "    8b/-> *ecx 0x00000001/r32"  "F - test-convert-function-with-local-var-in-same-reg-dereferenced/8")  # don't optimize this away
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx" "F - test-convert-function-with-local-var-in-same-reg-dereferenced/9")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-local-var-in-same-reg-dereferenced/10")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-local-var-in-same-reg-dereferenced/11")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-local-var-in-same-reg-dereferenced/12")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-local-var-in-same-reg-dereferenced/13")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-local-var-in-same-reg-dereferenced/14")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-local-var-in-same-reg-dereferenced/15")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-float-var-in-wrong-register:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x/eax: int <- copy 0\n")
+    (write _test-input-stream "  var y/eax: float <- convert x\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-float-var-in-wrong-register: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: float var 'y' should be in a floating-point register"  "F - test-float-var-in-wrong-register: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-float-var-in-wrong-register: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-non-float-var-in-wrong-register:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x/xmm5: int <- copy 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-non-float-var-in-wrong-register: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: non-float var 'x' should be in an integer register"  "F - test-non-float-var-in-wrong-register: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-non-float-var-in-wrong-register: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-with-allocate:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x/ecx: (addr handle int) <- copy 0\n")
+    (write _test-input-stream "  allocate x\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-allocate/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-allocate/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-allocate/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-allocate/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-allocate/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-allocate/5")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-convert-function-with-allocate/6")
+    (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 0/imm32"  "F - test-convert-function-with-allocate/7")
+    (check-next-stream-line-equal _test-output-stream "    (allocate Heap 0x00000004 %ecx)"  "F - test-convert-function-with-allocate/8")  # 4 = size-of(int)
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx" "F - test-convert-function-with-allocate/9")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-allocate/10")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-allocate/11")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-allocate/12")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-allocate/13")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-allocate/14")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-allocate/15")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-initializer-in-hex:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x/ecx: int <- copy 10\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-initializer-in-hex: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "literal integers are always hex in Mu; start '10' with a '0x' to be unambiguous, converting it to hexadecimal as necessary."  "F - test-initializer-in-hex: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-initializer-in-hex: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-with-second-local-var-in-same-reg:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x/ecx: int <- copy 3\n")
+    (write _test-input-stream "  var y/ecx: int <- copy 4\n")
+    (write _test-input-stream "  y <- increment\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-second-local-var-in-same-reg/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-second-local-var-in-same-reg/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-second-local-var-in-same-reg/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-second-local-var-in-same-reg/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-second-local-var-in-same-reg/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-second-local-var-in-same-reg/5")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-convert-function-with-second-local-var-in-same-reg/6")
+    (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 3/imm32"  "F - test-convert-function-with-second-local-var-in-same-reg/7")
+    (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 4/imm32"  "F - test-convert-function-with-second-local-var-in-same-reg/8")
+    (check-next-stream-line-equal _test-output-stream "    41/increment-ecx"    "F - test-convert-function-with-second-local-var-in-same-reg/9")
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx" "F - test-convert-function-with-second-local-var-in-same-reg/10")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-second-local-var-in-same-reg/11")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-second-local-var-in-same-reg/12")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-second-local-var-in-same-reg/13")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-second-local-var-in-same-reg/14")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-second-local-var-in-same-reg/15")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-second-local-var-in-same-reg/16")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-read-clobbered-reg-var:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)  # bytes of args in call to convert-mu
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x/ecx: int <- copy 3\n")
+    (write _test-input-stream "  var y/ecx: int <- copy 4\n")
+    (write _test-input-stream "  x <- increment\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-read-clobbered-reg-var: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: register ecx reads var 'x' after writing var 'y'"  "F - test-read-clobbered-reg-var: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-read-clobbered-reg-var: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-overlapping-int-fp-registers:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)  # bytes of args in call to convert-mu
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x/eax: int <- copy 3\n")
+    (write _test-input-stream "  var y/xmm0: float <- convert x\n")
+    (write _test-input-stream "  x <- increment\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # no errors
+    (check-next-stream-line-equal _test-error-stream  ""  "F - test-overlapping-int-fp-registers: error message")
+    # don't bother checking the generated code
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-call:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn main -> _/ebx: int {\n")
+    (write _test-input-stream "  var result/ebx: int <- foo\n")
+    (write _test-input-stream "  return result\n")
+    (write _test-input-stream "}\n")
+    (write _test-input-stream "fn foo -> _/ebx: int {\n")
+    (write _test-input-stream "  var result/ebx: int <- copy 3\n")
+    (write _test-input-stream "  return result\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "main:"                   "F - test-convert-function-call/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-call/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-call/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-call/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-call/4")
+    (check-next-stream-line-equal _test-output-stream "$main:0x00000001:loop:"  "F - test-convert-function-call/5")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ebx"  "F - test-convert-function-call-with-literal-arg/6")
+    (check-next-stream-line-equal _test-output-stream "    (foo)"               "F - test-convert-function-call/6")
+    (check-next-stream-line-equal _test-output-stream "    8b/-> %ebx 0x00000003/r32" "F - test-convert-function-call-with-literal-arg/8")
+    (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 4/imm32"  "F - test-convert-function-call-with-literal-arg/27")
+    (check-next-stream-line-equal _test-output-stream "    e9/jump $main:0x00000001:break/disp32"  "F - test-convert-function-call-with-literal-arg/10")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-call/7")
+    (check-next-stream-line-equal _test-output-stream "$main:0x00000001:break:" "F - test-convert-function-call/8")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-call/9")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-call/10")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-call/11")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-call/12")
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-call/13")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-call/14")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-call/15")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-call/16")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-call/17")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"  "F - test-convert-function-call/18")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ebx"  "F - test-convert-function-call-with-literal-arg/6")
+    (check-next-stream-line-equal _test-output-stream "    bb/copy-to-ebx 3/imm32"  "F - test-convert-function-call/19")
+    (check-next-stream-line-equal _test-output-stream "    8b/-> %ebx 0x00000003/r32" "F - test-convert-function-call-with-literal-arg/8")
+    (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 4/imm32"  "F - test-convert-function-call-with-literal-arg/27")
+    (check-next-stream-line-equal _test-output-stream "    e9/jump $foo:0x00000002:break/disp32"  "F - test-convert-function-call-with-literal-arg/10")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-call/20")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-call/21")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-call/22")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-call/23")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-call/24")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-call/25")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-call-with-inout-with-compound-type:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn f {\n")
+    (write _test-input-stream "  var x: (addr int)\n")
+    (write _test-input-stream "  g x\n")
+    (write _test-input-stream "}\n")
+    (write _test-input-stream "fn g a: (addr int) {\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "f:"                      "F - test-convert-function-call-with-inout-with-compound-type/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-call-with-inout-with-compound-type/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-call-with-inout-with-compound-type/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-call-with-inout-with-compound-type/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-call-with-inout-with-compound-type/4")
+    (check-next-stream-line-equal _test-output-stream "$f:0x00000001:loop:"     "F - test-convert-function-call-with-inout-with-compound-type/5")
+    (check-next-stream-line-equal _test-output-stream "    68/push 0/imm32"     "F - test-convert-function-call-with-inout-with-compound-type/6")
+    (check-next-stream-line-equal _test-output-stream "    (g *(ebp+0xfffffffc))"  "F - test-convert-function-call-with-inout-with-compound-type/7")
+    (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-call-with-inout-with-compound-type/8")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-call-with-inout-with-compound-type/9")
+    (check-next-stream-line-equal _test-output-stream "$f:0x00000001:break:"    "F - test-convert-function-call-with-inout-with-compound-type/10")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-call-with-inout-with-compound-type/11")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-call-with-inout-with-compound-type/12")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-call-with-inout-with-compound-type/13")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-call-with-inout-with-compound-type/14")
+    (check-next-stream-line-equal _test-output-stream "g:"                      "F - test-convert-function-call-with-inout-with-compound-type/15")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-call-with-inout-with-compound-type/16")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-call-with-inout-with-compound-type/17")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-call-with-inout-with-compound-type/18")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-call-with-inout-with-compound-type/19")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-call-with-inout-with-compound-type/20")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-call-with-inout-with-compound-type/21")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-call-with-inout-with-compound-type/22")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-call-with-inout-with-type-parameter:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn f {\n")
+    (write _test-input-stream "  var x: (addr int)\n")
+    (write _test-input-stream "  g x\n")
+    (write _test-input-stream "}\n")
+    (write _test-input-stream "fn g a: (addr _) {\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # no error; types matched
+    (check-stream-equal _test-error-stream  ""  "F - test-convert-function-call-with-inout-with-type-parameter: error stream should be empty")
+    # don't bother checking the generated code; that's in the test 'test-local-clobbered-by-fn-output' below
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-call-with-incorrect-inout-type:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn f {\n")
+    (write _test-input-stream "  var x: int\n")
+    (write _test-input-stream "  g x\n")
+    (write _test-input-stream "}\n")
+    (write _test-input-stream "fn g a: foo {\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-convert-function-call-with-incorrect-inout-type: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn f: call g: type for inout 'x' is not right"  "F - test-convert-function-call-with-incorrect-inout-type: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-convert-function-call-with-incorrect-inout-type: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-call-with-inout-with-incorrect-compound-type:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn f {\n")
+    (write _test-input-stream "  var x: (addr int)\n")
+    (write _test-input-stream "  g x\n")
+    (write _test-input-stream "}\n")
+    (write _test-input-stream "fn g a: (addr bool) {\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-convert-function-call-with-inout-with-incorrect-compound-type: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn f: call g: type for inout 'x' is not right"  "F - test-convert-function-call-with-inout-with-incorrect-compound-type: error message")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-call-with-inout-with-multiple-type-parameters:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn f {\n")
+    (write _test-input-stream "  var x: (addr int)\n")
+    (write _test-input-stream "  var y: (addr int)\n")
+    (write _test-input-stream "  g x, y\n")
+    (write _test-input-stream "}\n")
+    (write _test-input-stream "fn g a: (addr _), b: (addr _) {\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # no errors
+    (check-stream-equal _test-error-stream  ""  "F - test-convert-function-call-with-inout-with-multiple-type-parameters: error stream should be empty")
+    # don't bother checking the generated code
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-type-parameter-matches-rest-of-type:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn f {\n")
+    (write _test-input-stream "  var x: (addr array int)\n")
+    (write _test-input-stream "  g x\n")
+    (write _test-input-stream "}\n")
+    (write _test-input-stream "fn g a: (addr _) {\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # no errors
+    (check-stream-equal _test-error-stream  ""  "F - test-type-parameter-matches-rest-of-type: error stream should be empty")
+    # don't bother checking the generated code
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-call-with-inout-with-incompatible-type-parameters:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn f {\n")
+    (write _test-input-stream "  var x: (addr int)\n")
+    (write _test-input-stream "  var y: (addr boolean)\n")
+    (write _test-input-stream "  g x, y\n")
+    (write _test-input-stream "}\n")
+    (write _test-input-stream "fn g a: (addr _T), b: (addr _T) {\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-convert-function-call-with-inout-with-incompatible-type-parameters: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn f: call g: type for inout 'y' is not right"  "F - test-convert-function-call-with-inout-with-incompatible-type-parameters: error message")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-call-with-too-few-inouts:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn f {\n")
+    (write _test-input-stream "  g\n")
+    (write _test-input-stream "}\n")
+    (write _test-input-stream "fn g a: int {\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-convert-function-call-with-too-few-inouts: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn f: call g: too few inouts"  "F - test-convert-function-call-with-too-few-inouts: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-convert-function-call-with-too-few-inouts: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-call-with-too-many-inouts:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn f {\n")
+    (write _test-input-stream "  var x: int\n")
+    (write _test-input-stream "  g x\n")
+    (write _test-input-stream "}\n")
+    (write _test-input-stream "fn g {\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-convert-function-call-with-too-many-inouts: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn f: call g: too many inouts"  "F - test-convert-function-call-with-too-many-inouts: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-convert-function-call-with-too-many-inouts: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-call-with-incorrect-output-type:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn f {\n")
+    (write _test-input-stream "  var x/eax: int <- g\n")
+    (write _test-input-stream "}\n")
+    (write _test-input-stream "fn g -> _/eax: foo {\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-convert-function-call-with-incorrect-output-type: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn f: call g: type for output 'x' is not right"  "F - test-convert-function-call-with-incorrect-output-type: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-convert-function-call-with-incorrect-output-type: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-call-with-too-few-outputs:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn f {\n")
+    (write _test-input-stream "  g\n")
+    (write _test-input-stream "}\n")
+    (write _test-input-stream "fn g -> _/eax: int {\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-convert-function-call-with-too-few-outputs: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn f: call g: too few outputs"  "F - test-convert-function-call-with-too-few-outputs: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-convert-function-call-with-too-few-outputs: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-call-with-too-many-outputs:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn f {\n")
+    (write _test-input-stream "  var x/eax: int <- g\n")
+    (write _test-input-stream "}\n")
+    (write _test-input-stream "fn g {\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-convert-function-call-with-too-many-outputs: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn f: call g: too many outputs"  "F - test-convert-function-call-with-too-many-outputs: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-convert-function-call-with-too-many-outputs: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-call-with-missing-output-register:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn f {\n")
+    (write _test-input-stream "  var x: int\n")
+    (write _test-input-stream "  x <- g\n")
+    (write _test-input-stream "}\n")
+    (write _test-input-stream "fn g -> _/eax: int {\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-convert-function-call-with-missing-output-register: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn f: call g: output 'x' is not in a register"  "F - test-convert-function-call-with-missing-output-register: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-convert-function-call-with-missing-output-register: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-call-with-incorrect-output-register:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn f {\n")
+    (write _test-input-stream "  var x/ecx: int <- g\n")
+    (write _test-input-stream "}\n")
+    (write _test-input-stream "fn g -> _/eax: int {\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-convert-function-call-with-incorrect-output-register: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn f: call g: register for output 'x' is not right"  "F - test-convert-function-call-with-incorrect-output-register: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-convert-function-call-with-incorrect-output-register: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-with-local-var-dereferenced:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x/ecx: (addr int) <- copy 0\n")
+    (write _test-input-stream "  increment *x\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-local-var-dereferenced/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-local-var-dereferenced/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-local-var-dereferenced/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-local-var-dereferenced/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-local-var-dereferenced/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-local-var-dereferenced/5")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-convert-function-with-local-var-dereferenced/6")
+    (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 0/imm32"  "F - test-convert-function-with-local-var-dereferenced/7")
+    (check-next-stream-line-equal _test-output-stream "    ff 0/subop/increment *ecx"  "F - test-convert-function-with-local-var-dereferenced/8")
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx" "F - test-convert-function-with-local-var-dereferenced/9")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-local-var-dereferenced/10")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-local-var-dereferenced/11")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-local-var-dereferenced/12")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-local-var-dereferenced/13")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-local-var-dereferenced/14")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-local-var-dereferenced/15")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-dereference-of-var-on-stack:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x: (addr int)\n")
+    (write _test-input-stream "  increment *x\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-dereference-of-var-on-stack: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: cannot dereference var 'x' on stack"  "F - test-dereference-of-var-on-stack: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-dereference-of-var-on-stack: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-with-byte-operations:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x/eax: byte <- copy 0\n")
+    (write _test-input-stream "  var y/ecx: byte <- copy 0\n")
+    (write _test-input-stream "  y <- copy-byte x\n")
+    (write _test-input-stream "  var z/edx: (addr byte) <- copy 0\n")
+    (write _test-input-stream "  y <- copy-byte *z\n")
+    (write _test-input-stream "  copy-byte-to *z, x\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                                        "F - test-convert-function-with-byte-operations/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"                              "F - test-convert-function-with-byte-operations/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"                               "F - test-convert-function-with-byte-operations/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"                      "F - test-convert-function-with-byte-operations/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                                         "F - test-convert-function-with-byte-operations/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"                       "F - test-convert-function-with-byte-operations/5")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"                    "F - test-convert-function-with-byte-operations/6")
+    (check-next-stream-line-equal _test-output-stream "    b8/copy-to-eax 0/imm32"                  "F - test-convert-function-with-byte-operations/7")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"                    "F - test-convert-function-with-byte-operations/8")
+    (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 0/imm32"                  "F - test-convert-function-with-byte-operations/9")
+    (check-next-stream-line-equal _test-output-stream "    8a/byte-> %eax 0x00000001/r32"           "F - test-convert-function-with-byte-operations/10")
+    (check-next-stream-line-equal _test-output-stream "    81 4/subop/and %ecx 0xff/imm32"          "F - test-convert-function-with-byte-operations/11")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %edx"                    "F - test-convert-function-with-byte-operations/12")
+    (check-next-stream-line-equal _test-output-stream "    ba/copy-to-edx 0/imm32"                  "F - test-convert-function-with-byte-operations/13")
+    (check-next-stream-line-equal _test-output-stream "    8a/byte-> *edx 0x00000001/r32"           "F - test-convert-function-with-byte-operations/14")
+    (check-next-stream-line-equal _test-output-stream "    81 4/subop/and %ecx 0xff/imm32"          "F - test-convert-function-with-byte-operations/15")
+    (check-next-stream-line-equal _test-output-stream "    88/byte<- *edx 0x00000000/r32"           "F - test-convert-function-with-byte-operations/16")
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %edx"                     "F - test-convert-function-with-byte-operations/17")
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx"                     "F - test-convert-function-with-byte-operations/18")
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"                     "F - test-convert-function-with-byte-operations/19")
+    (check-next-stream-line-equal _test-output-stream "  }"                                         "F - test-convert-function-with-byte-operations/20")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"                      "F - test-convert-function-with-byte-operations/21")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"                              "F - test-convert-function-with-byte-operations/22")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"                      "F - test-convert-function-with-byte-operations/23")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"                             "F - test-convert-function-with-byte-operations/24")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"                                 "F - test-convert-function-with-byte-operations/25")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+# variables of type 'byte' are not allowed on the stack
+test-byte-values-on-stack:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x: byte\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-byte-values-on-stack: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: var 'x' of type 'byte' cannot be on the stack"  "F - test-byte-values-on-stack: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-byte-values-on-stack: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+# variables of type 'byte' are not allowed in esi or edi
+test-byte-values-in-unsupported-registers:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x/esi: byte <- copy 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-byte-values-in-unsupported-registers: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: var 'x' of type 'byte' cannot be in esi or edi"  "F - test-byte-values-in-unsupported-registers: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-byte-values-in-unsupported-registers: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+# variables of type 'byte' _can_ be function args. They then occupy 4 bytes.
+test-copy-byte-var-from-fn-arg:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo x: byte, y: int {\n")
+    (write _test-input-stream "  var a/eax: byte <- copy x\n")
+    (write _test-input-stream "  var b/eax: int <- copy y\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-copy-byte-from-fn-arg/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-copy-byte-from-fn-arg/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-copy-byte-from-fn-arg/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-copy-byte-from-fn-arg/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-copy-byte-from-fn-arg/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-copy-byte-from-fn-arg/5")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"  "F - test-copy-byte-from-fn-arg/6")
+    (check-next-stream-line-equal _test-output-stream "    8b/-> *(ebp+0x00000008) 0x00000000/r32"  "F - test-copy-byte-from-fn-arg/7")
+    (check-next-stream-line-equal _test-output-stream "    8b/-> *(ebp+0x0000000c) 0x00000000/r32"  "F - test-copy-byte-from-fn-arg/8")
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"   "F - test-copy-byte-from-fn-arg/9")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-copy-byte-from-fn-arg/10")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-copy-byte-from-fn-arg/11")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-copy-byte-from-fn-arg/12")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-copy-byte-from-fn-arg/13")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-copy-byte-from-fn-arg/14")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-copy-byte-from-fn-arg/15")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-compare-register-with-literal:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x/ecx: int <- copy 0\n")
+    (write _test-input-stream "  compare x, 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-compare-register-with-literal/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-compare-register-with-literal/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-compare-register-with-literal/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-compare-register-with-literal/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-compare-register-with-literal/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-compare-register-with-literal/5")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-convert-compare-register-with-literal/6")
+    (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 0/imm32"  "F - test-convert-compare-register-with-literal/7")
+    (check-next-stream-line-equal _test-output-stream "    81 7/subop/compare %ecx 0/imm32"  "F - test-convert-compare-register-with-literal/8")
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx" "F - test-convert-compare-register-with-literal/9")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-compare-register-with-literal/10")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-compare-register-with-literal/11")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-compare-register-with-literal/12")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-compare-register-with-literal/13")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-compare-register-with-literal/14")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-compare-register-with-literal/15")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-compare-byte-with-literal:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x/ecx: byte <- copy 0\n")
+    (write _test-input-stream "  compare x, 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # no errors; output is identical to test-convert-compare-register-with-literal
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-unknown-variable:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  compare x, 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-unknown-variable: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: unknown variable 'x'"  "F - test-unknown-variable: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-unknown-variable: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-with-local-var-in-block:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  {\n")
+    (write _test-input-stream "    var x: int\n")
+    (write _test-input-stream "    increment x\n")
+    (write _test-input-stream "  }\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-local-var-in-block/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-local-var-in-block/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-local-var-in-block/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-local-var-in-block/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-local-var-in-block/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-local-var-in-block/5")
+    (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-local-var-in-block/6")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-local-var-in-block/7")
+    (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-local-var-in-block/8")
+    (check-next-stream-line-equal _test-output-stream "      ff 0/subop/increment *(ebp+0xfffffffc)"  "F - test-convert-function-with-local-var-in-block/9")
+    (check-next-stream-line-equal _test-output-stream "      81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-local-var-in-block/10")
+    (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-local-var-in-block/11")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-local-var-in-block/12")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-local-var-in-block/13")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-local-var-in-block/14")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-local-var-in-block/15")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-local-var-in-block/16")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-local-var-in-block/17")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-local-var-in-block/18")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-with-local-var-in-mem-after-block:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  {\n")
+    (write _test-input-stream "    var y: int\n")
+    (write _test-input-stream "  }\n")
+    (write _test-input-stream "  var x: int\n")
+    (write _test-input-stream "  increment x\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-local-var-in-mem-after-block/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-local-var-in-mem-after-block/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-local-var-in-mem-after-block/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-local-var-in-mem-after-block/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-local-var-in-mem-after-block/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-local-var-in-mem-after-block/5")
+    (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-local-var-in-mem-after-block/6")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-local-var-in-mem-after-block/7")
+    (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-local-var-in-mem-after-block/8")
+    (check-next-stream-line-equal _test-output-stream "      81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-local-var-in-mem-after-block/9")
+    (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-local-var-in-mem-after-block/10")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-local-var-in-mem-after-block/11")
+    (check-next-stream-line-equal _test-output-stream "    68/push 0/imm32"     "F - test-convert-function-with-local-var-in-mem-after-block/12")
+    (check-next-stream-line-equal _test-output-stream "    ff 0/subop/increment *(ebp+0xfffffffc)"  "F - test-convert-function-with-local-var-in-mem-after-block/13")
+    (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-local-var-in-mem-after-block/14")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-local-var-in-mem-after-block/15")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-local-var-in-mem-after-block/16")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-local-var-in-mem-after-block/17")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-local-var-in-mem-after-block/18")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-local-var-in-mem-after-block/19")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-local-var-in-mem-after-block/20")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-with-local-var-in-named-block:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  $bar: {\n")
+    (write _test-input-stream "    var x: int\n")
+    (write _test-input-stream "    increment x\n")
+    (write _test-input-stream "  }\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-local-var-in-named-block/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-local-var-in-named-block/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-local-var-in-named-block/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-local-var-in-named-block/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-local-var-in-named-block/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-local-var-in-named-block/5")
+    (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-local-var-in-named-block/6")
+    (check-next-stream-line-equal _test-output-stream "$bar:loop:"              "F - test-convert-function-with-local-var-in-named-block/7")
+    (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-local-var-in-named-block/8")
+    (check-next-stream-line-equal _test-output-stream "      ff 0/subop/increment *(ebp+0xfffffffc)"  "F - test-convert-function-with-local-var-in-named-block/9")
+    (check-next-stream-line-equal _test-output-stream "      81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-local-var-in-named-block/10")
+    (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-local-var-in-named-block/11")
+    (check-next-stream-line-equal _test-output-stream "$bar:break:"             "F - test-convert-function-with-local-var-in-named-block/12")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-local-var-in-named-block/13")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-local-var-in-named-block/14")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-local-var-in-named-block/15")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-local-var-in-named-block/16")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-local-var-in-named-block/17")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-local-var-in-named-block/18")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-unknown-variable-in-named-block:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  $a: {\n")
+    (write _test-input-stream "    compare x, 0\n")
+    (write _test-input-stream "  }\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-unknown-variable-in-named-block: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: unknown variable 'x'"  "F - test-unknown-variable-in-named-block: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-unknown-variable-in-named-block: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-always-shadow-outermost-reg-vars-in-function:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x/ecx: int <- copy 3\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-always-shadow-outermost-reg-vars-in-function/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-always-shadow-outermost-reg-vars-in-function/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-always-shadow-outermost-reg-vars-in-function/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-always-shadow-outermost-reg-vars-in-function/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-always-shadow-outermost-reg-vars-in-function/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-always-shadow-outermost-reg-vars-in-function/5")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-convert-compare-register-with-literal/6")
+    (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 3/imm32"  "F - test-always-shadow-outermost-reg-vars-in-function/8")
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx" "F - test-convert-compare-register-with-literal/9")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-always-shadow-outermost-reg-vars-in-function/12")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-always-shadow-outermost-reg-vars-in-function/13")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-always-shadow-outermost-reg-vars-in-function/14")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-always-shadow-outermost-reg-vars-in-function/15")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-always-shadow-outermost-reg-vars-in-function/16")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-always-shadow-outermost-reg-vars-in-function/17")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-shadow-local:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x/ecx: int <- copy 3\n")
+    (write _test-input-stream "  {\n")
+    (write _test-input-stream "    var y/ecx: int <- copy 4\n")
+    (write _test-input-stream "  }\n")
+    (write _test-input-stream "  x <- increment\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-shadow-local/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-shadow-local/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-shadow-local/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-shadow-local/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-shadow-local/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-shadow-local/5")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-shadow-local/6")
+    (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 3/imm32"  "F - test-shadow-local/7")
+    (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-shadow-local/8")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-shadow-local/9")
+    (check-next-stream-line-equal _test-output-stream "      ff 6/subop/push %ecx"  "F - test-shadow-local/10")
+    (check-next-stream-line-equal _test-output-stream "      b9/copy-to-ecx 4/imm32"  "F - test-shadow-local/11")
+    (check-next-stream-line-equal _test-output-stream "      8f 0/subop/pop %ecx" "F - test-shadow-local/12")
+    (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-shadow-local/13")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-shadow-local/14")
+    (check-next-stream-line-equal _test-output-stream "    41/increment-ecx"    "F - test-shadow-local/15")
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx" "F - test-shadow-local/16")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-shadow-local/17")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-shadow-local/18")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-shadow-local/19")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-shadow-local/20")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-shadow-local/21")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-shadow-local/22")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-shadow-name:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x/ecx: int <- copy 3\n")
+    (write _test-input-stream "  {\n")
+    (write _test-input-stream "    var x/edx: int <- copy 4\n")
+    (write _test-input-stream "  }\n")
+    (write _test-input-stream "  x <- increment\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-shadow-name/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-shadow-name/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-shadow-name/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-shadow-name/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-shadow-name/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-shadow-name/5")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-shadow-name/6")
+    (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 3/imm32"  "F - test-shadow-name/7")
+    (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-shadow-name/8")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-shadow-name/9")
+    (check-next-stream-line-equal _test-output-stream "      ff 6/subop/push %edx"  "F - test-shadow-name/10")
+    (check-next-stream-line-equal _test-output-stream "      ba/copy-to-edx 4/imm32"  "F - test-shadow-name/11")
+    (check-next-stream-line-equal _test-output-stream "      8f 0/subop/pop %edx" "F - test-shadow-name/12")
+    (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-shadow-name/13")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-shadow-name/14")
+    (check-next-stream-line-equal _test-output-stream "    41/increment-ecx"    "F - test-shadow-name/15")
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx" "F - test-shadow-name/16")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-shadow-name/17")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-shadow-name/18")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-shadow-name/19")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-shadow-name/20")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-shadow-name/21")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-shadow-name/22")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-shadow-name-2:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x/ecx: int <- copy 3\n")
+    (write _test-input-stream "  {\n")
+    (write _test-input-stream "    var x/edx: int <- copy 4\n")
+    (write _test-input-stream "    var y/ecx: int <- copy 5\n")
+    (write _test-input-stream "  }\n")
+    (write _test-input-stream "  x <- increment\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-shadow-name-2/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-shadow-name-2/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-shadow-name-2/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-shadow-name-2/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-shadow-name-2/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-shadow-name-2/5")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-shadow-name-2/6")
+    (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 3/imm32"  "F - test-shadow-name-2/7")
+    (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-shadow-name-2/8")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-shadow-name-2/9")
+    (check-next-stream-line-equal _test-output-stream "      ff 6/subop/push %edx"  "F - test-shadow-name-2/10")
+    (check-next-stream-line-equal _test-output-stream "      ba/copy-to-edx 4/imm32"  "F - test-shadow-name-2/11")
+    (check-next-stream-line-equal _test-output-stream "      ff 6/subop/push %ecx"  "F - test-shadow-name-2/12")
+    (check-next-stream-line-equal _test-output-stream "      b9/copy-to-ecx 5/imm32"  "F - test-shadow-name-2/13")
+    (check-next-stream-line-equal _test-output-stream "      8f 0/subop/pop %ecx" "F - test-shadow-name-2/14")
+    (check-next-stream-line-equal _test-output-stream "      8f 0/subop/pop %edx" "F - test-shadow-name-2/15")
+    (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-shadow-name-2/16")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-shadow-name-2/17")
+    (check-next-stream-line-equal _test-output-stream "    41/increment-ecx"    "F - test-shadow-name-2/18")
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx" "F - test-shadow-name-2/19")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-shadow-name-2/20")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-shadow-name-2/21")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-shadow-name-2/22")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-shadow-name-2/23")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-shadow-name-2/24")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-shadow-name-2/25")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-do-not-spill-same-register-in-block:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x/ecx: int <- copy 3\n")
+    (write _test-input-stream "  var y/ecx: int <- copy 4\n")
+    (write _test-input-stream "  y <- increment\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-do-not-spill-same-register-in-block/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-do-not-spill-same-register-in-block/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-do-not-spill-same-register-in-block/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-do-not-spill-same-register-in-block/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-do-not-spill-same-register-in-block/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-do-not-spill-same-register-in-block/5")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-do-not-spill-same-register-in-block/6")
+    (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 3/imm32"  "F - test-do-not-spill-same-register-in-block/7")
+    (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 4/imm32"  "F - test-do-not-spill-same-register-in-block/8")
+    (check-next-stream-line-equal _test-output-stream "    41/increment-ecx"    "F - test-do-not-spill-same-register-in-block/9")
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx" "F - test-do-not-spill-same-register-in-block/10")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-do-not-spill-same-register-in-block/11")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-do-not-spill-same-register-in-block/12")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-do-not-spill-same-register-in-block/13")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-do-not-spill-same-register-in-block/14")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-do-not-spill-same-register-in-block/15")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-do-not-spill-same-register-in-block/16")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-spill-different-register-in-block:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x/eax: int <- copy 3\n")
+    (write _test-input-stream "  var y/ecx: int <- copy 4\n")
+    (write _test-input-stream "  y <- increment\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-spill-different-register-in-block/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-spill-different-register-in-block/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-spill-different-register-in-block/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-spill-different-register-in-block/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-spill-different-register-in-block/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-spill-different-register-in-block/5")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"  "F - test-spill-different-register-in-block/6")
+    (check-next-stream-line-equal _test-output-stream "    b8/copy-to-eax 3/imm32"  "F - test-spill-different-register-in-block/7")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-spill-different-register-in-block/8")
+    (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 4/imm32"  "F - test-spill-different-register-in-block/9")
+    (check-next-stream-line-equal _test-output-stream "    41/increment-ecx"    "F - test-spill-different-register-in-block/10")
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx" "F - test-spill-different-register-in-block/11")
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax" "F - test-spill-different-register-in-block/12")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-spill-different-register-in-block/13")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-spill-different-register-in-block/14")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-spill-different-register-in-block/15")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-spill-different-register-in-block/16")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-spill-different-register-in-block/17")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-spill-different-register-in-block/18")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-with-branches-in-block:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo x: int {\n")
+    (write _test-input-stream "  {\n")
+    (write _test-input-stream "    break-if->=\n")
+    (write _test-input-stream "    loop-if-addr<\n")
+    (write _test-input-stream "    increment x\n")
+    (write _test-input-stream "    loop\n")
+    (write _test-input-stream "  }\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-branches-in-block/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-branches-in-block/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-branches-in-block/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-branches-in-block/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-branches-in-block/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-branches-in-block/5")
+    (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-branches-in-block/6")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-branches-in-block/7")
+    (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-branches-in-block/8")
+    (check-next-stream-line-equal _test-output-stream "        0f 8c/jump-if-< break/disp32"  "F - test-convert-function-with-branches-in-block/9")
+    (check-next-stream-line-equal _test-output-stream "        e9/jump $foo:0x00000002:break/disp32"  "F - test-convert-function-with-branches-in-block/10")
+    (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-branches-in-block/11")
+    (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-branches-in-block/12")
+    (check-next-stream-line-equal _test-output-stream "        0f 83/jump-if-addr>= break/disp32"  "F - test-convert-function-with-branches-in-block/13")
+    (check-next-stream-line-equal _test-output-stream "        e9/jump $foo:0x00000002:loop/disp32"  "F - test-convert-function-with-branches-in-block/14")
+    (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-branches-in-block/15")
+    (check-next-stream-line-equal _test-output-stream "      ff 0/subop/increment *(ebp+0x00000008)"  "F - test-convert-function-with-branches-in-block/16")
+    (check-next-stream-line-equal _test-output-stream "      e9/jump loop/disp32"  "F - test-convert-function-with-branches-in-block/17")
+    (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-branches-in-block/18")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-branches-in-block/19")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-branches-in-block/20")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-branches-in-block/21")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-branches-in-block/22")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-branches-in-block/23")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-branches-in-block/24")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-branches-in-block/25")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-with-branches-in-block-2:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo x: int {\n")
+    (write _test-input-stream "  {\n")
+    (write _test-input-stream "    break-if->=\n")
+    (write _test-input-stream "    loop-if-float<\n")
+    (write _test-input-stream "    increment x\n")
+    (write _test-input-stream "    loop\n")
+    (write _test-input-stream "  }\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-branches-in-block/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-branches-in-block/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-branches-in-block/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-branches-in-block/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-branches-in-block/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-branches-in-block/5")
+    (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-branches-in-block/6")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-branches-in-block/7")
+    (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-branches-in-block/8")
+    (check-next-stream-line-equal _test-output-stream "        0f 8c/jump-if-< break/disp32"  "F - test-convert-function-with-branches-in-block/9")
+    (check-next-stream-line-equal _test-output-stream "        e9/jump $foo:0x00000002:break/disp32"  "F - test-convert-function-with-branches-in-block/10")
+    (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-branches-in-block/11")
+    (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-branches-in-block/12")
+    (check-next-stream-line-equal _test-output-stream "        0f 83/jump-if-addr>= break/disp32"  "F - test-convert-function-with-branches-in-block/13")
+    (check-next-stream-line-equal _test-output-stream "        e9/jump $foo:0x00000002:loop/disp32"  "F - test-convert-function-with-branches-in-block/14")
+    (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-branches-in-block/15")
+    (check-next-stream-line-equal _test-output-stream "      ff 0/subop/increment *(ebp+0x00000008)"  "F - test-convert-function-with-branches-in-block/16")
+    (check-next-stream-line-equal _test-output-stream "      e9/jump loop/disp32"  "F - test-convert-function-with-branches-in-block/17")
+    (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-branches-in-block/18")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-branches-in-block/19")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-branches-in-block/20")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-branches-in-block/21")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-branches-in-block/22")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-branches-in-block/23")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-branches-in-block/24")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-branches-in-block/25")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-with-branches-in-named-block:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo x: int {\n")
+    (write _test-input-stream "  $bar: {\n")
+    (write _test-input-stream "    break-if->= $bar\n")
+    (write _test-input-stream "    loop-if-addr< $bar\n")
+    (write _test-input-stream "    increment x\n")
+    (write _test-input-stream "    loop\n")
+    (write _test-input-stream "  }\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-branches-in-named-block/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-branches-in-named-block/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-branches-in-named-block/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-branches-in-named-block/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-branches-in-named-block/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-branches-in-named-block/5")
+    (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-branches-in-named-block/6")
+    (check-next-stream-line-equal _test-output-stream "$bar:loop:"              "F - test-convert-function-with-branches-in-named-block/7")
+    (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-branches-in-named-block/8")
+    (check-next-stream-line-equal _test-output-stream "        0f 8c/jump-if-< break/disp32"  "F - test-convert-function-with-branches-in-named-block/9")
+    (check-next-stream-line-equal _test-output-stream "        e9/jump $bar:break/disp32"  "F - test-convert-function-with-branches-in-named-block/10")
+    (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-branches-in-named-block/11")
+    (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-branches-in-named-block/12")
+    (check-next-stream-line-equal _test-output-stream "        0f 83/jump-if-addr>= break/disp32"  "F - test-convert-function-with-branches-in-named-block/13")
+    (check-next-stream-line-equal _test-output-stream "        e9/jump $bar:loop/disp32"  "F - test-convert-function-with-branches-in-named-block/14")
+    (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-branches-in-named-block/15")
+    (check-next-stream-line-equal _test-output-stream "      ff 0/subop/increment *(ebp+0x00000008)"  "F - test-convert-function-with-branches-in-named-block/16")
+    (check-next-stream-line-equal _test-output-stream "      e9/jump loop/disp32"   "F - test-convert-function-with-branches-in-named-block/17")
+    (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-branches-in-named-block/18")
+    (check-next-stream-line-equal _test-output-stream "$bar:break:"             "F - test-convert-function-with-branches-in-named-block/19")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-branches-in-named-block/20")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-branches-in-named-block/21")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-branches-in-named-block/22")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-branches-in-named-block/23")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-branches-in-named-block/24")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-branches-in-named-block/25")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-with-var-in-nested-block:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo x: int {\n")
+    (write _test-input-stream "  {\n")
+    (write _test-input-stream "    {\n")
+    (write _test-input-stream "      var x: int\n")
+    (write _test-input-stream "      increment x\n")
+    (write _test-input-stream "    }\n")
+    (write _test-input-stream "  }\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-var-in-nested-block/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-var-in-nested-block/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-var-in-nested-block/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-var-in-nested-block/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-var-in-nested-block/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-var-in-nested-block/5")
+    (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-var-in-nested-block/6")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-var-in-nested-block/7")
+    (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-var-in-nested-block/8")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:loop:"   "F - test-convert-function-with-var-in-nested-block/9")
+    (check-next-stream-line-equal _test-output-stream "        68/push 0/imm32" "F - test-convert-function-with-var-in-nested-block/10")
+    (check-next-stream-line-equal _test-output-stream "        ff 0/subop/increment *(ebp+0xfffffffc)"  "F - test-convert-function-with-var-in-nested-block/11")
+    (check-next-stream-line-equal _test-output-stream "        81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-var-in-nested-block/12")
+    (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-var-in-nested-block/13")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:break:"  "F - test-convert-function-with-var-in-nested-block/14")
+    (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-var-in-nested-block/15")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-var-in-nested-block/16")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-var-in-nested-block/17")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-var-in-nested-block/18")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-var-in-nested-block/19")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-var-in-nested-block/20")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-var-in-nested-block/21")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-var-in-nested-block/22")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-with-multiple-vars-in-nested-blocks:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo x: int {\n")
+    (write _test-input-stream "  {\n")
+    (write _test-input-stream "    var x/eax: int <- copy 0\n")
+    (write _test-input-stream "    {\n")
+    (write _test-input-stream "      var y: int\n")
+    (write _test-input-stream "      x <- add y\n")
+    (write _test-input-stream "    }\n")
+    (write _test-input-stream "  }\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-multiple-vars-in-nested-blocks/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-multiple-vars-in-nested-blocks/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-multiple-vars-in-nested-blocks/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-multiple-vars-in-nested-blocks/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-multiple-vars-in-nested-blocks/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-multiple-vars-in-nested-blocks/5")
+    (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-multiple-vars-in-nested-blocks/6")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-multiple-vars-in-nested-blocks/7")
+    (check-next-stream-line-equal _test-output-stream "      ff 6/subop/push %eax"  "F - test-convert-function-with-multiple-vars-in-nested-blocks/8")
+    (check-next-stream-line-equal _test-output-stream "      b8/copy-to-eax 0/imm32"  "F - test-convert-function-with-multiple-vars-in-nested-blocks/9")
+    (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-multiple-vars-in-nested-blocks/10")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:loop:"   "F - test-convert-function-with-multiple-vars-in-nested-blocks/11")
+    (check-next-stream-line-equal _test-output-stream "        68/push 0/imm32" "F - test-convert-function-with-multiple-vars-in-nested-blocks/12")
+    (check-next-stream-line-equal _test-output-stream "        03/add *(ebp+0xfffffff8) 0x00000000/r32"  "F - test-convert-function-with-multiple-vars-in-nested-blocks/13")
+    (check-next-stream-line-equal _test-output-stream "        81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-multiple-vars-in-nested-blocks/14")
+    (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-multiple-vars-in-nested-blocks/15")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:break:"  "F - test-convert-function-with-multiple-vars-in-nested-blocks/16")
+    (check-next-stream-line-equal _test-output-stream "      8f 0/subop/pop %eax"   "F - test-convert-function-with-multiple-vars-in-nested-blocks/17")
+    (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-multiple-vars-in-nested-blocks/18")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-multiple-vars-in-nested-blocks/19")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-multiple-vars-in-nested-blocks/20")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-multiple-vars-in-nested-blocks/21")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-multiple-vars-in-nested-blocks/22")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-multiple-vars-in-nested-blocks/23")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-multiple-vars-in-nested-blocks/24")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-multiple-vars-in-nested-blocks/25")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-with-branches-and-local-vars:
+    # A conditional 'break' after a 'var' in a block is converted into a
+    # nested block that performs all necessary cleanup before jumping. This
+    # results in some ugly code duplication.
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  {\n")
+    (write _test-input-stream "    var x: int\n")
+    (write _test-input-stream "    break-if->=\n")
+    (write _test-input-stream "    increment x\n")
+    (write _test-input-stream "  }\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-branches-and-local-vars/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-branches-and-local-vars/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-branches-and-local-vars/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-branches-and-local-vars/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-branches-and-local-vars/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-branches-and-local-vars/5")
+    (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-branches-and-local-vars/6")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-branches-and-local-vars/7")
+    (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-branches-and-local-vars/8")
+    (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-branches-and-local-vars/9")
+    (check-next-stream-line-equal _test-output-stream "        0f 8c/jump-if-< break/disp32"  "F - test-convert-function-with-branches-and-local-vars/10")
+    (check-next-stream-line-equal _test-output-stream "        81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-branches-and-local-vars/11")
+    (check-next-stream-line-equal _test-output-stream "        e9/jump $foo:0x00000002:break/disp32"  "F - test-convert-function-with-branches-and-local-vars/12")
+    (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-branches-and-local-vars/13")
+    (check-next-stream-line-equal _test-output-stream "      ff 0/subop/increment *(ebp+0xfffffffc)"  "F - test-convert-function-with-branches-and-local-vars/14")
+    (check-next-stream-line-equal _test-output-stream "      81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-branches-and-local-vars/15")
+    (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-branches-and-local-vars/16")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-branches-and-local-vars/17")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-branches-and-local-vars/18")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-branches-and-local-vars/19")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-branches-and-local-vars/20")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-branches-and-local-vars/21")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-branches-and-local-vars/22")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-branches-and-local-vars/23")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-with-conditional-loops-and-local-vars:
+    # A conditional 'loop' after a 'var' in a block is converted into a nested
+    # block that performs all necessary cleanup before jumping. This results
+    # in some ugly code duplication.
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  {\n")
+    (write _test-input-stream "    var x: int\n")
+    (write _test-input-stream "    loop-if->=\n")
+    (write _test-input-stream "    increment x\n")
+    (write _test-input-stream "  }\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-conditional-loops-and-local-vars/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-conditional-loops-and-local-vars/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-conditional-loops-and-local-vars/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-conditional-loops-and-local-vars/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-conditional-loops-and-local-vars/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-conditional-loops-and-local-vars/5")
+    (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-conditional-loops-and-local-vars/6")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-conditional-loops-and-local-vars/7")
+    (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-conditional-loops-and-local-vars/8")
+    (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-conditional-loops-and-local-vars/9")
+    (check-next-stream-line-equal _test-output-stream "        0f 8c/jump-if-< break/disp32"  "F - test-convert-function-with-conditional-loops-and-local-vars/10")
+    (check-next-stream-line-equal _test-output-stream "        81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-conditional-loops-and-local-vars/11")
+    (check-next-stream-line-equal _test-output-stream "        e9/jump $foo:0x00000002:loop/disp32"  "F - test-convert-function-with-conditional-loops-and-local-vars/12")
+    (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-conditional-loops-and-local-vars/13")
+    (check-next-stream-line-equal _test-output-stream "      ff 0/subop/increment *(ebp+0xfffffffc)"  "F - test-convert-function-with-conditional-loops-and-local-vars/14")
+    (check-next-stream-line-equal _test-output-stream "      81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-conditional-loops-and-local-vars/15")
+    (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-conditional-loops-and-local-vars/16")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-conditional-loops-and-local-vars/17")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-conditional-loops-and-local-vars/18")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-conditional-loops-and-local-vars/19")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-conditional-loops-and-local-vars/20")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-conditional-loops-and-local-vars/21")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-conditional-loops-and-local-vars/22")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-conditional-loops-and-local-vars/23")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-with-unconditional-loops-and-local-vars:
+    # An unconditional 'loop' after a 'var' in a block is emitted _after_ the
+    # regular block cleanup. Any instructions after 'loop' are dead and
+    # therefore skipped.
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  {\n")
+    (write _test-input-stream "    var x: int\n")
+    (write _test-input-stream "    loop\n")
+    (write _test-input-stream "    increment x\n")
+    (write _test-input-stream "  }\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-unconditional-loops-and-local-vars/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-unconditional-loops-and-local-vars/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-unconditional-loops-and-local-vars/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-unconditional-loops-and-local-vars/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-unconditional-loops-and-local-vars/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-unconditional-loops-and-local-vars/5")
+    (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-unconditional-loops-and-local-vars/6")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-unconditional-loops-and-local-vars/7")
+    (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-unconditional-loops-and-local-vars/8")
+    (check-next-stream-line-equal _test-output-stream "      81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-unconditional-loops-and-local-vars/9")
+    (check-next-stream-line-equal _test-output-stream "      e9/jump loop/disp32"  "F - test-convert-function-with-unconditional-loops-and-local-vars/10")
+    # not emitted:                                           ff 0/subop/increment *(ebp+0xfffffffc)
+    (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-unconditional-loops-and-local-vars/11")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-unconditional-loops-and-local-vars/12")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-unconditional-loops-and-local-vars/13")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-unconditional-loops-and-local-vars/14")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-unconditional-loops-and-local-vars/15")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-unconditional-loops-and-local-vars/16")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-unconditional-loops-and-local-vars/17")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-unconditional-loops-and-local-vars/18")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-with-branches-and-loops-and-local-vars:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  {\n")
+    (write _test-input-stream "    var x: int\n")
+    (write _test-input-stream "    break-if->=\n")
+    (write _test-input-stream "    increment x\n")
+    (write _test-input-stream "    loop\n")
+    (write _test-input-stream "  }\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-branches-and-loops-and-local-vars/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-branches-and-loops-and-local-vars/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-branches-and-loops-and-local-vars/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-branches-and-loops-and-local-vars/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-branches-and-loops-and-local-vars/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-branches-and-loops-and-local-vars/5")
+    (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-branches-and-loops-and-local-vars/6")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-branches-and-loops-and-local-vars/7")
+    (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-branches-and-loops-and-local-vars/8")
+    (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-branches-and-loops-and-local-vars/9")
+    (check-next-stream-line-equal _test-output-stream "        0f 8c/jump-if-< break/disp32"  "F - test-convert-function-with-branches-and-loops-and-local-vars/10")
+    (check-next-stream-line-equal _test-output-stream "        81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-branches-and-loops-and-local-vars/11")
+    (check-next-stream-line-equal _test-output-stream "        e9/jump $foo:0x00000002:break/disp32"  "F - test-convert-function-with-branches-and-loops-and-local-vars/12")
+    (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-branches-and-loops-and-local-vars/13")
+    (check-next-stream-line-equal _test-output-stream "      ff 0/subop/increment *(ebp+0xfffffffc)"  "F - test-convert-function-with-branches-and-loops-and-local-vars/14")
+    (check-next-stream-line-equal _test-output-stream "      81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-branches-and-loops-and-local-vars/15")
+    (check-next-stream-line-equal _test-output-stream "      e9/jump loop/disp32"  "F - test-convert-function-with-branches-and-loops-and-local-vars/16")
+    (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-branches-and-loops-and-local-vars/17")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-branches-and-loops-and-local-vars/18")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-branches-and-loops-and-local-vars/19")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-branches-and-loops-and-local-vars/20")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-branches-and-loops-and-local-vars/21")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-branches-and-loops-and-local-vars/22")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-branches-and-loops-and-local-vars/23")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-branches-and-loops-and-local-vars/24")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-with-nonlocal-branches-and-loops-and-local-vars:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  a: {\n")
+    (write _test-input-stream "    var x: int\n")
+    (write _test-input-stream "    {\n")
+    (write _test-input-stream "      var y: int\n")
+    (write _test-input-stream "      break-if->= a\n")
+    (write _test-input-stream "      increment x\n")
+    (write _test-input-stream "      loop\n")
+    (write _test-input-stream "    }\n")
+    (write _test-input-stream "  }\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/5")
+    (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/6")
+    (check-next-stream-line-equal _test-output-stream "a:loop:"                 "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/7")
+    (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/8")
+    (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/9")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:loop:"   "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/10")
+    (check-next-stream-line-equal _test-output-stream "        68/push 0/imm32" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/11")
+    (check-next-stream-line-equal _test-output-stream "        {"               "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/12")
+    (check-next-stream-line-equal _test-output-stream "          0f 8c/jump-if-< break/disp32"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/13")
+    (check-next-stream-line-equal _test-output-stream "          81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/14")
+    (check-next-stream-line-equal _test-output-stream "          81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/15")
+    (check-next-stream-line-equal _test-output-stream "          e9/jump a:break/disp32"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/16")
+    (check-next-stream-line-equal _test-output-stream "        }"               "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/17")
+    (check-next-stream-line-equal _test-output-stream "        ff 0/subop/increment *(ebp+0xfffffffc)"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/18")
+    (check-next-stream-line-equal _test-output-stream "        81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/19")
+    (check-next-stream-line-equal _test-output-stream "        e9/jump loop/disp32"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/20")
+    (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/21")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:break:"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/22")
+    (check-next-stream-line-equal _test-output-stream "      81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/23")
+    (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/24")
+    (check-next-stream-line-equal _test-output-stream "a:break:"                "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/25")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/26")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/27")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/28")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/29")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/30")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/31")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-2:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    # non-local conditional branch from a block without a local variable,
+    # unwinding a local on the stack
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  a: {\n")
+    (write _test-input-stream "    var x: int\n")
+    (write _test-input-stream "    {\n")
+    (write _test-input-stream "      break-if->= a\n")
+    (write _test-input-stream "    }\n")
+    (write _test-input-stream "  }\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-2/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-2/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-2/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-2/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-2/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-2/5")
+    (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-2/6")
+    (check-next-stream-line-equal _test-output-stream "a:loop:"                 "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-2/7")
+    (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-2/8")
+    (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-2/9")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:loop:"   "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-2/10")
+    (check-next-stream-line-equal _test-output-stream "        {"               "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-2/11")
+    (check-next-stream-line-equal _test-output-stream "          0f 8c/jump-if-< break/disp32"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-2/12")
+    (check-next-stream-line-equal _test-output-stream "          81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-2/13")
+    (check-next-stream-line-equal _test-output-stream "          e9/jump a:break/disp32"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-2/14")
+    (check-next-stream-line-equal _test-output-stream "        }"               "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-2/15")
+    (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-2/16")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:break:"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-2/17")
+    (check-next-stream-line-equal _test-output-stream "      81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-2/18")
+    (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-2/19")
+    (check-next-stream-line-equal _test-output-stream "a:break:"                "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-2/20")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-2/21")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-2/22")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-2/23")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-2/24")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-2/25")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-2/26")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-3:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    # non-local unconditional branch from a block without a local variable,
+    # unwinding a local on the stack
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  a: {\n")
+    (write _test-input-stream "    var x: int\n")
+    (write _test-input-stream "    {\n")
+    (write _test-input-stream "      break a\n")
+    (write _test-input-stream "    }\n")
+    (write _test-input-stream "  }\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-3/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-3/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-3/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-3/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-3/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-3/5")
+    (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-3/6")
+    (check-next-stream-line-equal _test-output-stream "a:loop:"                 "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-3/7")
+    (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-3/8")
+    (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-3/9")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:loop:"   "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-3/10")
+    (check-next-stream-line-equal _test-output-stream "        81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-3/11")
+    (check-next-stream-line-equal _test-output-stream "        e9/jump a:break/disp32"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-3/12")
+    (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-3/14")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:break:"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-3/15")
+    (check-next-stream-line-equal _test-output-stream "      81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-3/16")
+    (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-3/17")
+    (check-next-stream-line-equal _test-output-stream "a:break:"                "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-3/18")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-3/19")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-3/20")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-3/21")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-3/22")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-3/23")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-3/24")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-4:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  a: {\n")
+    (write _test-input-stream "    var x/esi: int <- copy 0\n")
+    (write _test-input-stream "    {\n")
+    (write _test-input-stream "      break a\n")
+    (write _test-input-stream "    }\n")
+    (write _test-input-stream "  }\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-4/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-4/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-4/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-4/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-4/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-4/5")
+    (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-4/6")
+    (check-next-stream-line-equal _test-output-stream "a:loop:"                 "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-4/7")
+    (check-next-stream-line-equal _test-output-stream "      ff 6/subop/push %esi"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-4/8")
+    (check-next-stream-line-equal _test-output-stream "      be/copy-to-esi 0/imm32"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-4/9")
+    (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-4/10")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:loop:"   "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-4/11")
+    (check-next-stream-line-equal _test-output-stream "        8f 0/subop/pop %esi"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-4/12")
+    (check-next-stream-line-equal _test-output-stream "        e9/jump a:break/disp32"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-4/13")
+    (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-4/14")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:break:"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-4/15")
+    (check-next-stream-line-equal _test-output-stream "      8f 0/subop/pop %esi"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-4/16")
+    (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-4/17")
+    (check-next-stream-line-equal _test-output-stream "a:break:"                "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-4/18")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-4/19")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-4/20")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-4/21")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-4/22")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-4/23")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars-4/24")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-with-nonlocal-unconditional-break-and-local-vars:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  a: {\n")
+    (write _test-input-stream "    var x: int\n")
+    (write _test-input-stream "    {\n")
+    (write _test-input-stream "      var y: int\n")
+    (write _test-input-stream "      break a\n")
+    (write _test-input-stream "      increment x\n")
+    (write _test-input-stream "    }\n")
+    (write _test-input-stream "  }\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/5")
+    (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/6")
+    (check-next-stream-line-equal _test-output-stream "a:loop:"                 "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/7")
+    (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/8")
+    (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/9")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:loop:"   "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/10")
+    (check-next-stream-line-equal _test-output-stream "        68/push 0/imm32" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/11")
+    (check-next-stream-line-equal _test-output-stream "        81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/12")
+    (check-next-stream-line-equal _test-output-stream "        81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/13")
+    (check-next-stream-line-equal _test-output-stream "        e9/jump a:break/disp32"  "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/14")
+    (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/15")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:break:"  "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/16")
+    (check-next-stream-line-equal _test-output-stream "      81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/17")
+    (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/18")
+    (check-next-stream-line-equal _test-output-stream "a:break:"                "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/19")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/20")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/21")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/22")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/23")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/24")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/25")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-with-unconditional-break-and-local-vars:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  {\n")
+    (write _test-input-stream "    var x: int\n")
+    (write _test-input-stream "    {\n")
+    (write _test-input-stream "      var y: int\n")
+    (write _test-input-stream "      break\n")
+    (write _test-input-stream "      increment x\n")
+    (write _test-input-stream "    }\n")
+    (write _test-input-stream "  }\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-unconditional-break-and-local-vars/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-unconditional-break-and-local-vars/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-unconditional-break-and-local-vars/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-unconditional-break-and-local-vars/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-unconditional-break-and-local-vars/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-unconditional-break-and-local-vars/5")
+    (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-unconditional-break-and-local-vars/6")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-unconditional-break-and-local-vars/7")
+    (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-unconditional-break-and-local-vars/8")
+    (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-unconditional-break-and-local-vars/9")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:loop:"   "F - test-convert-function-with-unconditional-break-and-local-vars/10")
+    (check-next-stream-line-equal _test-output-stream "        68/push 0/imm32" "F - test-convert-function-with-unconditional-break-and-local-vars/11")
+    (check-next-stream-line-equal _test-output-stream "        81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-unconditional-break-and-local-vars/12")
+    (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-unconditional-break-and-local-vars/13")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:break:"  "F - test-convert-function-with-unconditional-break-and-local-vars/14")
+    (check-next-stream-line-equal _test-output-stream "      81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-unconditional-break-and-local-vars/15")
+    (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-unconditional-break-and-local-vars/16")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-unconditional-break-and-local-vars/17")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-unconditional-break-and-local-vars/18")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-unconditional-break-and-local-vars/19")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-unconditional-break-and-local-vars/20")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-unconditional-break-and-local-vars/21")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-unconditional-break-and-local-vars/22")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-unconditional-break-and-local-vars/23")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-with-nonlocal-unconditional-loop-and-local-vars:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  a: {\n")
+    (write _test-input-stream "    var x: int\n")
+    (write _test-input-stream "    {\n")
+    (write _test-input-stream "      var y: int\n")
+    (write _test-input-stream "      loop a\n")
+    (write _test-input-stream "      increment x\n")
+    (write _test-input-stream "    }\n")
+    (write _test-input-stream "  }\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/5")
+    (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/6")
+    (check-next-stream-line-equal _test-output-stream "a:loop:"                 "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/7")
+    (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/8")
+    (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/9")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:loop:"   "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/10")
+    (check-next-stream-line-equal _test-output-stream "        68/push 0/imm32" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/11")
+    (check-next-stream-line-equal _test-output-stream "        81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/12")
+    (check-next-stream-line-equal _test-output-stream "        81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/13")
+    (check-next-stream-line-equal _test-output-stream "        e9/jump a:loop/disp32"  "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/14")
+    (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/15")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:break:"  "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/16")
+    (check-next-stream-line-equal _test-output-stream "      81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/17")
+    (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/18")
+    (check-next-stream-line-equal _test-output-stream "a:break:"                "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/19")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/20")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/21")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/22")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/23")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/24")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/25")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-with-local-array-var-in-mem:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x: (array int 3)\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-local-array-var-in-mem/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-local-array-var-in-mem/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-local-array-var-in-mem/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-local-array-var-in-mem/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-local-array-var-in-mem/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-local-array-var-in-mem/5")
+    # define x
+    (check-next-stream-line-equal _test-output-stream "    (push-n-zero-bytes 0x0000000c)"  "F - test-convert-function-with-local-array-var-in-mem/7")
+    (check-next-stream-line-equal _test-output-stream "    68/push 0x0000000c/imm32"  "F - test-convert-function-with-local-array-var-in-mem/8")
+    # reclaim x
+    (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 0x00000010/imm32"  "F - test-convert-function-with-local-array-var-in-mem/9")
+    #
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-local-array-var-in-mem/10")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-local-array-var-in-mem/11")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-local-array-var-in-mem/12")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-local-array-var-in-mem/13")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-local-array-var-in-mem/14")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-local-array-var-in-mem/15")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-array-size-in-hex:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x: (array int 10)\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-array-size-in-hex: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "literal integers are always hex in Mu; start '10' with a '0x' to be unambiguous, converting it to hexadecimal as necessary."  "F - test-array-size-in-hex: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-array-size-in-hex: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-array-size-with-metadata:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x: (array int 3/bar)\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # no errors
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-with-populate:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x/ecx: (addr handle array int) <- copy 0\n")
+    (write _test-input-stream "  populate x, 7\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-populate/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-populate/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-populate/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-populate/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-populate/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-populate/5")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-convert-function-with-populate/6")
+    (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 0/imm32"  "F - test-convert-function-with-populate/7")
+    (check-next-stream-line-equal _test-output-stream "    (allocate-array2 Heap 0x00000004 7 %ecx)"  "F - test-convert-function-with-populate/8")  # 4 = size-of(int)
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx" "F - test-convert-function-with-populate/9")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-populate/10")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-populate/11")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-populate/12")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-populate/13")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-populate/14")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-populate/15")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+# special-case for size(byte) when allocating array
+test-convert-function-with-local-array-of-bytes-in-mem:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x: (array byte 3)\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-local-array-of-bytes-in-mem/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-local-array-of-bytes-in-mem/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-local-array-of-bytes-in-mem/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-local-array-of-bytes-in-mem/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-local-array-of-bytes-in-mem/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-local-array-of-bytes-in-mem/5")
+    # define x
+    (check-next-stream-line-equal _test-output-stream "    (push-n-zero-bytes 0x00000003)"  "F - test-convert-function-with-local-array-of-bytes-in-mem/7")
+    (check-next-stream-line-equal _test-output-stream "    68/push 0x00000003/imm32"  "F - test-convert-function-with-local-array-of-bytes-in-mem/8")
+    # reclaim x
+    (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 0x00000007/imm32"  "F - test-convert-function-with-local-array-of-bytes-in-mem/9")
+    #
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-local-array-of-bytes-in-mem/10")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-local-array-of-bytes-in-mem/11")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-local-array-of-bytes-in-mem/12")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-local-array-of-bytes-in-mem/13")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-local-array-of-bytes-in-mem/14")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-local-array-of-bytes-in-mem/15")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-address:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a: int\n")
+    (write _test-input-stream "  var b/eax: (addr int) <- address a\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-address/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-address/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-address/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-address/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-address/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-address/5")
+    (check-next-stream-line-equal _test-output-stream "    68/push 0/imm32"     "F - test-convert-address/6")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"  "F - test-convert-address/7")
+    (check-next-stream-line-equal _test-output-stream "    8d/copy-address *(ebp+0xfffffffc) 0x00000000/r32"  "F - test-convert-address/8")
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"  "F - test-convert-address/9")
+    (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-address/10")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-address/11")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-address/12")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-address/13")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-address/14")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-address/15")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-address/16")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-floating-point-convert:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a/eax: int <- copy 0\n")
+    (write _test-input-stream "  var b/xmm1: float <- convert a\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-floating-point-convert/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-floating-point-convert/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-floating-point-convert/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-floating-point-convert/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-floating-point-convert/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-floating-point-convert/5")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"  "F - test-convert-floating-point-convert/6")
+    (check-next-stream-line-equal _test-output-stream "    b8/copy-to-eax 0/imm32"  "F - test-convert-floating-point-convert/7")
+    (check-next-stream-line-equal _test-output-stream "    81 5/subop/subtract %esp 4/imm32"  "F - test-convert-floating-point-convert/8")
+    (check-next-stream-line-equal _test-output-stream "    f3 0f 11/<- *esp 1/x32"  "F - test-convert-floating-point-convert/9")
+    (check-next-stream-line-equal _test-output-stream "    f3 0f 2a/convert-to-float %eax 0x00000001/x32"  "F - test-convert-floating-point-convert/10")
+    (check-next-stream-line-equal _test-output-stream "    f3 0f 10/-> *esp 1/x32"  "F - test-convert-floating-point-convert/11")
+    (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 4/imm32"  "F - test-floating-point-convert/12")
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax" "F - test-convert-floating-point-convert/13")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-floating-point-convert/14")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-floating-point-convert/15")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-floating-point-convert/16")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-floating-point-convert/17")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-floating-point-convert/18")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-floating-point-convert/19")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-floating-point-convert-2:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a/eax: int <- copy 0\n")
+    (write _test-input-stream "  var b/xmm1: float <- convert a\n")
+    (write _test-input-stream "  a <- convert b\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-floating-point-convert-2/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-floating-point-convert-2/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-floating-point-convert-2/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-floating-point-convert-2/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-floating-point-convert-2/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-floating-point-convert-2/5")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"  "F - test-convert-floating-point-convert-2/6")
+    (check-next-stream-line-equal _test-output-stream "    b8/copy-to-eax 0/imm32"  "F - test-convert-floating-point-convert-2/7")
+    (check-next-stream-line-equal _test-output-stream "    81 5/subop/subtract %esp 4/imm32"  "F - test-convert-floating-point-convert-2/8")
+    (check-next-stream-line-equal _test-output-stream "    f3 0f 11/<- *esp 1/x32"  "F - test-convert-floating-point-convert-2/9")
+    (check-next-stream-line-equal _test-output-stream "    f3 0f 2a/convert-to-float %eax 0x00000001/x32"  "F - test-convert-floating-point-convert-2/10")
+    (check-next-stream-line-equal _test-output-stream "    f3 0f 2d/convert-to-int %xmm1 0x00000000/r32"  "F - test-convert-floating-point-convert-2/11")
+    (check-next-stream-line-equal _test-output-stream "    f3 0f 10/-> *esp 1/x32"  "F - test-convert-floating-point-convert-2/12")
+    (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 4/imm32"  "F - test-floating-point-convert-2/13")
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax" "F - test-convert-floating-point-convert-2/14")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-floating-point-convert-2/15")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-floating-point-convert-2/16")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-floating-point-convert-2/17")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-floating-point-convert-2/18")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-floating-point-convert-2/19")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-floating-point-convert-2/20")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-floating-point-operation:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn f {\n")
+    (write _test-input-stream "  var m: float\n")
+    (write _test-input-stream "  var x/xmm1: float <- copy m\n")
+    (write _test-input-stream "  var y/xmm5: float <- copy m\n")
+    (write _test-input-stream "  x <- copy y\n")
+    (write _test-input-stream "  copy-to m, y\n")
+    (write _test-input-stream "  x <- add y\n")
+    (write _test-input-stream "  x <- add m\n")
+    (write _test-input-stream "  x <- subtract y\n")
+    (write _test-input-stream "  x <- subtract m\n")
+    (write _test-input-stream "  x <- multiply y\n")
+    (write _test-input-stream "  x <- multiply m\n")
+    (write _test-input-stream "  x <- divide y\n")
+    (write _test-input-stream "  x <- divide m\n")
+    (write _test-input-stream "  x <- reciprocal y\n")
+    (write _test-input-stream "  x <- reciprocal m\n")
+    (write _test-input-stream "  x <- square-root y\n")
+    (write _test-input-stream "  x <- square-root m\n")
+    (write _test-input-stream "  x <- inverse-square-root y\n")
+    (write _test-input-stream "  x <- inverse-square-root m\n")
+    (write _test-input-stream "  x <- max y\n")
+    (write _test-input-stream "  x <- max m\n")
+    (write _test-input-stream "  x <- min y\n")
+    (write _test-input-stream "  x <- min m\n")
+    (write _test-input-stream "  compare x, y\n")
+    (write _test-input-stream "  compare x, m\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "f:"                                                                     "F - test-convert-floating-point-operation/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"                                                         "F - test-convert-floating-point-operation/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"                                                          "F - test-convert-floating-point-operation/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"                                                 "F - test-convert-floating-point-operation/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                                                                    "F - test-convert-floating-point-operation/4")
+    (check-next-stream-line-equal _test-output-stream "$f:0x00000001:loop:"                                                    "F - test-convert-floating-point-operation/5")
+    (check-next-stream-line-equal _test-output-stream "    68/push 0/imm32"                                                    "F - test-convert-floating-point-operation/6")
+    (check-next-stream-line-equal _test-output-stream "    81 5/subop/subtract %esp 4/imm32"                                   "F - test-convert-floating-point-operation/7")
+    (check-next-stream-line-equal _test-output-stream "    f3 0f 11/<- *esp 1/x32"                                             "F - test-convert-floating-point-operation/8")
+    (check-next-stream-line-equal _test-output-stream "    f3 0f 10/copy *(ebp+0xfffffffc) 0x00000001/x32"                     "F - test-convert-floating-point-operation/9")
+    (check-next-stream-line-equal _test-output-stream "    81 5/subop/subtract %esp 4/imm32"                                   "F - test-convert-floating-point-operation/10")
+    (check-next-stream-line-equal _test-output-stream "    f3 0f 11/<- *esp 5/x32"                                             "F - test-convert-floating-point-operation/11")
+    (check-next-stream-line-equal _test-output-stream "    f3 0f 10/copy *(ebp+0xfffffffc) 0x00000005/x32"                     "F - test-convert-floating-point-operation/12")
+    (check-next-stream-line-equal _test-output-stream "    f3 0f 11/copy %xmm1 0x00000005/x32"                                 "F - test-convert-floating-point-operation/13")
+    (check-next-stream-line-equal _test-output-stream "    f3 0f 11/copy *(ebp+0xfffffffc) 0x00000005/x32"                     "F - test-convert-floating-point-operation/14")
+    (check-next-stream-line-equal _test-output-stream "    f3 0f 58/add %xmm5 0x00000001/x32"                                  "F - test-convert-floating-point-operation/15")
+    (check-next-stream-line-equal _test-output-stream "    f3 0f 58/add *(ebp+0xfffffffc) 0x00000001/x32"                      "F - test-convert-floating-point-operation/16")
+    (check-next-stream-line-equal _test-output-stream "    f3 0f 5c/subtract %xmm5 0x00000001/x32"                             "F - test-convert-floating-point-operation/17")
+    (check-next-stream-line-equal _test-output-stream "    f3 0f 5c/subtract *(ebp+0xfffffffc) 0x00000001/x32"                 "F - test-convert-floating-point-operation/18")
+    (check-next-stream-line-equal _test-output-stream "    f3 0f 59/multiply %xmm5 0x00000001/x32"                             "F - test-convert-floating-point-operation/19")
+    (check-next-stream-line-equal _test-output-stream "    f3 0f 59/multiply *(ebp+0xfffffffc) 0x00000001/x32"                 "F - test-convert-floating-point-operation/20")
+    (check-next-stream-line-equal _test-output-stream "    f3 0f 5e/divide %xmm5 0x00000001/x32"                               "F - test-convert-floating-point-operation/21")
+    (check-next-stream-line-equal _test-output-stream "    f3 0f 5e/divide *(ebp+0xfffffffc) 0x00000001/x32"                   "F - test-convert-floating-point-operation/22")
+    (check-next-stream-line-equal _test-output-stream "    f3 0f 53/reciprocal %xmm5 0x00000001/x32"                           "F - test-convert-floating-point-operation/23")
+    (check-next-stream-line-equal _test-output-stream "    f3 0f 53/reciprocal *(ebp+0xfffffffc) 0x00000001/x32"               "F - test-convert-floating-point-operation/24")
+    (check-next-stream-line-equal _test-output-stream "    f3 0f 51/square-root %xmm5 0x00000001/x32"                          "F - test-convert-floating-point-operation/25")
+    (check-next-stream-line-equal _test-output-stream "    f3 0f 51/square-root *(ebp+0xfffffffc) 0x00000001/x32"              "F - test-convert-floating-point-operation/26")
+    (check-next-stream-line-equal _test-output-stream "    f3 0f 52/inverse-square-root %xmm5 0x00000001/x32"                  "F - test-convert-floating-point-operation/27")
+    (check-next-stream-line-equal _test-output-stream "    f3 0f 52/inverse-square-root *(ebp+0xfffffffc) 0x00000001/x32"      "F - test-convert-floating-point-operation/28")
+    (check-next-stream-line-equal _test-output-stream "    f3 0f 5f/max %xmm5 0x00000001/x32"                                  "F - test-convert-floating-point-operation/29")
+    (check-next-stream-line-equal _test-output-stream "    f3 0f 5f/max *(ebp+0xfffffffc) 0x00000001/x32"                      "F - test-convert-floating-point-operation/30")
+    (check-next-stream-line-equal _test-output-stream "    f3 0f 5d/min %xmm5 0x00000001/x32"                                  "F - test-convert-floating-point-operation/31")
+    (check-next-stream-line-equal _test-output-stream "    f3 0f 5d/min *(ebp+0xfffffffc) 0x00000001/x32"                      "F - test-convert-floating-point-operation/32")
+    (check-next-stream-line-equal _test-output-stream "    0f 2f/compare %xmm5 0x00000001/x32"                                 "F - test-convert-floating-point-operation/33")
+    (check-next-stream-line-equal _test-output-stream "    0f 2f/compare *(ebp+0xfffffffc) 0x00000001/x32"                     "F - test-convert-floating-point-operation/34")
+    (check-next-stream-line-equal _test-output-stream "    f3 0f 10/-> *esp 5/x32"                                             "F - test-convert-floating-point-operation/35")
+    (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 4/imm32"                                        "F - test-convert-floating-point-operation/36")
+    (check-next-stream-line-equal _test-output-stream "    f3 0f 10/-> *esp 1/x32"                                             "F - test-convert-floating-point-operation/37")
+    (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 4/imm32"                                        "F - test-convert-floating-point-operation/38")
+    (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 0x00000004/imm32"                               "F - test-convert-floating-point-operation/39")
+    (check-next-stream-line-equal _test-output-stream "  }"                                                                    "F - test-convert-floating-point-operation/40")
+    (check-next-stream-line-equal _test-output-stream "$f:0x00000001:break:"                                                   "F - test-convert-floating-point-operation/41")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"                                                         "F - test-convert-floating-point-operation/42")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"                                                 "F - test-convert-floating-point-operation/43")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"                                                        "F - test-convert-floating-point-operation/44")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"                                                            "F - test-convert-floating-point-operation/45")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-floating-point-dereferenced:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn f {\n")
+    (write _test-input-stream "  var m: float\n")
+    (write _test-input-stream "  var x/xmm1: float <- copy m\n")
+    (write _test-input-stream "  var y/eax: (addr float) <- copy 0\n")
+    (write _test-input-stream "  x <- multiply *y\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "f:"                                                                     "F - test-convert-floating-point-dereferenced/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"                                                         "F - test-convert-floating-point-dereferenced/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"                                                          "F - test-convert-floating-point-dereferenced/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"                                                 "F - test-convert-floating-point-dereferenced/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                                                                    "F - test-convert-floating-point-dereferenced/4")
+    (check-next-stream-line-equal _test-output-stream "$f:0x00000001:loop:"                                                    "F - test-convert-floating-point-dereferenced/5")
+    (check-next-stream-line-equal _test-output-stream "    68/push 0/imm32"                                                    "F - test-convert-floating-point-dereferenced/6")
+    (check-next-stream-line-equal _test-output-stream "    81 5/subop/subtract %esp 4/imm32"                                   "F - test-convert-floating-point-dereferenced/7")
+    (check-next-stream-line-equal _test-output-stream "    f3 0f 11/<- *esp 1/x32"                                             "F - test-convert-floating-point-dereferenced/8")
+    (check-next-stream-line-equal _test-output-stream "    f3 0f 10/copy *(ebp+0xfffffffc) 0x00000001/x32"                     "F - test-convert-floating-point-dereferenced/9")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"                                               "F - test-convert-floating-point-dereferenced/10")
+    (check-next-stream-line-equal _test-output-stream "    b8/copy-to-eax 0/imm32"                                             "F - test-convert-floating-point-dereferenced/11")
+    (check-next-stream-line-equal _test-output-stream "    f3 0f 59/multiply *eax 0x00000001/x32"                              "F - test-convert-floating-point-dereferenced/12")
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"                                                "F - test-convert-floating-point-dereferenced/13")
+    (check-next-stream-line-equal _test-output-stream "    f3 0f 10/-> *esp 1/x32"                                             "F - test-convert-floating-point-dereferenced/14")
+    (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 4/imm32"                                        "F - test-convert-floating-point-dereferenced/15")
+    (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 0x00000004/imm32"                               "F - test-convert-floating-point-dereferenced/16")
+    (check-next-stream-line-equal _test-output-stream "  }"                                                                    "F - test-convert-floating-point-dereferenced/17")
+    (check-next-stream-line-equal _test-output-stream "$f:0x00000001:break:"                                                   "F - test-convert-floating-point-dereferenced/18")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"                                                         "F - test-convert-floating-point-dereferenced/19")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"                                                 "F - test-convert-floating-point-dereferenced/20")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"                                                        "F - test-convert-floating-point-dereferenced/21")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"                                                            "F - test-convert-floating-point-dereferenced/22")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-length-of-array:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo a: (addr array int) {\n")
+    (write _test-input-stream "  var b/eax: (addr array int) <- copy a\n")
+    (write _test-input-stream "  var c/eax: int <- length b\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-length-of-array/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-length-of-array/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-length-of-array/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-length-of-array/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-length-of-array/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-length-of-array/5")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"  "F - test-convert-length-of-array/6")
+    (check-next-stream-line-equal _test-output-stream "    8b/-> *(ebp+0x00000008) 0x00000000/r32"  "F - test-convert-length-of-array/7")
+    (check-next-stream-line-equal _test-output-stream "    8b/-> *eax 0x00000000/r32"  "F - test-convert-length-of-array/8")
+    (check-next-stream-line-equal _test-output-stream "    c1/shift 5/subop/>> %eax 0x00000002/imm8"  "F - test-convert-length-of-array/9")
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"  "F - test-convert-length-of-array/10")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-length-of-array/11")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-length-of-array/12")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-length-of-array/13")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-length-of-array/14")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-length-of-array/15")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-length-of-array/16")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+# special-case for size(byte) when computing array length
+test-convert-length-of-array-of-bytes:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo a: (addr array byte) {\n")
+    (write _test-input-stream "  var b/eax: (addr array byte) <- copy a\n")
+    (write _test-input-stream "  var c/eax: int <- length b\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-length-of-array-of-bytes/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-length-of-array-of-bytes/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-length-of-array-of-bytes/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-length-of-array-of-bytes/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-length-of-array-of-bytes/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-length-of-array-of-bytes/5")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"  "F - test-convert-length-of-array-of-bytes/6")
+    (check-next-stream-line-equal _test-output-stream "    8b/-> *(ebp+0x00000008) 0x00000000/r32"  "F - test-convert-length-of-array-of-bytes/7")
+    (check-next-stream-line-equal _test-output-stream "    8b/-> *eax 0x00000000/r32"  "F - test-convert-length-of-array-of-bytes/8")
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"  "F - test-convert-length-of-array-of-bytes/9")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-length-of-array-of-bytes/10")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-length-of-array-of-bytes/11")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-length-of-array-of-bytes/12")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-length-of-array-of-bytes/13")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-length-of-array-of-bytes/14")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-length-of-array-of-bytes/15")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-length-of-array-on-stack:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a: (array int 3)\n")
+    (write _test-input-stream "  var b/eax: int <- length a\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-length-of-array-on-stack/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-length-of-array-on-stack/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-length-of-array-on-stack/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-length-of-array-on-stack/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-length-of-array-on-stack/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-length-of-array-on-stack/5")
+    # define x
+    (check-next-stream-line-equal _test-output-stream "    (push-n-zero-bytes 0x0000000c)"  "F - test-convert-length-of-array-on-stack/6")
+    (check-next-stream-line-equal _test-output-stream "    68/push 0x0000000c/imm32"  "F - test-convert-length-of-array-on-stack/7")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"  "F - test-convert-length-of-array-on-stack/8")
+    (check-next-stream-line-equal _test-output-stream "    8b/-> *(ebp+0xfffffff0) 0x00000000/r32"  "F - test-convert-length-of-array-on-stack/9")
+    (check-next-stream-line-equal _test-output-stream "    c1/shift 5/subop/>> %eax 0x00000002/imm8"  "F - test-convert-length-of-array-on-stack/10")
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"  "F - test-convert-length-of-array-on-stack/11")
+    (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 0x00000010/imm32"  "F - test-convert-length-of-array-on-stack/12")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-length-of-array-on-stack/13")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-length-of-array-on-stack/14")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-length-of-array-on-stack/15")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-length-of-array-on-stack/16")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-length-of-array-on-stack/17")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-length-of-array-on-stack/18")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-reg-var-def-with-read-of-same-register:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)  # bytes of args in call to convert-mu
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x/eax: int <- copy 3\n")
+    (write _test-input-stream "  var y/eax: int <- add x\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp could be clobbered at this point (though they shouldn't be)
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    (check-stream-equal _test-error-stream  ""  "F - test-reg-var-def-with-read-of-same-register: error stream should be empty")
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                                        "F - test-reg-var-def-with-read-of-same-register/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"                              "F - test-reg-var-def-with-read-of-same-register/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"                               "F - test-reg-var-def-with-read-of-same-register/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"                      "F - test-reg-var-def-with-read-of-same-register/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                                         "F - test-reg-var-def-with-read-of-same-register/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"                       "F - test-reg-var-def-with-read-of-same-register/5")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"                    "F - test-reg-var-def-with-read-of-same-register/6")
+    (check-next-stream-line-equal _test-output-stream "    b8/copy-to-eax 3/imm32"                  "F - test-reg-var-def-with-read-of-same-register/7")
+    (check-next-stream-line-equal _test-output-stream "    01/add-to %eax 0x00000000/r32"           "F - test-reg-var-def-with-read-of-same-register/8")
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"                     "F - test-reg-var-def-with-read-of-same-register/9")
+    (check-next-stream-line-equal _test-output-stream "  }"                                         "F - test-reg-var-def-with-read-of-same-register/10")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"                      "F - test-reg-var-def-with-read-of-same-register/11")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"                              "F - test-reg-var-def-with-read-of-same-register/12")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"                      "F - test-reg-var-def-with-read-of-same-register/13")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"                             "F - test-reg-var-def-with-read-of-same-register/14")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"                                 "F - test-reg-var-def-with-read-of-same-register/15")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-index-into-array:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var arr/eax: (addr array int) <- copy 0\n")
+    (write _test-input-stream "  var idx/ecx: int <- copy 3\n")
+    (write _test-input-stream "  var x/eax: (addr int) <- index arr, idx\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                                        "F - test-convert-index-into-array/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"                              "F - test-convert-index-into-array/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"                               "F - test-convert-index-into-array/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"                      "F - test-convert-index-into-array/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                                         "F - test-convert-index-into-array/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"                       "F - test-convert-index-into-array/5")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"                    "F - test-convert-index-into-array/6")
+    (check-next-stream-line-equal _test-output-stream "    b8/copy-to-eax 0/imm32"                  "F - test-convert-index-into-array/7")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"                    "F - test-convert-index-into-array/8")
+    (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 3/imm32"                  "F - test-convert-index-into-array/9")
+    (check-next-stream-line-equal _test-output-stream "    (__check-mu-array-bounds %ecx 0x00000004 *eax \"foo\" \"arr\")"  "F - test-convert-index-into-array/10")
+    (check-next-stream-line-equal _test-output-stream "    8d/copy-address *(eax + ecx<<0x00000002 + 4) 0x00000000/r32"  "F - test-convert-index-into-array/11")
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx"                     "F - test-convert-index-into-array/12")
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"                     "F - test-convert-index-into-array/13")
+    (check-next-stream-line-equal _test-output-stream "  }"                                         "F - test-convert-index-into-array/14")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"                      "F - test-convert-index-into-array/15")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"                              "F - test-convert-index-into-array/16")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"                      "F - test-convert-index-into-array/17")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"                             "F - test-convert-index-into-array/18")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"                                 "F - test-convert-index-into-array/19")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-index-into-array-of-bytes:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var arr/eax: (addr array byte) <- copy 0\n")
+    (write _test-input-stream "  var idx/ecx: int <- copy 3\n")
+    (write _test-input-stream "  var x/eax: (addr byte) <- index arr, idx\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                                        "F - test-convert-index-into-array-of-bytes/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"                              "F - test-convert-index-into-array-of-bytes/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"                               "F - test-convert-index-into-array-of-bytes/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"                      "F - test-convert-index-into-array-of-bytes/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                                         "F - test-convert-index-into-array-of-bytes/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"                       "F - test-convert-index-into-array-of-bytes/5")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"                    "F - test-convert-index-into-array-of-bytes/6")
+    (check-next-stream-line-equal _test-output-stream "    b8/copy-to-eax 0/imm32"                  "F - test-convert-index-into-array-of-bytes/7")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"                    "F - test-convert-index-into-array-of-bytes/8")
+    (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 3/imm32"                  "F - test-convert-index-into-array-of-bytes/9")
+    (check-next-stream-line-equal _test-output-stream "    (__check-mu-array-bounds %ecx 0x00000001 *eax \"foo\" \"arr\")"  "F - test-convert-index-into-array-of-bytes/10")
+    (check-next-stream-line-equal _test-output-stream "    8d/copy-address *(eax + ecx<<0x00000000 + 4) 0x00000000/r32"  "F - test-convert-index-into-array-of-bytes/11")
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx"                     "F - test-convert-index-into-array-of-bytes/12")
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"                     "F - test-convert-index-into-array-of-bytes/13")
+    (check-next-stream-line-equal _test-output-stream "  }"                                         "F - test-convert-index-into-array-of-bytes/14")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"                      "F - test-convert-index-into-array-of-bytes/15")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"                              "F - test-convert-index-into-array-of-bytes/16")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"                      "F - test-convert-index-into-array-of-bytes/17")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"                             "F - test-convert-index-into-array-of-bytes/18")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"                                 "F - test-convert-index-into-array-of-bytes/19")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-index-into-array-with-literal:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var arr/eax: (addr array int) <- copy 0\n")
+    (write _test-input-stream "  var x/eax: (addr int) <- index arr, 2\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                                        "F - test-convert-index-into-array-with-literal/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"                              "F - test-convert-index-into-array-with-literal/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"                               "F - test-convert-index-into-array-with-literal/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"                      "F - test-convert-index-into-array-with-literal/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                                         "F - test-convert-index-into-array-with-literal/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"                       "F - test-convert-index-into-array-with-literal/5")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"                    "F - test-convert-index-into-array-with-literal/6")
+    (check-next-stream-line-equal _test-output-stream "    b8/copy-to-eax 0/imm32"                  "F - test-convert-index-into-array-with-literal/7")
+    (check-next-stream-line-equal _test-output-stream "    (__check-mu-array-bounds 2 0x00000004 *eax \"foo\" \"arr\")"  "F - test-convert-index-into-array-with-literal/8")
+                                                                                 # 2 * 4 bytes/elem + 4 bytes for size = offset 12
+    (check-next-stream-line-equal _test-output-stream "    8d/copy-address *(eax + 0x0000000c) 0x00000000/r32"  "F - test-convert-index-into-array-with-literal/9")
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"                     "F - test-convert-index-into-array-with-literal/10")
+    (check-next-stream-line-equal _test-output-stream "  }"                                         "F - test-convert-index-into-array-with-literal/11")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"                      "F - test-convert-index-into-array-with-literal/12")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"                              "F - test-convert-index-into-array-with-literal/13")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"                      "F - test-convert-index-into-array-with-literal/14")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"                             "F - test-convert-index-into-array-with-literal/15")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"                                 "F - test-convert-index-into-array-with-literal/16")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-index-into-array-of-bytes-with-literal:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var arr/eax: (addr array byte) <- copy 0\n")
+    (write _test-input-stream "  var x/eax: (addr byte) <- index arr, 2\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                                        "F - test-convert-index-into-array-of-bytes-with-literal/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"                              "F - test-convert-index-into-array-of-bytes-with-literal/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"                               "F - test-convert-index-into-array-of-bytes-with-literal/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"                      "F - test-convert-index-into-array-of-bytes-with-literal/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                                         "F - test-convert-index-into-array-of-bytes-with-literal/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"                       "F - test-convert-index-into-array-of-bytes-with-literal/5")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"                    "F - test-convert-index-into-array-of-bytes-with-literal/6")
+    (check-next-stream-line-equal _test-output-stream "    b8/copy-to-eax 0/imm32"                  "F - test-convert-index-into-array-of-bytes-with-literal/7")
+    (check-next-stream-line-equal _test-output-stream "    (__check-mu-array-bounds 2 0x00000001 *eax \"foo\" \"arr\")"  "F - test-convert-index-into-array-of-bytes-with-literal/8")
+                                                                                 # 2 * 1 byte/elem + 4 bytes for size = offset 6
+    (check-next-stream-line-equal _test-output-stream "    8d/copy-address *(eax + 0x00000006) 0x00000000/r32"  "F - test-convert-index-into-array-of-bytes-with-literal/8")
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"                     "F - test-convert-index-into-array-of-bytes-with-literal/9")
+    (check-next-stream-line-equal _test-output-stream "  }"                                         "F - test-convert-index-into-array-of-bytes-with-literal/10")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"                      "F - test-convert-index-into-array-of-bytes-with-literal/11")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"                              "F - test-convert-index-into-array-of-bytes-with-literal/12")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"                      "F - test-convert-index-into-array-of-bytes-with-literal/13")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"                             "F - test-convert-index-into-array-of-bytes-with-literal/14")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"                                 "F - test-convert-index-into-array-of-bytes-with-literal/15")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-index-into-array-on-stack:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var arr: (array int 3)\n")
+    (write _test-input-stream "  var idx/eax: int <- copy 2\n")
+    (write _test-input-stream "  var x/eax: (addr int) <- index arr, idx\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                                        "F - test-convert-index-into-array-on-stack/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"                              "F - test-convert-index-into-array-on-stack/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"                               "F - test-convert-index-into-array-on-stack/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"                      "F - test-convert-index-into-array-on-stack/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                                         "F - test-convert-index-into-array-on-stack/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"                       "F - test-convert-index-into-array-on-stack/5")
+    # var arr
+    (check-next-stream-line-equal _test-output-stream "    (push-n-zero-bytes 0x0000000c)"          "F - test-convert-index-into-array-on-stack/6")
+    (check-next-stream-line-equal _test-output-stream "    68/push 0x0000000c/imm32"                "F - test-convert-index-into-array-on-stack/7")
+    # var idx
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"                    "F - test-convert-index-into-array-on-stack/8")
+    (check-next-stream-line-equal _test-output-stream "    b8/copy-to-eax 2/imm32"                  "F - test-convert-index-into-array-on-stack/9")
+    (check-next-stream-line-equal _test-output-stream "    (__check-mu-array-bounds %eax 0x00000004 *(ebp+0xfffffff0) \"foo\" \"arr\")"  "F - test-convert-index-into-array-on-stack/10")
+    # var x is at (ebp-0x10) + idx<<2 + 4 = ebp + idx<<2 - 0xc
+    (check-next-stream-line-equal _test-output-stream "    8d/copy-address *(ebp + eax<<0x00000002 + 0xfffffff4) 0x00000000/r32"  "F - test-convert-index-into-array-on-stack/11")
+    # reclaim idx
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"                     "F - test-convert-index-into-array-on-stack/12")
+    # reclaim arr
+    (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 0x00000010/imm32"    "F - test-convert-index-into-array-on-stack/13")
+    #
+    (check-next-stream-line-equal _test-output-stream "  }"                                         "F - test-convert-index-into-array-on-stack/14")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"                      "F - test-convert-index-into-array-on-stack/15")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"                              "F - test-convert-index-into-array-on-stack/16")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"                      "F - test-convert-index-into-array-on-stack/17")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"                             "F - test-convert-index-into-array-on-stack/18")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"                                 "F - test-convert-index-into-array-on-stack/19")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-index-into-array-on-stack-with-literal:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var arr: (array int 3)\n")
+    (write _test-input-stream "  var x/eax: (addr int) <- index arr, 2\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                                        "F - test-convert-index-into-array-on-stack-with-literal/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"                              "F - test-convert-index-into-array-on-stack-with-literal/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"                               "F - test-convert-index-into-array-on-stack-with-literal/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"                      "F - test-convert-index-into-array-on-stack-with-literal/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                                         "F - test-convert-index-into-array-on-stack-with-literal/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"                       "F - test-convert-index-into-array-on-stack-with-literal/5")
+    # var arr
+    (check-next-stream-line-equal _test-output-stream "    (push-n-zero-bytes 0x0000000c)"          "F - test-convert-index-into-array-on-stack-with-literal/6")
+    (check-next-stream-line-equal _test-output-stream "    68/push 0x0000000c/imm32"                "F - test-convert-index-into-array-on-stack-with-literal/7")
+    # var x
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"                    "F - test-convert-index-into-array-on-stack-with-literal/8")
+    (check-next-stream-line-equal _test-output-stream "    (__check-mu-array-bounds 2 0x00000004 *(ebp+0xfffffff0) \"foo\" \"arr\")"  "F - test-convert-index-into-array-on-stack-with-literal/9")
+    # x is at (ebp-0x10) + 4 + 2*4 = ebp-4
+    (check-next-stream-line-equal _test-output-stream "    8d/copy-address *(ebp + 0xfffffffc) 0x00000000/r32"  "F - test-convert-index-into-array-on-stack-with-literal/10")
+    # reclaim x
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"                     "F - test-convert-index-into-array-on-stack-with-literal/11")
+    # reclaim arr
+    (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 0x00000010/imm32"    "F - test-convert-index-into-array-on-stack-with-literal/12")
+    #
+    (check-next-stream-line-equal _test-output-stream "  }"                                         "F - test-convert-index-into-array-on-stack-with-literal/13")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"                      "F - test-convert-index-into-array-on-stack-with-literal/14")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"                              "F - test-convert-index-into-array-on-stack-with-literal/15")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"                      "F - test-convert-index-into-array-on-stack-with-literal/16")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"                             "F - test-convert-index-into-array-on-stack-with-literal/17")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"                                 "F - test-convert-index-into-array-on-stack-with-literal/18")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-index-into-array-of-bytes-on-stack-with-literal:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var arr: (array byte 3)\n")
+    (write _test-input-stream "  var x/eax: (addr byte) <- index arr, 2\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                                        "F - test-convert-index-into-array-of-bytes-on-stack-with-literal/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"                              "F - test-convert-index-into-array-of-bytes-on-stack-with-literal/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"                               "F - test-convert-index-into-array-of-bytes-on-stack-with-literal/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"                      "F - test-convert-index-into-array-of-bytes-on-stack-with-literal/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                                         "F - test-convert-index-into-array-of-bytes-on-stack-with-literal/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"                       "F - test-convert-index-into-array-of-bytes-on-stack-with-literal/5")
+    # var arr
+    (check-next-stream-line-equal _test-output-stream "    (push-n-zero-bytes 0x00000003)"          "F - test-convert-index-into-array-of-bytes-on-stack-with-literal/6")
+    (check-next-stream-line-equal _test-output-stream "    68/push 0x00000003/imm32"                "F - test-convert-index-into-array-of-bytes-on-stack-with-literal/7")
+    # var x
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"                    "F - test-convert-index-into-array-of-bytes-on-stack-with-literal/8")
+    (check-next-stream-line-equal _test-output-stream "    (__check-mu-array-bounds 2 0x00000001 *(ebp+0xfffffff9) \"foo\" \"arr\")"  "F - test-convert-index-into-array-of-bytes-on-stack-with-literal/9")
+    # x is at (ebp-7) + 4 + 2 = ebp-1
+    (check-next-stream-line-equal _test-output-stream "    8d/copy-address *(ebp + 0xffffffff) 0x00000000/r32"  "F - test-convert-index-into-array-of-bytes-on-stack-with-literal/10")
+    # reclaim x
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"                     "F - test-convert-index-into-array-of-bytes-on-stack-with-literal/11")
+    # reclaim arr
+    (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 0x00000007/imm32"    "F - test-convert-index-into-array-of-bytes-on-stack-with-literal/12")
+    #
+    (check-next-stream-line-equal _test-output-stream "  }"                                         "F - test-convert-index-into-array-of-bytes-on-stack-with-literal/13")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"                      "F - test-convert-index-into-array-of-bytes-on-stack-with-literal/14")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"                              "F - test-convert-index-into-array-of-bytes-on-stack-with-literal/15")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"                      "F - test-convert-index-into-array-of-bytes-on-stack-with-literal/16")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"                             "F - test-convert-index-into-array-of-bytes-on-stack-with-literal/17")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"                                 "F - test-convert-index-into-array-of-bytes-on-stack-with-literal/18")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-index-into-array-using-offset:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var arr/eax: (addr array int) <- copy 0\n")
+    (write _test-input-stream "  var idx/ecx: int <- copy 3\n")
+    (write _test-input-stream "  var off/ecx: (offset int) <- compute-offset arr, idx\n")
+    (write _test-input-stream "  var x/eax: (addr int) <- index arr, off\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                                        "F - test-convert-index-into-array-using-offset/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"                              "F - test-convert-index-into-array-using-offset/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"                               "F - test-convert-index-into-array-using-offset/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"                      "F - test-convert-index-into-array-using-offset/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                                         "F - test-convert-index-into-array-using-offset/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"                       "F - test-convert-index-into-array-using-offset/5")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"                    "F - test-convert-index-into-array-using-offset/6")
+    (check-next-stream-line-equal _test-output-stream "    b8/copy-to-eax 0/imm32"                  "F - test-convert-index-into-array-using-offset/7")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"                    "F - test-convert-index-into-array-using-offset/8")
+    (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 3/imm32"                  "F - test-convert-index-into-array-using-offset/9")
+    (check-next-stream-line-equal _test-output-stream "    69/multiply %ecx 0x00000004/imm32 0x00000001/r32"  "F - test-convert-index-into-array-using-offset/10")
+    (check-next-stream-line-equal _test-output-stream "    (__check-mu-array-bounds %ecx 1 *eax \"foo\" \"arr\")"  "F - test-convert-index-into-array-using-offset/11")
+    (check-next-stream-line-equal _test-output-stream "    8d/copy-address *(eax + ecx + 4) 0x00000000/r32"  "F - test-convert-index-into-array-using-offset/12")
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx"                     "F - test-convert-index-into-array-using-offset/13")
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"                     "F - test-convert-index-into-array-using-offset/14")
+    (check-next-stream-line-equal _test-output-stream "  }"                                         "F - test-convert-index-into-array-using-offset/15")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"                      "F - test-convert-index-into-array-using-offset/16")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"                              "F - test-convert-index-into-array-using-offset/17")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"                      "F - test-convert-index-into-array-using-offset/18")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"                             "F - test-convert-index-into-array-using-offset/19")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"                                 "F - test-convert-index-into-array-using-offset/20")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-index-into-array-of-bytes-using-offset:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var arr/eax: (addr array byte) <- copy 0\n")
+    (write _test-input-stream "  var idx/ecx: int <- copy 3\n")
+    (write _test-input-stream "  var off/ecx: (offset byte) <- compute-offset arr, idx\n")
+    (write _test-input-stream "  var x/eax: (addr byte) <- index arr, off\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                                        "F - test-convert-index-into-array-of-bytes-using-offset/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"                              "F - test-convert-index-into-array-of-bytes-using-offset/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"                               "F - test-convert-index-into-array-of-bytes-using-offset/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"                      "F - test-convert-index-into-array-of-bytes-using-offset/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                                         "F - test-convert-index-into-array-of-bytes-using-offset/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"                       "F - test-convert-index-into-array-of-bytes-using-offset/5")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"                    "F - test-convert-index-into-array-of-bytes-using-offset/6")
+    (check-next-stream-line-equal _test-output-stream "    b8/copy-to-eax 0/imm32"                  "F - test-convert-index-into-array-of-bytes-using-offset/7")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"                    "F - test-convert-index-into-array-of-bytes-using-offset/8")
+    (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 3/imm32"                  "F - test-convert-index-into-array-of-bytes-using-offset/9")
+    (check-next-stream-line-equal _test-output-stream "    69/multiply %ecx 0x00000001/imm32 0x00000001/r32"  "F - test-convert-index-into-array-of-bytes-using-offset/10")
+    (check-next-stream-line-equal _test-output-stream "    (__check-mu-array-bounds %ecx 1 *eax \"foo\" \"arr\")"  "F - test-convert-index-into-array-of-bytes-using-offset/11")
+    (check-next-stream-line-equal _test-output-stream "    8d/copy-address *(eax + ecx + 4) 0x00000000/r32"  "F - test-convert-index-into-array-of-bytes-using-offset/12")
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx"                     "F - test-convert-index-into-array-of-bytes-using-offset/13")
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"                     "F - test-convert-index-into-array-of-bytes-using-offset/14")
+    (check-next-stream-line-equal _test-output-stream "  }"                                         "F - test-convert-index-into-array-of-bytes-using-offset/15")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"                      "F - test-convert-index-into-array-of-bytes-using-offset/16")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"                              "F - test-convert-index-into-array-of-bytes-using-offset/17")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"                      "F - test-convert-index-into-array-of-bytes-using-offset/18")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"                             "F - test-convert-index-into-array-of-bytes-using-offset/19")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"                                 "F - test-convert-index-into-array-of-bytes-using-offset/20")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-index-into-array-using-offset-on-stack:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var arr/eax: (addr array int) <- copy 0\n")
+    (write _test-input-stream "  var idx: int\n")
+    (write _test-input-stream "  var off/ecx: (offset int) <- compute-offset arr, idx\n")
+    (write _test-input-stream "  var x/eax: (addr int) <- index arr, off\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                                        "F - test-convert-index-into-array-using-offset-on-stack/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"                              "F - test-convert-index-into-array-using-offset-on-stack/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"                               "F - test-convert-index-into-array-using-offset-on-stack/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"                      "F - test-convert-index-into-array-using-offset-on-stack/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                                         "F - test-convert-index-into-array-using-offset-on-stack/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"                       "F - test-convert-index-into-array-using-offset-on-stack/5")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"                    "F - test-convert-index-into-array-using-offset-on-stack/6")
+    (check-next-stream-line-equal _test-output-stream "    b8/copy-to-eax 0/imm32"                  "F - test-convert-index-into-array-using-offset-on-stack/7")
+    (check-next-stream-line-equal _test-output-stream "    68/push 0/imm32"                         "F - test-convert-index-into-array-using-offset-on-stack/8")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"                    "F - test-convert-index-into-array-using-offset-on-stack/9")
+    (check-next-stream-line-equal _test-output-stream "    69/multiply *(ebp+0xfffffff8) 0x00000004/imm32 0x00000001/r32"  "F - test-convert-index-into-array-using-offset-on-stack/10")
+    (check-next-stream-line-equal _test-output-stream "    (__check-mu-array-bounds %ecx 1 *eax \"foo\" \"arr\")"  "F - test-convert-index-into-array-using-offset-on-stack/11")
+    (check-next-stream-line-equal _test-output-stream "    8d/copy-address *(eax + ecx + 4) 0x00000000/r32"  "F - test-convert-index-into-array-using-offset-on-stack/12")
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx"                     "F - test-convert-index-into-array-using-offset-on-stack/13")
+    (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 0x00000004/imm32"    "F - test-convert-index-into-array-using-offset-on-stack/14")
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"                     "F - test-convert-index-into-array-using-offset-on-stack/15")
+    (check-next-stream-line-equal _test-output-stream "  }"                                         "F - test-convert-index-into-array-using-offset-on-stack/16")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"                      "F - test-convert-index-into-array-using-offset-on-stack/17")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"                              "F - test-convert-index-into-array-using-offset-on-stack/18")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"                      "F - test-convert-index-into-array-using-offset-on-stack/19")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"                             "F - test-convert-index-into-array-using-offset-on-stack/20")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"                                 "F - test-convert-index-into-array-using-offset-on-stack/21")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-index-into-array-of-bytes-using-offset-on-stack:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var arr/eax: (addr array byte) <- copy 0\n")
+    (write _test-input-stream "  var idx: int\n")
+    (write _test-input-stream "  var off/ecx: (offset byte) <- compute-offset arr, idx\n")
+    (write _test-input-stream "  var x/eax: (addr byte) <- index arr, off\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                                        "F - test-convert-index-into-array-of-bytes-using-offset-on-stack/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"                              "F - test-convert-index-into-array-of-bytes-using-offset-on-stack/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"                               "F - test-convert-index-into-array-of-bytes-using-offset-on-stack/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"                      "F - test-convert-index-into-array-of-bytes-using-offset-on-stack/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                                         "F - test-convert-index-into-array-of-bytes-using-offset-on-stack/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"                       "F - test-convert-index-into-array-of-bytes-using-offset-on-stack/5")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"                    "F - test-convert-index-into-array-of-bytes-using-offset-on-stack/6")
+    (check-next-stream-line-equal _test-output-stream "    b8/copy-to-eax 0/imm32"                  "F - test-convert-index-into-array-of-bytes-using-offset-on-stack/7")
+    (check-next-stream-line-equal _test-output-stream "    68/push 0/imm32"                         "F - test-convert-index-into-array-of-bytes-using-offset-on-stack/8")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"                    "F - test-convert-index-into-array-of-bytes-using-offset-on-stack/9")
+    (check-next-stream-line-equal _test-output-stream "    69/multiply *(ebp+0xfffffff8) 0x00000001/imm32 0x00000001/r32"  "F - test-convert-index-into-array-of-bytes-using-offset-on-stack/10")
+    (check-next-stream-line-equal _test-output-stream "    (__check-mu-array-bounds %ecx 1 *eax \"foo\" \"arr\")"  "F - test-convert-index-into-array-of-bytes-using-offset-on-stack/11")
+    (check-next-stream-line-equal _test-output-stream "    8d/copy-address *(eax + ecx + 4) 0x00000000/r32"  "F - test-convert-index-into-array-of-bytes-using-offset-on-stack/12")
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx"                     "F - test-convert-index-into-array-of-bytes-using-offset-on-stack/13")
+    (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 0x00000004/imm32"    "F - test-convert-index-into-array-of-bytes-using-offset-on-stack/14")
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"                     "F - test-convert-index-into-array-of-bytes-using-offset-on-stack/15")
+    (check-next-stream-line-equal _test-output-stream "  }"                                         "F - test-convert-index-into-array-of-bytes-using-offset-on-stack/16")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"                      "F - test-convert-index-into-array-of-bytes-using-offset-on-stack/17")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"                              "F - test-convert-index-into-array-of-bytes-using-offset-on-stack/18")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"                      "F - test-convert-index-into-array-of-bytes-using-offset-on-stack/19")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"                             "F - test-convert-index-into-array-of-bytes-using-offset-on-stack/20")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"                                 "F - test-convert-index-into-array-of-bytes-using-offset-on-stack/21")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-and-type-definition:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo a: (addr t) {\n")
+    (write _test-input-stream "  var _a/eax: (addr t) <- copy a\n")
+    (write _test-input-stream "  var b/ecx: (addr int) <- get _a, x\n")
+    (write _test-input-stream "  var c/ecx: (addr int) <- get _a, y\n")
+    (write _test-input-stream "}\n")
+    (write _test-input-stream "type t {\n")
+    (write _test-input-stream "  x: int\n")
+    (write _test-input-stream "  y: int\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-and-type-definition/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-and-type-definition/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-and-type-definition/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-and-type-definition/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-and-type-definition/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-and-type-definition/5")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"  "F - test-convert-function-and-type-definition/6")
+    (check-next-stream-line-equal _test-output-stream "    8b/-> *(ebp+0x00000008) 0x00000000/r32"  "F - test-convert-function-and-type-definition/7")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-convert-function-and-type-definition/8")
+    (check-next-stream-line-equal _test-output-stream "    8d/copy-address *(eax + 0x00000000) 0x00000001/r32"  "F - test-convert-function-and-type-definition/9")
+    (check-next-stream-line-equal _test-output-stream "    8d/copy-address *(eax + 0x00000004) 0x00000001/r32"  "F - test-convert-function-and-type-definition/11")
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx" "F - test-convert-function-and-type-definition/13")
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax" "F - test-convert-function-and-type-definition/14")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-and-type-definition/15")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-and-type-definition/16")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-and-type-definition/17")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-and-type-definition/18")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-and-type-definition/19")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-and-type-definition/20")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-type-definition-with-array:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "type t {\n")
+    (write _test-input-stream "  a: (array int 3)\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-type-definition-with-array: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "type t: 'array' elements not allowed for now"  "F - test-type-definition-with-array: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-type-definition-with-array: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-type-definition-with-addr:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "type t {\n")
+    (write _test-input-stream "  a: (addr int)\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-type-definition-with-addr: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "type t: 'addr' elements not allowed"  "F - test-type-definition-with-addr: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-type-definition-with-addr: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-with-local-var-with-user-defined-type:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a: t\n")
+    (write _test-input-stream "}\n")
+    (write _test-input-stream "type t {\n")
+    (write _test-input-stream "  x: int\n")
+    (write _test-input-stream "  y: int\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-local-var-with-user-defined-type/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-local-var-with-user-defined-type/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-local-var-with-user-defined-type/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-local-var-with-user-defined-type/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-local-var-with-user-defined-type/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-local-var-with-user-defined-type/5")
+    (check-next-stream-line-equal _test-output-stream "    68/push 0/imm32"     "F - test-convert-function-with-local-var-with-user-defined-type/6")
+    (check-next-stream-line-equal _test-output-stream "    68/push 0/imm32"     "F - test-convert-function-with-local-var-with-user-defined-type/7")
+    (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 0x00000008/imm32"  "F - test-convert-function-with-local-var-with-user-defined-type/8")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-local-var-with-user-defined-type/9")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-local-var-with-user-defined-type/10")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-local-var-with-user-defined-type/11")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-local-var-with-user-defined-type/12")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-local-var-with-user-defined-type/13")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-local-var-with-user-defined-type/14")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-with-local-var-with-user-defined-type-containing-user-defined-type:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a: t\n")
+    (write _test-input-stream "}\n")
+    (write _test-input-stream "type t {\n")
+    (write _test-input-stream "  x: s\n")
+    (write _test-input-stream "}\n")
+    (write _test-input-stream "type s {\n")
+    (write _test-input-stream "  z: int\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-local-var-with-user-defined-type-containing-user-defined-type/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-local-var-with-user-defined-type-containing-user-defined-type/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-local-var-with-user-defined-type-containing-user-defined-type/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-local-var-with-user-defined-type-containing-user-defined-type/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-local-var-with-user-defined-type-containing-user-defined-type/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-local-var-with-user-defined-type-containing-user-defined-type/5")
+    (check-next-stream-line-equal _test-output-stream "    68/push 0/imm32"     "F - test-convert-function-with-local-var-with-user-defined-type-containing-user-defined-type/7")
+    (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-local-var-with-user-defined-type-containing-user-defined-type/8")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-local-var-with-user-defined-type-containing-user-defined-type/9")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-local-var-with-user-defined-type-containing-user-defined-type/10")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-local-var-with-user-defined-type-containing-user-defined-type/11")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-local-var-with-user-defined-type-containing-user-defined-type/12")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-local-var-with-user-defined-type-containing-user-defined-type/13")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-local-var-with-user-defined-type-containing-user-defined-type/14")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-call-with-arg-of-user-defined-type:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn f {\n")
+    (write _test-input-stream "  var a: t\n")
+    (write _test-input-stream "  foo a\n")
+    (write _test-input-stream "}\n")
+    (write _test-input-stream "fn foo x: t {\n")
+    (write _test-input-stream "}\n")
+    (write _test-input-stream "type t {\n")
+    (write _test-input-stream "  x: int\n")
+    (write _test-input-stream "  y: int\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "f:"                      "F - test-convert-function-call-with-arg-of-user-defined-type/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-call-with-arg-of-user-defined-type/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-call-with-arg-of-user-defined-type/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-call-with-arg-of-user-defined-type/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-call-with-arg-of-user-defined-type/4")
+    (check-next-stream-line-equal _test-output-stream "$f:0x00000001:loop:"     "F - test-convert-function-call-with-arg-of-user-defined-type/5")
+    # var a: t
+    (check-next-stream-line-equal _test-output-stream "    68/push 0/imm32"     "F - test-convert-function-call-with-arg-of-user-defined-type/6")
+    (check-next-stream-line-equal _test-output-stream "    68/push 0/imm32"     "F - test-convert-function-call-with-arg-of-user-defined-type/7")
+    # foo a
+    (check-next-stream-line-equal _test-output-stream "    (foo *(ebp+0xfffffff8) *(ebp+0xfffffffc))"  "F - test-convert-function-call-with-arg-of-user-defined-type/8")
+    #
+    (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 0x00000008/imm32"  "F - test-convert-function-call-with-arg-of-user-defined-type/9")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-call-with-arg-of-user-defined-type/10")
+    (check-next-stream-line-equal _test-output-stream "$f:0x00000001:break:"    "F - test-convert-function-call-with-arg-of-user-defined-type/11")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-call-with-arg-of-user-defined-type/12")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-call-with-arg-of-user-defined-type/13")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-call-with-arg-of-user-defined-type/14")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-call-with-arg-of-user-defined-type/15")
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-call-with-arg-of-user-defined-type/16")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-call-with-arg-of-user-defined-type/17")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-call-with-arg-of-user-defined-type/18")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-call-with-arg-of-user-defined-type/19")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-call-with-arg-of-user-defined-type/20")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-call-with-arg-of-user-defined-type/21")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-call-with-arg-of-user-defined-type/22")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-call-with-arg-of-user-defined-type/23")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-call-with-arg-of-user-defined-type-register-indirect:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn f {\n")
+    (write _test-input-stream "  var a/eax: (addr t) <- copy 0\n")
+    (write _test-input-stream "  foo *a\n")
+    (write _test-input-stream "}\n")
+    (write _test-input-stream "fn foo x: t {\n")
+    (write _test-input-stream "}\n")
+    (write _test-input-stream "type t {\n")
+    (write _test-input-stream "  x: int\n")
+    (write _test-input-stream "  y: int\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "f:"                      "F - test-convert-function-call-with-arg-of-user-defined-type/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-call-with-arg-of-user-defined-type/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-call-with-arg-of-user-defined-type/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-call-with-arg-of-user-defined-type/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-call-with-arg-of-user-defined-type/4")
+    (check-next-stream-line-equal _test-output-stream "$f:0x00000001:loop:"     "F - test-convert-function-call-with-arg-of-user-defined-type/5")
+    # var a
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"  "F - test-convert-function-call-with-arg-of-user-defined-type/6")
+    (check-next-stream-line-equal _test-output-stream "    b8/copy-to-eax 0/imm32"  "F - test-convert-function-call-with-arg-of-user-defined-type/7")
+    # foo a
+    (check-next-stream-line-equal _test-output-stream "    (foo *(eax+0x00000000) *(eax+0x00000004))"  "F - test-convert-function-call-with-arg-of-user-defined-type/8")
+    #
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax" "F - test-convert-function-call-with-arg-of-user-defined-type/9")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-call-with-arg-of-user-defined-type/10")
+    (check-next-stream-line-equal _test-output-stream "$f:0x00000001:break:"    "F - test-convert-function-call-with-arg-of-user-defined-type/11")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-call-with-arg-of-user-defined-type/12")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-call-with-arg-of-user-defined-type/13")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-call-with-arg-of-user-defined-type/14")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-call-with-arg-of-user-defined-type/15")
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-call-with-arg-of-user-defined-type/16")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-call-with-arg-of-user-defined-type/17")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-call-with-arg-of-user-defined-type/18")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-call-with-arg-of-user-defined-type/19")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-call-with-arg-of-user-defined-type/20")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-call-with-arg-of-user-defined-type/21")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-call-with-arg-of-user-defined-type/22")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-call-with-arg-of-user-defined-type/23")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+# we don't have special support for call-by-reference; just explicitly create
+# a new variable with the address of the arg
+test-convert-function-call-with-arg-of-user-defined-type-by-reference:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn f {\n")
+    (write _test-input-stream "  var a: t\n")
+    (write _test-input-stream "  var b/eax: (addr t) <- address a\n")
+    (write _test-input-stream "  foo b\n")
+    (write _test-input-stream "}\n")
+    (write _test-input-stream "fn foo x: (addr t) {\n")
+    (write _test-input-stream "  var x/ecx: (addr t) <- copy x\n")
+    (write _test-input-stream "}\n")
+    (write _test-input-stream "type t {\n")
+    (write _test-input-stream "  x: int\n")
+    (write _test-input-stream "  y: int\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "f:"                      "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/4")
+    (check-next-stream-line-equal _test-output-stream "$f:0x00000001:loop:"     "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/5")
+    # var a: t
+    (check-next-stream-line-equal _test-output-stream "    68/push 0/imm32"     "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/6")
+    (check-next-stream-line-equal _test-output-stream "    68/push 0/imm32"     "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/7")
+    # var b/eax: (addr t)
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"  "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/8")
+    (check-next-stream-line-equal _test-output-stream "    8d/copy-address *(ebp+0xfffffff8) 0x00000000/r32"  "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/9")
+    # foo a
+    (check-next-stream-line-equal _test-output-stream "    (foo %eax)"          "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/10")
+    #
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax" "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/11")
+    (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 0x00000008/imm32"  "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/12")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/13")
+    (check-next-stream-line-equal _test-output-stream "$f:0x00000001:break:"    "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/14")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/15")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/16")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/17")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/18")
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/19")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/20")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/21")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/22")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/23")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/24")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/25")
+    (check-next-stream-line-equal _test-output-stream "    8b/-> *(ebp+0x00000008) 0x00000001/r32"  "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/26")
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx" "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/27")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/28")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/29")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/30")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/31")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/32")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/33")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-get-on-local-variable:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a: t\n")
+    (write _test-input-stream "  var c/ecx: (addr int) <- get a, y\n")
+    (write _test-input-stream "}\n")
+    (write _test-input-stream "type t {\n")
+    (write _test-input-stream "  x: int\n")
+    (write _test-input-stream "  y: int\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-get-on-local-variable/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-get-on-local-variable/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-get-on-local-variable/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-get-on-local-variable/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-get-on-local-variable/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-get-on-local-variable/5")
+    # var a
+    (check-next-stream-line-equal _test-output-stream "    68/push 0/imm32"     "F - test-convert-get-on-local-variable/6")
+    (check-next-stream-line-equal _test-output-stream "    68/push 0/imm32"     "F - test-convert-get-on-local-variable/7")
+    # var c
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-convert-get-on-local-variable/8")
+    # get
+    (check-next-stream-line-equal _test-output-stream "    8d/copy-address *(ebp+0xfffffffc) 0x00000001/r32"  "F - test-convert-get-on-local-variable/9")
+    # reclaim c
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx" "F - test-convert-get-on-local-variable/10")
+    # reclaim a
+    (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 0x00000008/imm32"  "F - test-convert-get-on-local-variable/11")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-get-on-local-variable/12")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-get-on-local-variable/13")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-get-on-local-variable/14")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-get-on-local-variable/15")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-get-on-local-variable/16")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-get-on-local-variable/17")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-get-on-function-argument:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo a: t {\n")
+    (write _test-input-stream "  var c/ecx: (addr int) <- get a, y\n")
+    (write _test-input-stream "}\n")
+    (write _test-input-stream "type t {\n")
+    (write _test-input-stream "  x: int\n")
+    (write _test-input-stream "  y: int\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-get-on-function-argument/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-get-on-function-argument/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-get-on-function-argument/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-get-on-function-argument/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-get-on-function-argument/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-get-on-function-argument/5")
+    # var c
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-convert-get-on-function-argument/6")
+    # get
+    (check-next-stream-line-equal _test-output-stream "    8d/copy-address *(ebp+0x0000000c) 0x00000001/r32"  "F - test-convert-get-on-function-argument/7")
+    # reclaim c
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx" "F - test-convert-get-on-function-argument/8")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-get-on-function-argument/9")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-get-on-function-argument/10")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-get-on-function-argument/11")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-get-on-function-argument/12")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-get-on-function-argument/13")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-get-on-function-argument/14")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-get-on-function-argument-with-known-type:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "type t {\n")
+    (write _test-input-stream "  x: int\n")
+    (write _test-input-stream "  y: int\n")
+    (write _test-input-stream "}\n")
+    (write _test-input-stream "fn foo a: t {\n")
+    (write _test-input-stream "  var c/ecx: (addr int) <- get a, y\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-get-on-function-argument-with-known-type/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-get-on-function-argument-with-known-type/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-get-on-function-argument-with-known-type/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-get-on-function-argument-with-known-type/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-get-on-function-argument-with-known-type/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-get-on-function-argument-with-known-type/5")
+    # var c
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-convert-get-on-function-argument-with-known-type/6")
+    # get
+    (check-next-stream-line-equal _test-output-stream "    8d/copy-address *(ebp+0x0000000c) 0x00000001/r32"  "F - test-convert-get-on-function-argument-with-known-type/7")
+    # reclaim c
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx" "F - test-convert-get-on-function-argument-with-known-type/8")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-get-on-function-argument-with-known-type/9")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-get-on-function-argument-with-known-type/10")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-get-on-function-argument-with-known-type/11")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-get-on-function-argument-with-known-type/12")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-get-on-function-argument-with-known-type/13")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-get-on-function-argument-with-known-type/14")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-add-with-too-many-inouts:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a: int\n")
+    (write _test-input-stream "  var b/ecx: int <- add a, 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-add-with-too-many-inouts: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt add: too many inouts; most primitives support at most two arguments, across inouts and outputs"  "F - test-add-with-too-many-inouts: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-add-with-too-many-inouts: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-add-with-too-many-inouts-2:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a: int\n")
+    (write _test-input-stream "  add-to a, 0, 1\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-add-with-too-many-inouts-2: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt add-to: too many inouts; most primitives support at most two arguments, across inouts and outputs"  "F - test-add-with-too-many-inouts-2: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-add-with-too-many-inouts-2: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-add-with-too-many-outputs:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a/eax: int <- copy 0\n")
+    (write _test-input-stream "  var b/ebx: int <- copy 0\n")
+    (write _test-input-stream "  var c/ecx: int <- copy 0\n")
+    (write _test-input-stream "  c, b <- add a\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-add-with-too-many-outputs: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt add: too many outputs; most primitives support at most one output"  "F - test-add-with-too-many-outputs: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-add-with-too-many-outputs: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-add-with-non-number:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a: int\n")
+    (write _test-input-stream "  var b/ecx: (addr int) <- add a\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-add-with-non-number: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt add: 'b' must be a non-addr non-offset scalar"  "F - test-add-with-non-number: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-add-with-non-number: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-add-with-addr-dereferenced:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a/eax: (addr int) <- copy 0\n")
+    (write _test-input-stream "  add-to *a, 1\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+    # no error
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-copy-with-no-inout:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x/eax: boolean <- copy\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-copy-with-no-inout: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt 'copy' expects an inout"  "F - test-copy-with-no-inout: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-copy-with-no-inout: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-copy-with-multiple-inouts:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x/eax: boolean <- copy 0, 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-copy-with-multiple-inouts: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt 'copy' must have just one inout"  "F - test-copy-with-multiple-inouts: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-copy-with-multiple-inouts: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-copy-with-no-output:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  copy 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-copy-with-no-output: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt 'copy' expects an output"  "F - test-copy-with-no-output: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-copy-with-no-output: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-copy-with-multiple-outputs:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x/eax: boolean <- copy 0\n")
+    (write _test-input-stream "  var y/ecx: boolean <- copy 0\n")
+    (write _test-input-stream "  x, y <- copy 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-copy-with-multiple-outputs: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt 'copy' must have just one output"  "F - test-copy-with-multiple-outputs: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-copy-with-multiple-outputs: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-copy-invalid-value-to-address:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x/eax: int <- copy 0\n")
+    (write _test-input-stream "  var y/ecx: (addr int) <- copy x\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-copy-invalid-value-to-address: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt copy: 'y' must be a non-addr non-offset scalar"  "F - test-copy-invalid-value-to-address: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-copy-invalid-value-to-address: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-copy-null-value-to-address:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var y/ecx: (addr int) <- copy 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+    # no errors
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-copy-invalid-value-to-offset:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x/eax: int <- copy 0\n")
+    (write _test-input-stream "  var y/ecx: (offset int) <- copy x\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-copy-invalid-value-to-address: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt copy: 'y' must be a non-addr non-offset scalar"  "F - test-copy-invalid-value-to-address: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-copy-invalid-value-to-offset: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-copy-null-value-to-offset:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var y/ecx: (offset int) <- copy 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # no errors
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-copy-non-literal-to-byte:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x/ecx: int <- copy 3\n")
+    (write _test-input-stream "  var y/ecx: byte <- copy x\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-copy-non-literal-to-byte: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt copy: cannot copy non-literal to 'y' of type byte; use copy-byte"  "F - test-copy-non-literal-to-byte: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-copy-non-literal-to-byte: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-copy-deref-address:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x/eax: (addr addr int) <- copy 0\n")
+    (write _test-input-stream "  var y/ecx: (addr int) <- copy *x\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+    # no errors
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-copy-to-non-register:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x: int\n")
+    (write _test-input-stream "  x <- copy 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-copy-to-non-register: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt copy: output 'x' not in a register"  "F - test-copy-to-non-register: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-copy-to-non-register: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-copy-from-non-scalar-inout:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x: (handle int)\n")
+    (write _test-input-stream "  var y/eax: int <- copy x\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-copy-from-non-scalar-inout: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt copy: 'x' is too large to fit in a register"  "F - test-copy-from-non-scalar-inout: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-copy-from-non-scalar-inout: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-copy-to-with-no-inout:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  copy-to\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-copy-to-with-no-inout: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt 'copy-to' must have two inouts"  "F - test-copy-to-with-no-inout: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-copy-to-with-no-inout: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-copy-to-with-no-source:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x: boolean\n")
+    (write _test-input-stream "  copy-to x\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-copy-to-with-no-source: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt 'copy-to' must have two inouts"  "F - test-copy-to-with-no-source: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-copy-to-with-no-source: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-copy-to-with-no-register:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x: boolean\n")
+    (write _test-input-stream "  copy-to x, x\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-copy-to-with-no-register: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt copy-to: source (second inout) is in memory"  "F - test-copy-to-with-no-register: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-copy-to-with-no-register: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-copy-to-with-too-many-inouts:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x: boolean\n")
+    (write _test-input-stream "  copy-to x, 0, 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-copy-to-with-too-many-inouts: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt 'copy-to' must have two inouts"  "F - test-copy-to-with-too-many-inouts: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-copy-to-with-too-many-inouts: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-copy-to-with-output:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x/eax: boolean <- copy 0\n")
+    (write _test-input-stream "  var y/ecx: boolean <- copy 0\n")
+    (write _test-input-stream "  x <- copy-to y, 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-copy-to-with-output: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt 'copy-to' must not have any outputs"  "F - test-copy-to-with-output: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-copy-to-with-output: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-copy-to-invalid-value-to-address:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x/eax: int <- copy 0\n")
+    (write _test-input-stream "  var y: (addr int)\n")
+    (write _test-input-stream "  copy-to y, x\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-copy-to-invalid-value-to-address: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt copy-to: 'y' must be a non-addr non-offset scalar"  "F - test-copy-to-invalid-value-to-address: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-copy-to-invalid-value-to-address: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-copy-to-null-value-to-address:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var y: (addr int)\n")
+    (write _test-input-stream "  copy-to y, 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+    # no errors
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-copy-to-invalid-value-to-offset:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x/eax: int <- copy 0\n")
+    (write _test-input-stream "  var y: (offset int)\n")
+    (write _test-input-stream "  copy-to y, x\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-copy-to-invalid-value-to-offset: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt copy-to: 'y' must be a non-addr non-offset scalar"  "F - test-copy-to-invalid-value-to-offset: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-copy-to-invalid-value-to-offset: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-copy-to-null-value-to-offset:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var y: (offset int)\n")
+    (write _test-input-stream "  copy-to y, 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+    # no errors
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-copy-to-non-literal-to-byte:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x/ecx: byte <- copy 3\n")
+    (write _test-input-stream "  var y/eax: (addr byte) <- copy 0\n")
+    (write _test-input-stream "  copy-to *y, x\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-copy-to-non-literal-to-byte: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt copy-to: cannot copy non-literal to type byte; use copy-byte-to"  "F - test-copy-to-non-literal-to-byte: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-copy-to-non-literal-to-byte: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-copy-to-deref-address:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x/eax: (addr int) <- copy 0\n")
+    (write _test-input-stream "  var y/ecx: (addr addr int) <- copy 0\n")
+    (write _test-input-stream "  copy-to *y, x\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+    # no errors
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-copy-to-from-non-scalar-inout:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x: (handle int)\n")
+    (write _test-input-stream "  var y: int\n")
+    (write _test-input-stream "  copy-to y, x\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-copy-to-from-non-scalar-inout: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt copy-to: 'x' is too large to copy"  "F - test-copy-to-from-non-scalar-inout: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-copy-to-from-non-scalar-inout: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-copy-byte-with-no-inout:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x/eax: byte <- copy-byte\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-copy-byte-with-no-inout: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt 'copy-byte' expects an inout"  "F - test-copy-byte-with-no-inout: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-copy-byte-with-no-inout: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-copy-byte-with-multiple-inouts:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x/eax: byte <- copy-byte 0, 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-copy-byte-with-multiple-inouts: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt 'copy-byte' must have just one inout"  "F - test-copy-byte-with-multiple-inouts: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-copy-byte-with-multiple-inouts: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-copy-byte-with-no-output:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  copy-byte 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-copy-byte-with-no-output: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt 'copy-byte' expects an output"  "F - test-copy-byte-with-no-output: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-copy-byte-with-no-output: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-copy-byte-with-multiple-outputs:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x/eax: byte <- copy 0\n")
+    (write _test-input-stream "  var y/ecx: byte <- copy 0\n")
+    (write _test-input-stream "  x, y <- copy-byte 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-copy-byte-with-multiple-outputs: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt 'copy-byte' must have just one output"  "F - test-copy-byte-with-multiple-outputs: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-copy-byte-with-multiple-outputs: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-copy-byte-deref-address:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x/eax: (addr byte) <- copy 0\n")
+    (write _test-input-stream "  var y/ecx: byte <- copy-byte *x\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # not bothering checking output
+    (check-next-stream-line-equal _test-error-stream  ""  "F - test-copy-byte-deref-address: error message")
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-copy-byte-with-invalid-output-type:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x/eax: (addr byte) <- copy 0\n")
+    (write _test-input-stream "  var y/eax: int <- copy-byte *x\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-copy-byte-with-invalid-output-type: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt 'copy-byte' must write to output of type byte"  "F - test-copy-byte-with-invalid-output-type: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-copy-byte-with-invalid-output-type: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-copy-byte-from-non-scalar-inout:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x: (handle int)\n")
+    (write _test-input-stream "  var y/eax: byte <- copy-byte x\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-copy-byte-from-non-scalar-inout: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt copy-byte: 'x' is too large to fit in a register"  "F - test-copy-byte-from-non-scalar-inout: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-copy-byte-from-non-scalar-inout: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-copy-byte-to-with-no-inout:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  copy-byte-to\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-copy-byte-to-with-no-inout: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt 'copy-byte-to' must have two inouts"  "F - test-copy-byte-to-with-no-inout: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-copy-byte-to-with-no-inout: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-copy-byte-to-with-no-source:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x/eax: (addr byte) <- copy 0\n")
+    (write _test-input-stream "  copy-byte-to *x\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-copy-byte-to-with-no-source: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt 'copy-byte-to' must have two inouts"  "F - test-copy-byte-to-with-no-source: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-copy-byte-to-with-no-source: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-copy-byte-to-with-too-many-inouts:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x/eax: (addr byte) <- copy 0\n")
+    (write _test-input-stream "  copy-byte-to *x, 0, 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-copy-byte-to-with-too-many-inouts: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt 'copy-byte-to' must have two inouts"  "F - test-copy-byte-to-with-too-many-inouts: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-copy-byte-to-with-too-many-inouts: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-copy-byte-to-with-output:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x/eax: byte <- copy 0\n")
+    (write _test-input-stream "  var y/ecx: (addr byte) <- copy 0\n")
+    (write _test-input-stream "  x <- copy-byte-to *y, 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-copy-byte-to-with-output: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt 'copy-byte-to' must not have any outputs"  "F - test-copy-byte-to-with-output: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-copy-byte-to-with-output: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-copy-byte-to-with-invalid-output-type:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x/eax: byte <- copy 0\n")
+    (write _test-input-stream "  var y: int\n")
+    (write _test-input-stream "  copy-byte-to y, x\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-copy-byte-to-with-invalid-output-type: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt copy-byte-to: 'y' must be a byte"  "F - test-copy-byte-to-with-invalid-output-type: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-copy-byte-to-with-invalid-output-type: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-copy-byte-to-with-literal-inout:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x/eax: (addr byte) <- copy 0\n")
+    (write _test-input-stream "  copy-byte-to *x, 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-copy-byte-to-with-literal-inout: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt copy-byte-to: source (second inout) must be in a register"  "F - test-copy-byte-to-with-literal-inout: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-copy-byte-to-with-literal-inout: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-copy-byte-to-deref-address:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x/eax: byte <- copy 0\n")
+    (write _test-input-stream "  var y/ecx: (addr byte) <- copy 0\n")
+    (write _test-input-stream "  copy-byte-to *y, x\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+    # no errors
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-copy-byte-to-from-non-scalar-inout:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x: (handle int)\n")
+    (write _test-input-stream "  var y/eax: (addr byte) <- copy 0\n")
+    (write _test-input-stream "  copy-byte-to *y, x\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-copy-byte-to-from-non-scalar-inout: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt copy-byte-to: 'x' is too large to copy"  "F - test-copy-byte-to-from-non-scalar-inout: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-copy-byte-to-from-non-scalar-inout: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-compare-with-no-inout:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x: boolean\n")
+    (write _test-input-stream "  compare\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-compare-with-no-inout: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt 'compare' must have two inouts"  "F - test-compare-with-no-inout: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-compare-with-no-inout: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-compare-with-just-one-inout:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x: boolean\n")
+    (write _test-input-stream "  compare x\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-compare-with-just-one-inout: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt 'compare' must have two inouts"  "F - test-compare-with-just-one-inout: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-compare-with-just-one-inout: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-compare-with-too-many-inouts:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x: boolean\n")
+    (write _test-input-stream "  compare x, 0, 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-compare-with-too-many-inouts: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt 'compare' must have two inouts"  "F - test-compare-with-too-many-inouts: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-compare-with-too-many-inouts: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-compare-with-output:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x/eax: boolean <- copy 0\n")
+    (write _test-input-stream "  var y/ecx: boolean <- copy 0\n")
+    (write _test-input-stream "  x <- compare y, 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-compare-with-output: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt 'compare' must not have any outputs"  "F - test-compare-with-output: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-compare-with-output: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-compare-invalid-value-to-address:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x/eax: int <- copy 0\n")
+    (write _test-input-stream "  var y: (addr int)\n")
+    (write _test-input-stream "  compare y, x\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-compare-invalid-value-to-address: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt compare: 'y' must be a non-addr non-offset scalar"  "F - test-compare-invalid-value-to-address: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-compare-invalid-value-to-address: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-compare-address:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x/eax: (addr int) <- copy 0\n")
+    (write _test-input-stream "  var y/ecx: (addr int) <- copy 0\n")
+    (write _test-input-stream "  compare y, x\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+    # no errors
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-compare-deref-address:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x/eax: (addr int) <- copy 0\n")
+    (write _test-input-stream "  var y/ecx: (addr addr int) <- copy 0\n")
+    (write _test-input-stream "  compare *y, x\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+    # no errors
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-compare-two-vars-in-memory:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x: boolean\n")
+    (write _test-input-stream "  compare x, x\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-compare-two-vars-in-memory: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt compare: both inouts are in memory"  "F - test-compare-two-vars-in-memory: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-compare-two-vars-in-memory: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-compare-non-scalar:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x: (handle int)\n")
+    (write _test-input-stream "  var y: int\n")
+    (write _test-input-stream "  compare y, x\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-compare-non-scalar: output should be empty")
+#?     (check-next-stream-line-equal _test-error-stream  "fn foo: stmt compare: 'x' is too large to compare"  "F - test-compare-non-scalar: error message")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt compare: both inouts are in memory"  "F - test-compare-non-scalar: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-compare-non-scalar: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-compare-with-string-literal:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x/eax: (addr array byte) <- copy 0\n")
+    (write _test-input-stream "  compare x, \"abc\"\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-compare-with-string-literal: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt compare: string literal \"abc\" is not supported; use the string-equal? function"  "F - test-compare-with-string-literal: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-compare-with-string-literal: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-address-with-no-inout:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x/eax: boolean <- address\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-address-with-no-inout: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt 'address' expects an inout"  "F - test-address-with-no-inout: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-address-with-no-inout: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-address-with-multiple-inouts:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x/eax: boolean <- address 0, 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-address-with-multiple-inouts: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt 'address' must have just one inout"  "F - test-address-with-multiple-inouts: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-address-with-multiple-inouts: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-address-with-no-output:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  address 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-address-with-no-output: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt 'address' expects an output"  "F - test-address-with-no-output: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-address-with-no-output: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-address-with-multiple-outputs:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x/eax: boolean <- copy 0\n")
+    (write _test-input-stream "  var y/ecx: boolean <- copy 0\n")
+    (write _test-input-stream "  x, y <- address 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-address-with-multiple-outputs: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt 'address' must have just one output"  "F - test-address-with-multiple-outputs: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-address-with-multiple-outputs: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+# silly but it works
+test-address-of-deref:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x/eax: (addr int) <- copy 0\n")
+    (write _test-input-stream "  var y/ecx: (addr int) <- address *x\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+    # no errors
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-address-to-non-register:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x: (addr int)\n")
+    (write _test-input-stream "  x <- address 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-address-to-non-register: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt address: output 'x' not in a register"  "F - test-address-to-non-register: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-address-to-non-register: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-address-with-wrong-type:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x: int\n")
+    (write _test-input-stream "  var y/eax: (addr boolean) <- address x\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-address-with-wrong-type: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt address: output 'y' cannot hold address of 'x'"  "F - test-address-with-wrong-type: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-address-with-wrong-type: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-address-with-right-type-for-array:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x: (array int 3)\n")
+    (write _test-input-stream "  var y/eax: (addr array int) <- address x\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+    # no errors
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-address-with-right-type-for-stream:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x: (stream int 3)\n")
+    (write _test-input-stream "  var y/eax: (addr stream int) <- address x\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+    # no errors
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-get-with-wrong-field:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a: t\n")
+    (write _test-input-stream "  var c/ecx: (addr int) <- get a, y\n")
+    (write _test-input-stream "}\n")
+    (write _test-input-stream "type t {\n")
+    (write _test-input-stream "  x: int\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-get-with-wrong-field: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt get: type 't' has no member called 'y'"  "F - test-get-with-wrong-field: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-get-with-wrong-field: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-get-with-wrong-base-type:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a: int\n")
+    (write _test-input-stream "  var c/ecx: (addr int) <- get a, y\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-get-with-wrong-base-type: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt get: var 'a' must have a 'type' definition"  "F - test-get-with-wrong-base-type: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-get-with-wrong-base-type: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-get-with-wrong-base-type-2:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a: (addr t)\n")
+    (write _test-input-stream "  var c/ecx: (addr int) <- get a, y\n")
+    (write _test-input-stream "}\n")
+    (write _test-input-stream "type t {\n")
+    (write _test-input-stream "  x: int\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-get-with-wrong-base-type-2: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt get: var 'a' is an 'addr' type, and so must live in a register"  "F - test-get-with-wrong-base-type-2: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-get-with-wrong-base-type-2: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-get-with-wrong-base-type-3:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a: (handle int)\n")
+    (write _test-input-stream "  var c/ecx: (addr int) <- get a, y\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-get-with-wrong-base-type-3: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt get: var 'a' must have a 'type' definition"  "F - test-get-with-wrong-base-type-3: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-get-with-wrong-base-type-3: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-get-with-wrong-offset-type:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a: t\n")
+    (write _test-input-stream "  var b: int\n")
+    (write _test-input-stream "  var c/ecx: (addr int) <- get a, b\n")
+    (write _test-input-stream "}\n")
+    (write _test-input-stream "type t {\n")
+    (write _test-input-stream "  x: int\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-get-with-wrong-offset-type: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt get: type 't' has no member called 'b'"  "F - test-get-with-wrong-offset-type: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-get-with-wrong-offset-type: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-get-with-wrong-output-type:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a: t\n")
+    (write _test-input-stream "  var c: (addr int)\n")
+    (write _test-input-stream "  c <- get a, x\n")
+    (write _test-input-stream "}\n")
+    (write _test-input-stream "type t {\n")
+    (write _test-input-stream "  x: int\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-get-with-wrong-output-type: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt get: output 'c' is not in a register"  "F - test-get-with-wrong-output-type: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-get-with-wrong-output-type: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-get-with-wrong-output-type-2:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a: t\n")
+    (write _test-input-stream "  var c/ecx: int <- get a, x\n")
+    (write _test-input-stream "}\n")
+    (write _test-input-stream "type t {\n")
+    (write _test-input-stream "  x: int\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-get-with-wrong-output-type-2: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt get: output must be an addr"  "F - test-get-with-wrong-output-type-2: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-get-with-wrong-output-type-2: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-get-with-wrong-output-type-3:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a: t\n")
+    (write _test-input-stream "  var c/ecx: (array int) <- get a, x\n")
+    (write _test-input-stream "}\n")
+    (write _test-input-stream "type t {\n")
+    (write _test-input-stream "  x: int\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-get-with-wrong-output-type-3: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt get: output must be an addr"  "F - test-get-with-wrong-output-type-3: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-get-with-wrong-output-type-3: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-get-with-wrong-output-type-4:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a: t\n")
+    (write _test-input-stream "  var c/ecx: (addr boolean) <- get a, x\n")
+    (write _test-input-stream "}\n")
+    (write _test-input-stream "type t {\n")
+    (write _test-input-stream "  x: int\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-get-with-wrong-output-type-4: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt get: wrong output type for member 'x' of type 't'"  "F - test-get-with-wrong-output-type-4: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-get-with-wrong-output-type-4: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-get-with-wrong-output-type-5:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a: t\n")
+    (write _test-input-stream "  var c/ecx: (addr handle int) <- get a, x\n")
+    (write _test-input-stream "}\n")
+    (write _test-input-stream "type t {\n")
+    (write _test-input-stream "  x: (handle int)\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+    # no errors
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-get-with-too-few-inouts:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a: t\n")
+    (write _test-input-stream "  var c/ecx: (addr int) <- get a\n")
+    (write _test-input-stream "}\n")
+    (write _test-input-stream "type t {\n")
+    (write _test-input-stream "  x: int\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-get-with-too-few-inouts: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt get: too few inouts (2 required)"  "F - test-get-with-too-few-inouts: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-get-with-too-few-inouts: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-get-with-too-many-inouts:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a: t\n")
+    (write _test-input-stream "  var c/ecx: (addr int) <- get a, x, 0\n")
+    (write _test-input-stream "}\n")
+    (write _test-input-stream "type t {\n")
+    (write _test-input-stream "  x: int\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-get-with-too-many-inouts: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt get: too many inouts (2 required)"  "F - test-get-with-too-many-inouts: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-get-with-too-many-inouts: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-get-with-no-output:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a: t\n")
+    (write _test-input-stream "  get a, x\n")
+    (write _test-input-stream "}\n")
+    (write _test-input-stream "type t {\n")
+    (write _test-input-stream "  x: int\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-get-with-no-output: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt get: must have an output"  "F - test-get-with-no-output: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-get-with-no-output: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-get-with-too-many-outputs:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a: t\n")
+    (write _test-input-stream "  var b: int\n")
+    (write _test-input-stream "  var c/eax: (addr int) <- copy 0\n")
+    (write _test-input-stream "  c, b <- get a, x\n")
+    (write _test-input-stream "}\n")
+    (write _test-input-stream "type t {\n")
+    (write _test-input-stream "  x: int\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-get-with-too-many-outputs: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt get: too many outputs (1 required)"  "F - test-get-with-too-many-outputs: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-get-with-too-many-outputs: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-array-of-user-defined-types:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "type t {\n")  # each t is 8 bytes, which is a power of 2
+    (write _test-input-stream "  x: int\n")
+    (write _test-input-stream "  y: int\n")
+    (write _test-input-stream "}\n")
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var arr/eax: (addr array t) <- copy 0\n")
+    (write _test-input-stream "  var idx/ecx: int <- copy 3\n")
+    (write _test-input-stream "  var x/eax: (addr t) <- index arr, idx\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                                        "F - test-convert-array-of-user-defined-types/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"                              "F - test-convert-array-of-user-defined-types/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"                               "F - test-convert-array-of-user-defined-types/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"                      "F - test-convert-array-of-user-defined-types/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                                         "F - test-convert-array-of-user-defined-types/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"                       "F - test-convert-array-of-user-defined-types/5")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"                    "F - test-convert-array-of-user-defined-types/6")
+    (check-next-stream-line-equal _test-output-stream "    b8/copy-to-eax 0/imm32"                  "F - test-convert-array-of-user-defined-types/7")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"                    "F - test-convert-array-of-user-defined-types/8")
+    (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 3/imm32"                  "F - test-convert-array-of-user-defined-types/9")
+    (check-next-stream-line-equal _test-output-stream "    (__check-mu-array-bounds %ecx 0x00000008 *eax \"foo\" \"arr\")"  "F - test-convert-array-of-user-defined-types/10")
+    (check-next-stream-line-equal _test-output-stream "    8d/copy-address *(eax + ecx<<0x00000003 + 4) 0x00000000/r32"  "F - test-convert-array-of-user-defined-types/11")
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx"                     "F - test-convert-array-of-user-defined-types/12")
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"                     "F - test-convert-array-of-user-defined-types/13")
+    (check-next-stream-line-equal _test-output-stream "  }"                                         "F - test-convert-array-of-user-defined-types/14")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"                      "F - test-convert-array-of-user-defined-types/15")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"                              "F - test-convert-array-of-user-defined-types/16")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"                      "F - test-convert-array-of-user-defined-types/17")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"                             "F - test-convert-array-of-user-defined-types/18")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"                                 "F - test-convert-array-of-user-defined-types/19")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-length-of-array-of-user-defined-types-to-eax:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "type t {\n")  # size = 12, which is not a power of 2
+    (write _test-input-stream "  x: int\n")
+    (write _test-input-stream "  y: int\n")
+    (write _test-input-stream "  z: int\n")
+    (write _test-input-stream "}\n")
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var arr/eax: (addr array t) <- copy 0\n")
+    (write _test-input-stream "  var x/eax: int <- length arr\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-length-of-array-of-user-defined-types-to-eax/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-length-of-array-of-user-defined-types-to-eax/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-length-of-array-of-user-defined-types-to-eax/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-length-of-array-of-user-defined-types-to-eax/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-length-of-array-of-user-defined-types-to-eax/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-length-of-array-of-user-defined-types-to-eax/5")
+    # var arr
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"  "F - test-convert-length-of-array-of-user-defined-types-to-eax/6")
+    (check-next-stream-line-equal _test-output-stream "    b8/copy-to-eax 0/imm32"  "F - test-convert-length-of-array-of-user-defined-types-to-eax/7")
+    # length instruction
+    (check-next-stream-line-equal _test-output-stream "    51/push-ecx"         "F - test-convert-length-of-array-of-user-defined-types-to-eax/8")
+    (check-next-stream-line-equal _test-output-stream "    52/push-edx"         "F - test-convert-length-of-array-of-user-defined-types-to-eax/9")
+    (check-next-stream-line-equal _test-output-stream "    8b/-> *eax 0x00000000/r32"  "F - test-convert-length-of-array-of-user-defined-types-to-eax/10")
+    (check-next-stream-line-equal _test-output-stream "    31/xor %edx 2/r32/edx"  "F - test-convert-length-of-array-of-user-defined-types-to-eax/11")
+    (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 0x0000000c/imm32"  "F - test-convert-length-of-array-of-user-defined-types-to-eax/12")
+    (check-next-stream-line-equal _test-output-stream "    f7 7/subop/idiv-eax-edx-by %ecx"  "F - test-convert-length-of-array-of-user-defined-types-to-eax/13")
+    (check-next-stream-line-equal _test-output-stream "    5a/pop-to-edx"       "F - test-convert-length-of-array-of-user-defined-types-to-eax/14")
+    (check-next-stream-line-equal _test-output-stream "    59/pop-to-ecx"       "F - test-convert-length-of-array-of-user-defined-types-to-eax/15")
+    # reclaim arr
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"  "F - test-convert-length-of-array-of-user-defined-types-to-eax/16")
+    #
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-length-of-array-of-user-defined-types-to-eax/17")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-length-of-array-of-user-defined-types-to-eax/18")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-length-of-array-of-user-defined-types-to-eax/19")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-length-of-array-of-user-defined-types-to-eax/20")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-length-of-array-of-user-defined-types-to-eax/21")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-length-of-array-of-user-defined-types-to-eax/22")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-length-of-array-of-user-defined-types-to-ecx:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "type t {\n")  # size = 12, which is not a power of 2
+    (write _test-input-stream "  x: int\n")
+    (write _test-input-stream "  y: int\n")
+    (write _test-input-stream "  z: int\n")
+    (write _test-input-stream "}\n")
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var arr/eax: (addr array t) <- copy 0\n")
+    (write _test-input-stream "  var x/ecx: int <- length arr\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-length-of-array-of-user-defined-types-to-ecx/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-length-of-array-of-user-defined-types-to-ecx/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-length-of-array-of-user-defined-types-to-ecx/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-length-of-array-of-user-defined-types-to-ecx/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-length-of-array-of-user-defined-types-to-ecx/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-length-of-array-of-user-defined-types-to-ecx/5")
+    # var a
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"  "F - test-convert-length-of-array-of-user-defined-types-to-ecx/6")
+    (check-next-stream-line-equal _test-output-stream "    b8/copy-to-eax 0/imm32"  "F - test-convert-length-of-array-of-user-defined-types-to-ecx/7")
+    # var x
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-convert-length-of-array-of-user-defined-types-to-ecx/8")
+    # length instruction
+    (check-next-stream-line-equal _test-output-stream "    50/push-eax"         "F - test-convert-length-of-array-of-user-defined-types-to-ecx/9")
+    (check-next-stream-line-equal _test-output-stream "    52/push-edx"         "F - test-convert-length-of-array-of-user-defined-types-to-ecx/10")
+    (check-next-stream-line-equal _test-output-stream "    8b/-> *eax 0x00000000/r32"  "F - test-convert-length-of-array-of-user-defined-types-to-ecx/11")
+    (check-next-stream-line-equal _test-output-stream "    31/xor %edx 2/r32/edx"  "F - test-convert-length-of-array-of-user-defined-types-to-ecx/12")
+    (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 0x0000000c/imm32"  "F - test-convert-length-of-array-of-user-defined-types-to-ecx/13")
+    (check-next-stream-line-equal _test-output-stream "    f7 7/subop/idiv-eax-edx-by %ecx"  "F - test-convert-length-of-array-of-user-defined-types-to-ecx/14")
+    (check-next-stream-line-equal _test-output-stream "    89/<- %ecx 0/r32/eax"  "F - test-convert-length-of-array-of-user-defined-types-to-ecx/15")
+    (check-next-stream-line-equal _test-output-stream "    5a/pop-to-edx"       "F - test-convert-length-of-array-of-user-defined-types-to-ecx/16")
+    (check-next-stream-line-equal _test-output-stream "    58/pop-to-eax"       "F - test-convert-length-of-array-of-user-defined-types-to-ecx/17")
+    # reclaim x
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx"  "F - test-convert-length-of-array-of-user-defined-types-to-ecx/18")
+    # reclaim a
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"  "F - test-convert-length-of-array-of-user-defined-types-to-ecx/19")
+    #
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-length-of-array-of-user-defined-types-to-ecx/20")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-length-of-array-of-user-defined-types-to-ecx/21")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-length-of-array-of-user-defined-types-to-ecx/22")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-length-of-array-of-user-defined-types-to-ecx/23")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-length-of-array-of-user-defined-types-to-ecx/24")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-length-of-array-of-user-defined-types-to-ecx/25")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-length-of-array-of-user-defined-types-to-edx:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "type t {\n")  # size = 12, which is not a power of 2
+    (write _test-input-stream "  x: int\n")
+    (write _test-input-stream "  y: int\n")
+    (write _test-input-stream "  z: int\n")
+    (write _test-input-stream "}\n")
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var arr/eax: (addr array t) <- copy 0\n")
+    (write _test-input-stream "  var x/edx: int <- length arr\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-length-of-array-of-user-defined-types-to-edx/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-length-of-array-of-user-defined-types-to-edx/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-length-of-array-of-user-defined-types-to-edx/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-length-of-array-of-user-defined-types-to-edx/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-length-of-array-of-user-defined-types-to-edx/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-length-of-array-of-user-defined-types-to-edx/5")
+    # var a
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"  "F - test-convert-length-of-array-of-user-defined-types-to-edx/6")
+    (check-next-stream-line-equal _test-output-stream "    b8/copy-to-eax 0/imm32"  "F - test-convert-length-of-array-of-user-defined-types-to-edx/7")
+    # var x
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %edx"  "F - test-convert-length-of-array-of-user-defined-types-to-edx/8")
+    # length instruction
+    (check-next-stream-line-equal _test-output-stream "    50/push-eax"         "F - test-convert-length-of-array-of-user-defined-types-to-edx/9")
+    (check-next-stream-line-equal _test-output-stream "    51/push-ecx"         "F - test-convert-length-of-array-of-user-defined-types-to-edx/10")
+    (check-next-stream-line-equal _test-output-stream "    8b/-> *eax 0x00000000/r32"  "F - test-convert-length-of-array-of-user-defined-types-to-edx/11")
+    (check-next-stream-line-equal _test-output-stream "    31/xor %edx 2/r32/edx"  "F - test-convert-length-of-array-of-user-defined-types-to-edx/12")
+    (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 0x0000000c/imm32"  "F - test-convert-length-of-array-of-user-defined-types-to-edx/13")
+    (check-next-stream-line-equal _test-output-stream "    f7 7/subop/idiv-eax-edx-by %ecx"  "F - test-convert-length-of-array-of-user-defined-types-to-edx/14")
+    (check-next-stream-line-equal _test-output-stream "    89/<- %edx 0/r32/eax"  "F - test-convert-length-of-array-of-user-defined-types-to-edx/15")
+    (check-next-stream-line-equal _test-output-stream "    59/pop-to-ecx"       "F - test-convert-length-of-array-of-user-defined-types-to-edx/16")
+    (check-next-stream-line-equal _test-output-stream "    58/pop-to-eax"       "F - test-convert-length-of-array-of-user-defined-types-to-edx/17")
+    # reclaim x
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %edx"  "F - test-convert-length-of-array-of-user-defined-types-to-edx/18")
+    # reclaim a
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"  "F - test-convert-length-of-array-of-user-defined-types-to-edx/19")
+    #
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-length-of-array-of-user-defined-types-to-edx/20")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-length-of-array-of-user-defined-types-to-edx/21")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-length-of-array-of-user-defined-types-to-edx/22")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-length-of-array-of-user-defined-types-to-edx/23")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-length-of-array-of-user-defined-types-to-edx/24")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-length-of-array-of-user-defined-types-to-edx/25")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-length-of-array-of-user-defined-types:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "type t {\n")  # each t is 8 bytes, which is a power of 2
+    (write _test-input-stream "  x: int\n")
+    (write _test-input-stream "  y: int\n")
+    (write _test-input-stream "  z: int\n")
+    (write _test-input-stream "}\n")
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var arr/eax: (addr array t) <- copy 0\n")
+    (write _test-input-stream "  var x/ebx: int <- length arr\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-length-of-array-of-user-defined-types/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-length-of-array-of-user-defined-types/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-length-of-array-of-user-defined-types/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-length-of-array-of-user-defined-types/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-length-of-array-of-user-defined-types/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-length-of-array-of-user-defined-types/5")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"  "F - test-convert-length-of-array-of-user-defined-types/6")
+    (check-next-stream-line-equal _test-output-stream "    b8/copy-to-eax 0/imm32"  "F - test-convert-length-of-array-of-user-defined-types/7")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ebx"  "F - test-convert-length-of-array-of-user-defined-types/8")
+    (check-next-stream-line-equal _test-output-stream "    50/push-eax"         "F - test-convert-length-of-array-of-user-defined-types/9")
+    (check-next-stream-line-equal _test-output-stream "    51/push-ecx"         "F - test-convert-length-of-array-of-user-defined-types/10")
+    (check-next-stream-line-equal _test-output-stream "    52/push-edx"         "F - test-convert-length-of-array-of-user-defined-types/11")
+    (check-next-stream-line-equal _test-output-stream "    8b/-> *eax 0x00000000/r32"  "F - test-convert-length-of-array-of-user-defined-types/12")
+    (check-next-stream-line-equal _test-output-stream "    31/xor %edx 2/r32/edx"  "F - test-convert-length-of-array-of-user-defined-types/13")
+    (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 0x0000000c/imm32"  "F - test-convert-length-of-array-of-user-defined-types/14")
+    (check-next-stream-line-equal _test-output-stream "    f7 7/subop/idiv-eax-edx-by %ecx"  "F - test-convert-length-of-array-of-user-defined-types/15")
+    (check-next-stream-line-equal _test-output-stream "    89/<- %ebx 0/r32/eax"  "F - test-convert-length-of-array-of-user-defined-types/16")
+    (check-next-stream-line-equal _test-output-stream "    5a/pop-to-edx"       "F - test-convert-length-of-array-of-user-defined-types/17")
+    (check-next-stream-line-equal _test-output-stream "    59/pop-to-ecx"       "F - test-convert-length-of-array-of-user-defined-types/18")
+    (check-next-stream-line-equal _test-output-stream "    58/pop-to-eax"       "F - test-convert-length-of-array-of-user-defined-types/19")
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ebx"  "F - test-convert-length-of-array-of-user-defined-types/20")
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"  "F - test-convert-length-of-array-of-user-defined-types/21")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-length-of-array-of-user-defined-types/22")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-length-of-array-of-user-defined-types/23")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-length-of-array-of-user-defined-types/24")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-length-of-array-of-user-defined-types/25")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-length-of-array-of-user-defined-types/26")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-length-of-array-of-user-defined-types/27")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-index-with-non-array-atom-base-type:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a: int\n")
+    (write _test-input-stream "  var c/ecx: (addr int) <- index a, 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-index-with-non-array-atom-base-type: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt index: var 'a' is not an array"  "F - test-index-with-non-array-atom-base-type: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-index-with-non-array-atom-base-type: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-index-with-non-array-compound-base-type:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a: (handle int)\n")
+    (write _test-input-stream "  var c/ecx: (addr int) <- index a, 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-index-with-non-array-compound-base-type: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt index: var 'a' is not an array"  "F - test-index-with-non-array-compound-base-type: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-index-with-non-array-compound-base-type: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-index-with-non-array-compound-base-type-2:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a: (addr int)\n")
+    (write _test-input-stream "  var c/ecx: (addr int) <- index a, 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-index-with-non-array-compound-base-type-2: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt index: var 'a' is not an array"  "F - test-index-with-non-array-compound-base-type-2: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-index-with-non-array-compound-base-type-2: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-index-with-array-atom-base-type:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a: array\n")
+    (write _test-input-stream "  var c/ecx: (addr int) <- index a, 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-index-with-array-atom-base-type: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt index: array 'a' must specify the type of its elements"  "F - test-index-with-array-atom-base-type: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-index-with-array-atom-base-type: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-index-with-addr-base-on-stack:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a: (addr array int)\n")
+    (write _test-input-stream "  var c/ecx: (addr int) <- index a, 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-index-with-addr-base-on-stack: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt index: var 'a' is an addr to an array, and so must live in a register"  "F - test-index-with-addr-base-on-stack: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-index-with-addr-base-on-stack: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-index-with-wrong-index-type:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a/eax: (addr array int) <- copy 0\n")
+    (write _test-input-stream "  var b: boolean\n")
+    (write _test-input-stream "  var c/ecx: (addr int) <- index a, b\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-index-with-wrong-index-type: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt index: second argument 'b' must be an int or offset"  "F - test-index-with-wrong-index-type: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-index-with-wrong-index-type: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-index-with-offset-atom-index-type:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a/eax: (addr array int) <- copy 0\n")
+    (write _test-input-stream "  var b: offset\n")
+    (write _test-input-stream "  var c/ecx: (addr int) <- index a, b\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-index-with-offset-atom-index-type: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt index: offset 'b' must specify the type of array elements"  "F - test-index-with-offset-atom-index-type: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-index-with-offset-atom-index-type: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-index-with-offset-on-stack:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a/eax: (addr array int) <- copy 0\n")
+    (write _test-input-stream "  var b: int\n")
+    (write _test-input-stream "  var c/ecx: (addr int) <- index a, b\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-index-with-offset-on-stack: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt index: second argument 'b' must be in a register"  "F - test-index-with-offset-on-stack: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-index-with-offset-on-stack: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-index-needs-offset-type:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a/eax: (addr array t) <- copy 0\n")
+    (write _test-input-stream "  var b/ebx: int <- copy 0\n")
+    (write _test-input-stream "  var c/ecx: (addr int) <- index a, b\n")
+    (write _test-input-stream "}\n")
+    (write _test-input-stream "type t {\n")  # size 12 is not a power of two
+    (write _test-input-stream "  x: int\n")
+    (write _test-input-stream "  y: int\n")
+    (write _test-input-stream "  z: int\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-index-needs-offset-type: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt index: cannot take an int for array 'a'; create an offset instead. See mu.md for details."  "F - test-index-needs-offset-type: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-index-needs-offset-type: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-index-with-output-not-address:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a/ebx: (addr array boolean) <- copy 0\n")
+    (write _test-input-stream "  var o/edi: int <- index a, 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-index-with-output-not-address: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt index: output 'o' must be an addr"  "F - test-index-with-output-not-address: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-index-with-output-not-address: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-index-with-output-not-address-2:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a/ebx: (addr array boolean) <- copy 0\n")
+    (write _test-input-stream "  var o/edi: (int) <- index a, 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-index-with-output-not-address-2: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt index: output 'o' must be an addr"  "F - test-index-with-output-not-address-2: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-index-with-output-not-address-2: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-index-with-wrong-output-type:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a/ebx: (addr array boolean) <- copy 0\n")
+    (write _test-input-stream "  var o/edi: (addr int) <- index a, 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-index-with-wrong-output-type: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt index: output 'o' does not have the right type"  "F - test-index-with-wrong-output-type: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-index-with-wrong-output-type: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-index-with-wrong-output-compound-type:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a/ebx: (addr array handle boolean) <- copy 0\n")
+    (write _test-input-stream "  var o/edi: (addr handle int) <- index a, 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-index-with-wrong-output-compound-type: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt index: output 'o' does not have the right type"  "F - test-index-with-wrong-output-compound-type: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-index-with-wrong-output-compound-type: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-index-with-no-inouts:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var c/ecx: (addr int) <- index\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-index-with-no-inouts: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt index: too few inouts (2 required)"  "F - test-index-with-no-inouts: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-index-with-no-inouts: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-index-with-too-few-inouts:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a: (array int 3)\n")
+    (write _test-input-stream "  var c/ecx: (addr int) <- index a\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-index-with-too-few-inouts: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt index: too few inouts (2 required)"  "F - test-index-with-too-few-inouts: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-index-with-too-few-inouts: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-index-with-too-many-inouts:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a: (array int 3)\n")
+    (write _test-input-stream "  var c/ecx: (addr int) <- index a, 0, 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-index-with-too-many-inouts: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt index: too many inouts (2 required)"  "F - test-index-with-too-many-inouts: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-index-with-too-many-inouts: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-index-with-no-output:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a: (array int 3)\n")
+    (write _test-input-stream "  index a, 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-index-with-no-output: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt index: must have an output"  "F - test-index-with-no-output: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-index-with-no-output: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-index-with-too-many-outputs:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a: (array int 3)\n")
+    (write _test-input-stream "  var b/eax: (addr int) <- copy 0\n")
+    (write _test-input-stream "  var c/ecx: (addr int) <- copy 0\n")
+    (write _test-input-stream "  b, c <- index a, 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-index-with-too-many-outputs: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt index: too many outputs (1 required)"  "F - test-index-with-too-many-outputs: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-index-with-too-many-outputs: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-compute-offset-with-non-array-atom-base-type:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a: int\n")
+    (write _test-input-stream "  var c/ecx: (offset int) <- compute-offset a, 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-compute-offset-with-non-array-atom-base-type: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt compute-offset: var 'a' is not an array"  "F - test-compute-offset-with-non-array-atom-base-type: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-compute-offset-with-non-array-atom-base-type: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-compute-offset-with-non-array-compound-base-type:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a: (handle int)\n")
+    (write _test-input-stream "  var c/ecx: (offset int) <- compute-offset a, 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-compute-offset-with-non-array-compound-base-type: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt compute-offset: var 'a' is not an array"  "F - test-compute-offset-with-non-array-compound-base-type: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-compute-offset-with-non-array-compound-base-type: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-compute-offset-with-non-array-compound-base-type-2:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a: (addr int)\n")
+    (write _test-input-stream "  var c/ecx: (offset int) <- compute-offset a, 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-compute-offset-with-non-array-compound-base-type-2: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt compute-offset: var 'a' is not an array"  "F - test-compute-offset-with-non-array-compound-base-type-2: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-compute-offset-with-non-array-compound-base-type-2: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-compute-offset-with-array-atom-base-type:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a: array\n")
+    (write _test-input-stream "  var c/ecx: (offset int) <- compute-offset a, 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-compute-offset-with-array-atom-base-type: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt compute-offset: array 'a' must specify the type of its elements"  "F - test-compute-offset-with-array-atom-base-type: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-compute-offset-with-array-atom-base-type: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-compute-offset-with-wrong-index-type:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a/eax: (addr array int) <- copy 0\n")
+    (write _test-input-stream "  var b: boolean\n")
+    (write _test-input-stream "  var c/ecx: (offset int) <- compute-offset a, b\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-compute-offset-with-wrong-index-type: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt compute-offset: second argument 'b' must be an int"  "F - test-compute-offset-with-wrong-index-type: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-compute-offset-with-wrong-index-type: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-compute-offset-with-output-not-offset:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a/ebx: (addr array boolean) <- copy 0\n")
+    (write _test-input-stream "  var o/edi: int <- compute-offset a, 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-compute-offset-with-output-not-offset: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt compute-offset: output 'o' must be an offset"  "F - test-compute-offset-with-output-not-offset: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-compute-offset-with-output-not-offset: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-compute-offset-with-output-not-address-2:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a/ebx: (addr array boolean) <- copy 0\n")
+    (write _test-input-stream "  var o/edi: (int) <- compute-offset a, 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-compute-offset-with-output-not-address-2: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt compute-offset: output 'o' must be an offset"  "F - test-compute-offset-with-output-not-address-2: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-compute-offset-with-output-not-address-2: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-compute-offset-with-wrong-output-type:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a/ebx: (addr array boolean) <- copy 0\n")
+    (write _test-input-stream "  var o/edi: (offset int) <- compute-offset a, 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-compute-offset-with-wrong-output-type: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt compute-offset: output 'o' does not have the right type"  "F - test-compute-offset-with-wrong-output-type: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-compute-offset-with-wrong-output-type: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-compute-offset-with-wrong-output-compound-type:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a/ebx: (addr array handle boolean) <- copy 0\n")
+    (write _test-input-stream "  var o/edi: (offset handle int) <- compute-offset a, 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-compute-offset-with-wrong-output-compound-type: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt compute-offset: output 'o' does not have the right type"  "F - test-compute-offset-with-wrong-output-compound-type: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-compute-offset-with-wrong-output-compound-type: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-compute-offset-with-no-inouts:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var c/ecx: (offset int) <- compute-offset\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-compute-offset-with-no-inouts: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt compute-offset: too few inouts (2 required)"  "F - test-compute-offset-with-no-inouts: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-compute-offset-with-no-inouts: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-compute-offset-with-too-few-inouts:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a: (array int 3)\n")
+    (write _test-input-stream "  var c/ecx: (offset int) <- compute-offset a\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-compute-offset-with-too-few-inouts: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt compute-offset: too few inouts (2 required)"  "F - test-compute-offset-with-too-few-inouts: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-compute-offset-with-too-few-inouts: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-compute-offset-with-too-many-inouts:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a: (array int 3)\n")
+    (write _test-input-stream "  var c/ecx: (offset int) <- compute-offset a, 0, 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-compute-offset-with-too-many-inouts: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt compute-offset: too many inouts (2 required)"  "F - test-compute-offset-with-too-many-inouts: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-compute-offset-with-too-many-inouts: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-compute-offset-with-no-output:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a: (array int 3)\n")
+    (write _test-input-stream "  compute-offset a, 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-compute-offset-with-no-output: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt compute-offset: must have an output"  "F - test-compute-offset-with-no-output: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-compute-offset-with-no-output: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-compute-offset-with-too-many-outputs:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a: (array int 3)\n")
+    (write _test-input-stream "  var b/eax: (offset int) <- compute-offset a, 0\n")
+    (write _test-input-stream "  var c/ecx: (addr int) <- copy 0\n")
+    (write _test-input-stream "  b, c <- compute-offset a, 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-compute-offset-with-too-many-outputs: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt compute-offset: too many outputs (1 required)"  "F - test-compute-offset-with-too-many-outputs: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-compute-offset-with-too-many-outputs: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-read-from-stream:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var s/esi: (addr stream int) <- copy 0\n")
+    (write _test-input-stream "  var o/ecx: (addr int) <- copy 0\n")
+    (write _test-input-stream "  read-from-stream s, o\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                            "F - test-convert-read-from-stream/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"                  "F - test-convert-read-from-stream/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"                   "F - test-convert-read-from-stream/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"          "F - test-convert-read-from-stream/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                             "F - test-convert-read-from-stream/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"           "F - test-convert-read-from-stream/5")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %esi"        "F - test-convert-read-from-stream/6")
+    (check-next-stream-line-equal _test-output-stream "    be/copy-to-esi 0/imm32"      "F - test-convert-read-from-stream/7")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"        "F - test-convert-read-from-stream/8")
+    (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 0/imm32"      "F - test-convert-read-from-stream/9")
+    (check-next-stream-line-equal _test-output-stream "    (read-from-stream %esi %ecx 0x00000004)"  "F - test-convert-read-from-stream/10")
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx"         "F - test-convert-read-from-stream/11")
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %esi"         "F - test-convert-read-from-stream/12")
+    (check-next-stream-line-equal _test-output-stream "  }"                             "F - test-convert-read-from-stream/13")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"          "F - test-convert-read-from-stream/14")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"                  "F - test-convert-read-from-stream/15")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"          "F - test-convert-read-from-stream/16")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"                 "F - test-convert-read-from-stream/17")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"                     "F - test-convert-read-from-stream/18")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-read-from-stream-with-correct-payload-size:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var s/esi: (addr stream handle int) <- copy 0\n")
+    (write _test-input-stream "  var o/ecx: (addr handle int) <- copy 0\n")
+    (write _test-input-stream "  read-from-stream s, o\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                            "F - test-convert-read-from-stream-with-correct-payload-size/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"                  "F - test-convert-read-from-stream-with-correct-payload-size/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"                   "F - test-convert-read-from-stream-with-correct-payload-size/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"          "F - test-convert-read-from-stream-with-correct-payload-size/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                             "F - test-convert-read-from-stream-with-correct-payload-size/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"           "F - test-convert-read-from-stream-with-correct-payload-size/5")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %esi"        "F - test-convert-read-from-stream-with-correct-payload-size/6")
+    (check-next-stream-line-equal _test-output-stream "    be/copy-to-esi 0/imm32"      "F - test-convert-read-from-stream-with-correct-payload-size/7")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"        "F - test-convert-read-from-stream-with-correct-payload-size/8")
+    (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 0/imm32"      "F - test-convert-read-from-stream-with-correct-payload-size/9")
+    (check-next-stream-line-equal _test-output-stream "    (read-from-stream %esi %ecx 0x00000008)"  "F - test-convert-read-from-stream-with-correct-payload-size/10")
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx"         "F - test-convert-read-from-stream-with-correct-payload-size/11")
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %esi"         "F - test-convert-read-from-stream-with-correct-payload-size/12")
+    (check-next-stream-line-equal _test-output-stream "  }"                             "F - test-convert-read-from-stream-with-correct-payload-size/13")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"          "F - test-convert-read-from-stream-with-correct-payload-size/14")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"                  "F - test-convert-read-from-stream-with-correct-payload-size/15")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"          "F - test-convert-read-from-stream-with-correct-payload-size/16")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"                 "F - test-convert-read-from-stream-with-correct-payload-size/17")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"                     "F - test-convert-read-from-stream-with-correct-payload-size/18")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-read-from-stream-with-non-stream-atom-base-type:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a: int\n")
+    (write _test-input-stream "  read-from-stream a, 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-read-from-stream-with-non-stream-atom-base-type: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt read-from-stream: var 'a' must be an addr to a stream"  "F - test-read-from-stream-with-non-stream-atom-base-type: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-read-from-stream-with-non-stream-atom-base-type: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-read-from-stream-with-non-stream-compound-base-type:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a: (handle int)\n")
+    (write _test-input-stream "  read-from-stream a, 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-read-from-stream-with-non-stream-compound-base-type: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt read-from-stream: var 'a' must be an addr to a stream"  "F - test-read-from-stream-with-non-stream-compound-base-type: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-read-from-stream-with-non-stream-compound-base-type: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-read-from-stream-with-non-stream-compound-base-type-2:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a: (addr int)\n")
+    (write _test-input-stream "  read-from-stream a, 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-read-from-stream-with-non-stream-compound-base-type-2: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt read-from-stream: var 'a' must be an addr to a stream"  "F - test-read-from-stream-with-non-stream-compound-base-type-2: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-read-from-stream-with-non-stream-compound-base-type-2: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-read-from-stream-with-stream-atom-base-type:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a: stream\n")
+    (write _test-input-stream "  read-from-stream a, 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-read-from-stream-with-stream-atom-base-type: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt read-from-stream: var 'a' must be an addr to a stream"  "F - test-read-from-stream-with-stream-atom-base-type: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-read-from-stream-with-stream-atom-base-type: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-read-from-stream-with-wrong-index-type:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a/eax: (addr stream int) <- copy 0\n")
+    (write _test-input-stream "  var b: boolean\n")
+    (write _test-input-stream "  read-from-stream a, b\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-read-from-stream-with-wrong-index-type: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt read-from-stream: target 'b' must be an addr"  "F - test-read-from-stream-with-wrong-index-type: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-read-from-stream-with-wrong-index-type: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-read-from-stream-with-no-inouts:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  read-from-stream\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-read-from-stream-with-no-inouts: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt read-from-stream: too few inouts (2 required)"  "F - test-read-from-stream-with-no-inouts: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-read-from-stream-with-no-inouts: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-read-from-stream-with-too-few-inouts:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a: (addr stream int)\n")
+    (write _test-input-stream "  read-from-stream a\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-read-from-stream-with-too-few-inouts: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt read-from-stream: too few inouts (2 required)"  "F - test-read-from-stream-with-too-few-inouts: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-read-from-stream-with-too-few-inouts: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-read-from-stream-with-too-many-inouts:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a: (addr stream int)\n")
+    (write _test-input-stream "  var b: (addr int)\n")
+    (write _test-input-stream "  read-from-stream a, b, 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-read-from-stream-with-too-many-inouts: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt read-from-stream: too many inouts (2 required)"  "F - test-read-from-stream-with-too-many-inouts: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-read-from-stream-with-too-many-inouts: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-read-from-stream-with-output:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a: (addr stream int)\n")
+    (write _test-input-stream "  var b/eax: (addr int) <- copy 0\n")
+    (write _test-input-stream "  b <- read-from-stream a, b\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-read-from-stream-with-output: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt read-from-stream: unexpected output"  "F - test-read-from-stream-with-output: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-read-from-stream-with-output: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-write-to-stream:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var s/esi: (addr stream int) <- copy 0\n")
+    (write _test-input-stream "  var o/ecx: (addr int) <- copy 0\n")
+    (write _test-input-stream "  write-to-stream s, o\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                            "F - test-convert-write-to-stream/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"                  "F - test-convert-write-to-stream/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"                   "F - test-convert-write-to-stream/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"          "F - test-convert-write-to-stream/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                             "F - test-convert-write-to-stream/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"           "F - test-convert-write-to-stream/5")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %esi"        "F - test-convert-write-to-stream/6")
+    (check-next-stream-line-equal _test-output-stream "    be/copy-to-esi 0/imm32"      "F - test-convert-write-to-stream/7")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"        "F - test-convert-write-to-stream/8")
+    (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 0/imm32"      "F - test-convert-write-to-stream/9")
+    (check-next-stream-line-equal _test-output-stream "    (write-to-stream %esi %ecx 0x00000004)"  "F - test-convert-write-to-stream/10")
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx"         "F - test-convert-write-to-stream/11")
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %esi"         "F - test-convert-write-to-stream/12")
+    (check-next-stream-line-equal _test-output-stream "  }"                             "F - test-convert-write-to-stream/13")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"          "F - test-convert-write-to-stream/14")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"                  "F - test-convert-write-to-stream/15")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"          "F - test-convert-write-to-stream/16")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"                 "F - test-convert-write-to-stream/17")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"                     "F - test-convert-write-to-stream/18")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-write-to-stream-with-correct-payload-size:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var s/esi: (addr stream handle int) <- copy 0\n")
+    (write _test-input-stream "  var o/ecx: (addr handle int) <- copy 0\n")
+    (write _test-input-stream "  write-to-stream s, o\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                            "F - test-convert-write-to-stream-with-correct-payload-size/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"                  "F - test-convert-write-to-stream-with-correct-payload-size/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"                   "F - test-convert-write-to-stream-with-correct-payload-size/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"          "F - test-convert-write-to-stream-with-correct-payload-size/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                             "F - test-convert-write-to-stream-with-correct-payload-size/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"           "F - test-convert-write-to-stream-with-correct-payload-size/5")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %esi"        "F - test-convert-write-to-stream-with-correct-payload-size/6")
+    (check-next-stream-line-equal _test-output-stream "    be/copy-to-esi 0/imm32"      "F - test-convert-write-to-stream-with-correct-payload-size/7")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"        "F - test-convert-write-to-stream-with-correct-payload-size/8")
+    (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 0/imm32"      "F - test-convert-write-to-stream-with-correct-payload-size/9")
+    (check-next-stream-line-equal _test-output-stream "    (write-to-stream %esi %ecx 0x00000008)"  "F - test-convert-write-to-stream-with-correct-payload-size/10")
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx"         "F - test-convert-write-to-stream-with-correct-payload-size/11")
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %esi"         "F - test-convert-write-to-stream-with-correct-payload-size/12")
+    (check-next-stream-line-equal _test-output-stream "  }"                             "F - test-convert-write-to-stream-with-correct-payload-size/13")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"          "F - test-convert-write-to-stream-with-correct-payload-size/14")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"                  "F - test-convert-write-to-stream-with-correct-payload-size/15")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"          "F - test-convert-write-to-stream-with-correct-payload-size/16")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"                 "F - test-convert-write-to-stream-with-correct-payload-size/17")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"                     "F - test-convert-write-to-stream-with-correct-payload-size/18")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-write-to-stream-with-non-stream-atom-base-type:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a: int\n")
+    (write _test-input-stream "  write-to-stream a, 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-write-to-stream-with-non-stream-atom-base-type: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt write-to-stream: var 'a' must be an addr to a stream"  "F - test-write-to-stream-with-non-stream-atom-base-type: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-write-to-stream-with-non-stream-atom-base-type: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-write-to-stream-with-non-stream-compound-base-type:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a: (handle int)\n")
+    (write _test-input-stream "  write-to-stream a, 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-write-to-stream-with-non-stream-compound-base-type: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt write-to-stream: var 'a' must be an addr to a stream"  "F - test-write-to-stream-with-non-stream-compound-base-type: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-write-to-stream-with-non-stream-compound-base-type: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-write-to-stream-with-non-stream-compound-base-type-2:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a: (addr int)\n")
+    (write _test-input-stream "  write-to-stream a, 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-write-to-stream-with-non-stream-compound-base-type-2: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt write-to-stream: var 'a' must be an addr to a stream"  "F - test-write-to-stream-with-non-stream-compound-base-type-2: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-write-to-stream-with-non-stream-compound-base-type-2: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-write-to-stream-with-stream-atom-base-type:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a: stream\n")
+    (write _test-input-stream "  write-to-stream a, 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-write-to-stream-with-stream-atom-base-type: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt write-to-stream: var 'a' must be an addr to a stream"  "F - test-write-to-stream-with-stream-atom-base-type: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-write-to-stream-with-stream-atom-base-type: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-write-to-stream-with-wrong-index-type:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a/eax: (addr stream int) <- copy 0\n")
+    (write _test-input-stream "  var b: boolean\n")
+    (write _test-input-stream "  write-to-stream a, b\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-write-to-stream-with-wrong-index-type: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt write-to-stream: target 'b' must be an addr"  "F - test-write-to-stream-with-wrong-index-type: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-write-to-stream-with-wrong-index-type: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-write-to-stream-with-no-inouts:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  write-to-stream\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-write-to-stream-with-no-inouts: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt write-to-stream: too few inouts (2 required)"  "F - test-write-to-stream-with-no-inouts: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-write-to-stream-with-no-inouts: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-write-to-stream-with-too-few-inouts:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a: (addr stream int)\n")
+    (write _test-input-stream "  write-to-stream a\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-write-to-stream-with-too-few-inouts: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt write-to-stream: too few inouts (2 required)"  "F - test-write-to-stream-with-too-few-inouts: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-write-to-stream-with-too-few-inouts: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-write-to-stream-with-too-many-inouts:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a: (addr stream int)\n")
+    (write _test-input-stream "  var b: (addr int)\n")
+    (write _test-input-stream "  write-to-stream a, b, 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-write-to-stream-with-too-many-inouts: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt write-to-stream: too many inouts (2 required)"  "F - test-write-to-stream-with-too-many-inouts: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-write-to-stream-with-too-many-inouts: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-write-to-stream-with-output:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a: (addr stream int)\n")
+    (write _test-input-stream "  var b/eax: (addr int) <- copy 0\n")
+    (write _test-input-stream "  b <- write-to-stream a, b\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-write-to-stream-with-output: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt write-to-stream: unexpected output"  "F - test-write-to-stream-with-output: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-write-to-stream-with-output: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-length-with-non-array-atom-base-type:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a: int\n")
+    (write _test-input-stream "  var c/ecx: int <- length a\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-length-with-non-array-atom-base-type: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt length: var 'a' is not an array"  "F - test-length-with-non-array-atom-base-type: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-length-with-non-array-atom-base-type: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-length-with-non-array-compound-base-type:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a: (handle int)\n")
+    (write _test-input-stream "  var c/ecx: (addr int) <- length a, 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-length-with-non-array-compound-base-type: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt length: var 'a' is not an array"  "F - test-length-with-non-array-compound-base-type: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-length-with-non-array-compound-base-type: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-length-with-non-array-compound-base-type-2:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a: (addr int)\n")
+    (write _test-input-stream "  var c/ecx: (addr int) <- length a, 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-length-with-non-array-compound-base-type-2: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt length: var 'a' is not an array"  "F - test-length-with-non-array-compound-base-type-2: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-length-with-non-array-compound-base-type-2: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-length-with-array-atom-base-type:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a: array\n")
+    (write _test-input-stream "  var c/ecx: (addr int) <- length a\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-length-with-array-atom-base-type: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt length: array 'a' must specify the type of its elements"  "F - test-length-with-array-atom-base-type: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-length-with-array-atom-base-type: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-length-with-addr-base-on-stack:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a: (addr array int)\n")
+    (write _test-input-stream "  var c/ecx: (addr int) <- length a\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-length-with-addr-base-on-stack: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt length: var 'a' is an addr to an array, and so must live in a register"  "F - test-length-with-addr-base-on-stack: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-length-with-addr-base-on-stack: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-length-with-wrong-output-type:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a/ebx: (addr array boolean) <- copy 0\n")
+    (write _test-input-stream "  var o/edi: (addr int) <- length a\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-length-with-wrong-output-type: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt length: output 'o' does not have the right type"  "F - test-length-with-wrong-output-type: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-length-with-wrong-output-type: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-length-with-wrong-output-compound-type:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a/ebx: (addr array handle boolean) <- copy 0\n")
+    (write _test-input-stream "  var o/edi: (addr handle int) <- length a\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-length-with-wrong-output-compound-type: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt length: output 'o' does not have the right type"  "F - test-length-with-wrong-output-compound-type: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-length-with-wrong-output-compound-type: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-length-with-no-inouts:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var c/ecx: int <- length\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-length-with-no-inouts: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt length: too few inouts (1 required)"  "F - test-length-with-no-inouts: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-length-with-no-inouts: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-length-with-too-many-inouts:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a: (array int 3)\n")
+    (write _test-input-stream "  var c/ecx: int <- length a, 0, 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-length-with-too-many-inouts: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt length: too many inouts (1 required)"  "F - test-length-with-too-many-inouts: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-length-with-too-many-inouts: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-length-with-no-output:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a: (array int 3)\n")
+    (write _test-input-stream "  length a\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-length-with-no-output: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt length: must have an output"  "F - test-length-with-no-output: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-length-with-no-output: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-length-with-too-many-outputs:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var a: (array int 3)\n")
+    (write _test-input-stream "  var b/eax: int <- copy 0\n")
+    (write _test-input-stream "  var c/ecx: int <- copy 0\n")
+    (write _test-input-stream "  b, c <- length a\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-length-with-too-many-outputs: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt length: too many outputs (1 required)"  "F - test-length-with-too-many-outputs: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-length-with-too-many-outputs: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-with-return-register-and-local:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo -> _/eax: int {\n")
+    (write _test-input-stream "  var y/eax: int <- copy 3\n")
+    (write _test-input-stream "  var z/ecx: int <- copy 4\n")
+    (write _test-input-stream "  return y\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-return-register-and-local/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-return-register-and-local/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-return-register-and-local/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-return-register-and-local/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-return-register-and-local/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-return-register-and-local/5")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"  "F - test-convert-function-with-return-register-and-local/6")
+    (check-next-stream-line-equal _test-output-stream "    b8/copy-to-eax 3/imm32"  "F - test-convert-function-with-return-register-and-local/7")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-convert-function-with-return-register-and-local/8")
+    (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 4/imm32"  "F - test-convert-function-with-return-register-and-local/9")
+    (check-next-stream-line-equal _test-output-stream "    8b/-> %eax 0x00000000/r32" "F - test-convert-function-with-return-register-and-local/10")
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx"         "F - test-convert-function-with-return-register-and-local/11")
+    (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 4/imm32"  "F - test-convert-function-with-return-register-and-local/12")
+    (check-next-stream-line-equal _test-output-stream "    e9/jump $foo:0x00000001:break/disp32"  "F - test-convert-function-with-return-register-and-local/13")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-return-register-and-local/14")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-return-register-and-local/15")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-return-register-and-local/16")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-return-register-and-local/17")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-return-register-and-local/18")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-return-register-and-local/19")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-with-return-register-and-local-2:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo -> _/eax: int {\n")
+    (write _test-input-stream "  var y/eax: int <- copy 3\n")
+    (write _test-input-stream "  var z/ecx: int <- copy 4\n")
+    (write _test-input-stream "  return z\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-return-register-and-local-2/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-return-register-and-local-2/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-return-register-and-local-2/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-return-register-and-local-2/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-return-register-and-local-2/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-return-register-and-local-2/5")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"  "F - test-convert-function-with-return-register-and-local-2/6")
+    (check-next-stream-line-equal _test-output-stream "    b8/copy-to-eax 3/imm32"  "F - test-convert-function-with-return-register-and-local-2/7")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-convert-function-with-return-register-and-local-2/8")
+    (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 4/imm32"  "F - test-convert-function-with-return-register-and-local-2/9")
+    (check-next-stream-line-equal _test-output-stream "    8b/-> %ecx 0x00000000/r32" "F - test-convert-function-with-return-register-and-local-2/10")
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx"         "F - test-convert-function-with-return-register-and-local-2/11")
+    (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 4/imm32"  "F - test-convert-function-with-return-register-and-local-2/12")
+    (check-next-stream-line-equal _test-output-stream "    e9/jump $foo:0x00000001:break/disp32"  "F - test-convert-function-with-return-register-and-local-2/13")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-return-register-and-local-2/14")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-return-register-and-local-2/15")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-return-register-and-local-2/16")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-return-register-and-local-2/17")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-return-register-and-local-2/18")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-return-register-and-local-2/19")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-with-return-float-register-and-local:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo -> _/xmm1: float {\n")
+    (write _test-input-stream "  var y/eax: int <- copy 3\n")
+    (write _test-input-stream "  var g/xmm0: float <- convert y\n")
+    (write _test-input-stream "  var h/xmm1: float <- convert y\n")
+    (write _test-input-stream "  return g\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-return-float-register-and-local/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-return-float-register-and-local/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-return-float-register-and-local/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-return-float-register-and-local/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-return-float-register-and-local/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-return-float-register-and-local/5")
+    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"  "F - test-convert-function-with-return-float-register-and-local/6")  # var y
+    (check-next-stream-line-equal _test-output-stream "    b8/copy-to-eax 3/imm32"  "F - test-convert-function-with-return-float-register-and-local/7")
+    (check-next-stream-line-equal _test-output-stream "    81 5/subop/subtract %esp 4/imm32"  "F - test-convert-function-with-return-float-register-and-local/8")  # var g
+    (check-next-stream-line-equal _test-output-stream "    f3 0f 11/<- *esp 0/x32"  "F - test-convert-function-with-return-float-register-and-local/9")
+    (check-next-stream-line-equal _test-output-stream "    f3 0f 2a/convert-to-float %eax 0x00000000/x32"  "F - test-convert-function-with-return-float-register-and-local/10")
+    (check-next-stream-line-equal _test-output-stream "    81 5/subop/subtract %esp 4/imm32"  "F - test-convert-function-with-return-float-register-and-local/11")  # var h
+    (check-next-stream-line-equal _test-output-stream "    f3 0f 11/<- *esp 1/x32"  "F - test-convert-function-with-return-float-register-and-local/12")
+    (check-next-stream-line-equal _test-output-stream "    f3 0f 2a/convert-to-float %eax 0x00000001/x32"  "F - test-convert-function-with-return-float-register-and-local/13")
+    (check-next-stream-line-equal _test-output-stream "    f3 0f 10/-> %xmm0 0x00000001/x32"  "F - test-convert-function-with-return-float-register-and-local/14")  # return g
+    (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 4/imm32"  "F - test-convert-floating-point-dereferenced/15")  # reclaim h
+    (check-next-stream-line-equal _test-output-stream "    f3 0f 10/-> *esp 0/x32"  "F - test-convert-floating-point-dereferenced/16")  # reclaim g
+    (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 4/imm32"  "F - test-convert-floating-point-dereferenced/17")
+    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"         "F - test-convert-function-with-return-float-register-and-local/18")  # reclaim y
+    (check-next-stream-line-equal _test-output-stream "    e9/jump $foo:0x00000001:break/disp32"  "F - test-convert-function-with-return-float-register-and-local/19")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-return-float-register-and-local/20")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-return-float-register-and-local/21")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-return-float-register-and-local/22")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-return-float-register-and-local/23")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-return-float-register-and-local/24")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-return-float-register-and-local/25")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-function-with-return-and-local-vars:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo -> _/eax: int {\n")
+    (write _test-input-stream "  {\n")
+    (write _test-input-stream "    var x: int\n")
+    (write _test-input-stream "    {\n")
+    (write _test-input-stream "      var y: int\n")
+    (write _test-input-stream "      return y\n")
+    (write _test-input-stream "      increment x\n")
+    (write _test-input-stream "    }\n")
+    (write _test-input-stream "  }\n")
+    (write _test-input-stream "  return 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-return-and-local-vars/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-return-and-local-vars/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-return-and-local-vars/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-return-and-local-vars/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-return-and-local-vars/4")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-return-and-local-vars/5")
+    (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-return-and-local-vars/6")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-return-and-local-vars/7")
+    (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-return-and-local-vars/8")  # var x
+    (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-return-and-local-vars/9")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:loop:"   "F - test-convert-function-with-return-and-local-vars/10")
+    (check-next-stream-line-equal _test-output-stream "        68/push 0/imm32" "F - test-convert-function-with-return-and-local-vars/11")  # var y
+    (check-next-stream-line-equal _test-output-stream "        8b/-> *(ebp+0xfffffff8) 0x00000000/r32" "F - test-convert-function-with-return-and-local-vars/12")
+    (check-next-stream-line-equal _test-output-stream "        81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-return-and-local-vars/13")
+    (check-next-stream-line-equal _test-output-stream "        81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-return-and-local-vars/14")
+    (check-next-stream-line-equal _test-output-stream "        e9/jump $foo:0x00000001:break/disp32"  "F - test-convert-function-with-return-and-local-vars/15")
+    (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-return-and-local-vars/16")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:break:"  "F - test-convert-function-with-return-and-local-vars/17")
+    (check-next-stream-line-equal _test-output-stream "      81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-return-and-local-vars/18")
+    (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-return-and-local-vars/19")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-return-and-local-vars/20")
+    (check-next-stream-line-equal _test-output-stream "    c7 0/subop/copy %eax 0/imm32"  "F - test-convert-function-with-return-and-local-vars/21")
+    (check-next-stream-line-equal _test-output-stream "    e9/jump $foo:0x00000001:break/disp32"  "F - test-convert-function-with-return-and-local-vars/21")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-return-and-local-vars/21")
+    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-return-and-local-vars/22")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-return-and-local-vars/23")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-return-and-local-vars/24")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-return-and-local-vars/25")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-return-and-local-vars/26")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-copy-object-with-no-inout:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  copy-object\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-copy-object-with-no-inout: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt 'copy-object' must have two inouts"  "F - test-copy-object-with-no-inout: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-copy-object-with-no-inout: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-copy-object-with-no-source:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x: (addr int)\n")
+    (write _test-input-stream "  copy-object x\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-copy-object-with-no-source: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt 'copy-object' must have two inouts"  "F - test-copy-object-with-no-source: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-copy-object-with-no-source: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-copy-object-with-too-many-inouts:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x: (addr boolean)\n")
+    (write _test-input-stream "  copy-object x, x, x\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-copy-object-with-too-many-inouts: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt 'copy-object' must have two inouts"  "F - test-copy-object-with-too-many-inouts: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-copy-object-with-too-many-inouts: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-copy-object-with-output:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x/eax: (addr boolean) <- copy 0\n")
+    (write _test-input-stream "  var y/ecx: (addr boolean) <- copy 0\n")
+    (write _test-input-stream "  x <- copy-object x, y\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-copy-object-with-output: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt 'copy-object' must not have any outputs"  "F - test-copy-object-with-output: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-copy-object-with-output: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-copy-object-deref-address:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x/eax: (addr int) <- copy 0\n")
+    (write _test-input-stream "  var y/ecx: (addr addr int) <- copy 0\n")
+    (write _test-input-stream "  copy-object *y, x\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+    # no errors
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-copy-object-non-addr:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x: int\n")
+    (write _test-input-stream "  var y: int\n")
+    (write _test-input-stream "  copy-object y, x\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-copy-object-non-addr: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt copy-object: two inouts with identical addr types expected"  "F - test-copy-object-non-addr: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-copy-object-non-addr: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-copy-object-non-equal:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x: (addr int)\n")
+    (write _test-input-stream "  var y: (addr boolean)\n")
+    (write _test-input-stream "  copy-object y, x\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-copy-object-non-equal: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt copy-object: two inouts with identical addr types expected"  "F - test-copy-object-non-equal: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-copy-object-non-equal: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-allocate-with-no-inout:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  allocate\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-allocate-with-no-inout: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt 'allocate' must have a single inout"  "F - test-allocate-with-no-inout: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-allocate-with-no-inout: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-allocate-with-too-many-inouts:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x: (addr handle int)\n")
+    (write _test-input-stream "  allocate x, 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-allocate-with-too-many-inouts: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt 'allocate' must have a single inout"  "F - test-allocate-with-too-many-inouts: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-allocate-with-too-many-inouts: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-allocate-with-output:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x/eax: boolean <- copy 0\n")
+    (write _test-input-stream "  var y/ecx: (addr handle int) <- copy 0\n")
+    (write _test-input-stream "  x <- allocate y\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-allocate-with-output: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt 'allocate' must not have any outputs"  "F - test-allocate-with-output: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-allocate-with-output: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-allocate-non-addr:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var y: (handle int)\n")
+    (write _test-input-stream "  allocate y\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-allocate-non-addr: output must be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt allocate: inout 'y' must have type (addr handle ...)"  "F - test-allocate-non-addr: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-allocate-non-addr: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-allocate-non-addr-handle:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var y/ecx: (addr int) <- copy 0\n")
+    (write _test-input-stream "  allocate y\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-allocate-non-addr-handle: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt allocate: inout 'y' must have type (addr handle ...)"  "F - test-allocate-non-addr-handle: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-allocate-non-addr-handle: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-allocate-deref-address:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var y/ecx: (addr addr handle int) <- copy 0\n")
+    (write _test-input-stream "  allocate *y\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+    # no errors
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-populate-with-no-inout:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  populate\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-populate-with-no-inout: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt 'populate' must have two inouts"  "F - test-populate-with-no-inout: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-populate-with-no-inout: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-populate-with-too-many-inouts:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x: (addr handle int)\n")
+    (write _test-input-stream "  populate x, 3, 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-populate-with-too-many-inouts: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt 'populate' must have two inouts"  "F - test-populate-with-too-many-inouts: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-populate-with-too-many-inouts: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-populate-with-output:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x/eax: boolean <- copy 0\n")
+    (write _test-input-stream "  var y/ecx: (addr handle int) <- copy 0\n")
+    (write _test-input-stream "  x <- populate y\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-populate-with-output: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt 'populate' must not have any outputs"  "F - test-populate-with-output: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-populate-with-output: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-populate-non-addr:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var y: (handle int)\n")
+    (write _test-input-stream "  populate y, 3\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-populate-non-addr: output must be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt populate: first inout 'y' must have type (addr handle array ...)"  "F - test-populate-non-addr: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-populate-non-addr: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-populate-non-addr-handle:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var y/ecx: (addr int) <- copy 0\n")
+    (write _test-input-stream "  populate y, 3\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-populate-non-addr-handle: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt populate: first inout 'y' must have type (addr handle array ...)"  "F - test-populate-non-addr-handle: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-populate-non-addr-handle: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-populate-non-addr-handle-array:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var y/ecx: (addr handle int) <- copy 0\n")
+    (write _test-input-stream "  populate y, 3\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-populate-non-addr-handle-array: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt populate: first inout 'y' must have type (addr handle array ...)"  "F - test-populate-non-addr-handle-array: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-populate-non-addr-handle-array: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-populate-deref-address:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var y/ecx: (addr addr handle array int) <- copy 0\n")
+    (write _test-input-stream "  populate *y, 3\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+    # no errors
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-populate-stream-with-no-inout:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  populate-stream\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-populate-stream-with-no-inout: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt 'populate-stream' must have two inouts"  "F - test-populate-stream-with-no-inout: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-populate-stream-with-no-inout: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-populate-stream-with-too-many-inouts:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x: (addr handle int)\n")
+    (write _test-input-stream "  populate-stream x, 3, 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-populate-stream-with-too-many-inouts: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt 'populate-stream' must have two inouts"  "F - test-populate-stream-with-too-many-inouts: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-populate-stream-with-too-many-inouts: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-populate-stream-with-output:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x/eax: boolean <- copy 0\n")
+    (write _test-input-stream "  var y/ecx: (addr handle int) <- copy 0\n")
+    (write _test-input-stream "  x <- populate-stream y\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-populate-stream-with-output: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt 'populate-stream' must not have any outputs"  "F - test-populate-stream-with-output: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-populate-stream-with-output: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-populate-stream-non-addr:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var y: (handle int)\n")
+    (write _test-input-stream "  populate-stream y, 3\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-populate-stream-non-addr: output must be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt populate-stream: first inout 'y' must have type (addr handle stream ...)"  "F - test-populate-stream-non-addr: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-populate-stream-non-addr: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-populate-stream-non-addr-handle:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var y/ecx: (addr int) <- copy 0\n")
+    (write _test-input-stream "  populate-stream y, 3\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-populate-stream-non-addr-handle: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt populate-stream: first inout 'y' must have type (addr handle stream ...)"  "F - test-populate-stream-non-addr-handle: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-populate-stream-non-addr-handle: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-populate-stream-non-addr-handle-stream:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var y/ecx: (addr handle int) <- copy 0\n")
+    (write _test-input-stream "  populate-stream y, 3\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-populate-stream-non-addr-handle-stream: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt populate-stream: first inout 'y' must have type (addr handle stream ...)"  "F - test-populate-stream-non-addr-handle-stream: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-populate-stream-non-addr-handle-stream: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-populate-stream-deref-address:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var y/ecx: (addr addr handle stream int) <- copy 0\n")
+    (write _test-input-stream "  populate-stream *y, 3\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+    # no errors
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-with-no-inout:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x/eax: int <- convert\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-convert-with-no-inout: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt 'convert' expects an inout"  "F - test-convert-with-no-inout: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-convert-with-no-inout: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-with-multiple-inouts:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x/eax: int <- convert 0, 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-convert-with-multiple-inouts: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt 'convert' must have just one inout"  "F - test-convert-with-multiple-inouts: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-convert-with-multiple-inouts: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-with-no-output:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  convert 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-convert-with-no-output: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt 'convert' expects an output"  "F - test-convert-with-no-output: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-convert-with-no-output: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-with-multiple-outputs:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x/eax: int <- copy 0\n")
+    (write _test-input-stream "  var y/ecx: int <- copy 0\n")
+    (write _test-input-stream "  x, y <- convert 0\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-convert-with-multiple-outputs: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt 'convert' must have just one output"  "F - test-convert-with-multiple-outputs: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-convert-with-multiple-outputs: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-deref-address:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x/eax: (addr int) <- copy 0\n")
+    (write _test-input-stream "  var y/xmm4: float <- convert *x\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+    # no errors
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-to-non-register:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x: float\n")
+    (write _test-input-stream "  var y: int\n")
+    (write _test-input-stream "  x <- convert y\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-convert-to-non-register: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt convert: output 'x' not in a register"  "F - test-convert-to-non-register: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-convert-to-non-register: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-invalid-inout-type:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x: boolean\n")
+    (write _test-input-stream "  var y/xmm1: float <- convert x\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-convert-invalid-inout-type: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt convert: inout 'x' must be an int or float"  "F - test-convert-invalid-inout-type: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-convert-invalid-inout-type: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-invalid-output-type:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x: float\n")
+    (write _test-input-stream "  var y/eax: boolean <- convert x\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-convert-invalid-output-type: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt convert: output 'y' must be an int or float"  "F - test-convert-invalid-output-type: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-convert-invalid-output-type: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-int-to-int:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x: int\n")
+    (write _test-input-stream "  var y/eax: int <- convert x\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-convert-int-to-int: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt convert: no need to convert int to int"  "F - test-convert-int-to-int: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-convert-int-to-int: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+test-convert-float-to-float:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    (clear-stream _test-error-stream)
+    (clear-stream $_test-error-buffered-file->buffer)
+    # var ed/edx: exit-descriptor = tailor-exit-descriptor(16)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    (tailor-exit-descriptor %edx 0x10)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  var x: float\n")
+    (write _test-input-stream "  var y/xmm6: float <- convert x\n")
+    (write _test-input-stream "}\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file _test-error-buffered-file %edx)
+    # registers except esp clobbered at this point
+    # restore ed
+    89/<- %edx 4/r32/esp
+    (flush _test-output-buffered-file)
+    (flush _test-error-buffered-file)
+#?     # dump _test-error-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-error-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-error-stream)
+#?     # }}}
+    # check output
+    (check-stream-equal _test-output-stream  ""  "F - test-convert-float-to-float: output should be empty")
+    (check-next-stream-line-equal _test-error-stream  "fn foo: stmt convert: no need to convert float to float"  "F - test-convert-float-to-float: error message")
+    # check that stop(1) was called
+    (check-ints-equal *(edx+4) 2 "F - test-convert-float-to-float: exit status")
+    # don't restore from ebp
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    5d/pop-to-ebp
+    c3/return
+
+#######################################################
+# Parsing
+#######################################################
+
+== data
+
+# Global state added to each var record when parsing a function
+Next-block-index:  # (addr int)
+    1/imm32
+
+Curr-block-depth:  # (addr int)
+    1/imm32
+
+== code
+
+parse-mu:  # in: (addr buffered-file), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # pseudocode
+    #   var curr-function: (addr handle function) = Program->functions
+    #   var curr-signature: (addr handle function) = Program->signatures
+    #   var curr-type: (addr handle typeinfo) = Program->types
+    #   var line: (stream byte 512)
+    #   var word-slice: slice
+    #   while true                                  # line loop
+    #     clear-stream(line)
+    #     read-line-buffered(in, line)
+    #     if (line->write == 0) break               # end of file
+    #     word-slice = next-mu-token(line)
+    #     if slice-empty?(word-slice)               # end of line
+    #       continue
+    #     else if slice-starts-with?(word-slice, "#")  # comment
+    #       continue                                # end of line
+    #     else if slice-equal?(word-slice, "fn")
+    #       var new-function: (handle function) = allocate(function)
+    #       var vars: (stack live-var 256)
+    #       populate-mu-function-header(line, new-function, vars)
+    #       populate-mu-function-body(in, new-function, vars)
+    #       assert(vars->top == 0)
+    #       *curr-function = new-function
+    #       curr-function = &new-function->next
+    #     else if slice-equal?(word-slice, "sig")
+    #       var new-function: (handle function) = allocate(function)
+    #       populate-mu-function-signature(line, new-function)
+    #       *curr-signature = new-function
+    #       curr-signature = &new-function->next
+    #     else if slice-equal?(word-slice, "type")
+    #       word-slice = next-mu-token(line)
+    #       type-id = pos-or-insert-slice(Type-id, word-slice)
+    #       var new-type: (handle typeinfo) = find-or-create-typeinfo(type-id)
+    #       assert(next-word(line) == "{")
+    #       populate-mu-type(in, new-type)
+    #     else
+    #       abort()
+    #
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # var curr-signature: (addr handle function) at *(ebp-4)
+    68/push _Program-signatures/imm32
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    52/push-edx
+    53/push-ebx
+    56/push-esi
+    57/push-edi
+    # var line/ecx: (stream byte 512)
+    81 5/subop/subtract %esp 0x200/imm32
+    68/push 0x200/imm32/size
+    68/push 0/imm32/read
+    68/push 0/imm32/write
+    89/<- %ecx 4/r32/esp
+    # var word-slice/edx: slice
+    68/push 0/imm32/end
+    68/push 0/imm32/start
+    89/<- %edx 4/r32/esp
+    # var curr-function/edi: (addr handle function)
+    bf/copy-to-edi _Program-functions/imm32
+    # var vars/ebx: (stack live-var 256)
+    81 5/subop/subtract %esp 0xc00/imm32
+    68/push 0xc00/imm32/size
+    68/push 0/imm32/top
+    89/<- %ebx 4/r32/esp
+    {
+$parse-mu:line-loop:
+      (clear-stream %ecx)
+      (read-line-buffered *(ebp+8) %ecx)
+      # if (line->write == 0) break
+      81 7/subop/compare *ecx 0/imm32
+      0f 84/jump-if-= break/disp32
+#?       # dump line {{{
+#?       (write 2 "parse-mu: ^")
+#?       (write-stream 2 %ecx)
+#?       (write 2 "$\n")
+#?       (rewind-stream %ecx)
+#?       # }}}
+      (next-mu-token %ecx %edx)
+      # if slice-empty?(word-slice) continue
+      (slice-empty? %edx)  # => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 85/jump-if-!= loop/disp32
+      # if (*word-slice->start == "#") continue
+      # . eax = *word-slice->start
+      8b/-> *edx 0/r32/eax
+      8a/copy-byte *eax 0/r32/AL
+      81 4/subop/and %eax 0xff/imm32
+      # . if (eax == '#') continue
+      3d/compare-eax-and 0x23/imm32/hash
+      0f 84/jump-if-= loop/disp32
+      # if (slice-equal?(word-slice, "fn")) parse a function
+      {
+$parse-mu:fn:
+        (slice-equal? %edx "fn")  # => eax
+        3d/compare-eax-and 0/imm32/false
+        0f 84/jump-if-= break/disp32
+        # var new-function/esi: (handle function)
+        68/push 0/imm32
+        68/push 0/imm32
+        89/<- %esi 4/r32/esp
+        # populate-mu-function(line, in, vars, new-function)
+        (allocate Heap *Function-size %esi)
+        # var new-function-addr/eax: (addr function)
+        (lookup *esi *(esi+4))  # => eax
+        # initialize vars
+        (clear-stack %ebx)
+        #
+        (populate-mu-function-header %ecx %eax %ebx *(ebp+0xc) *(ebp+0x10))
+        (populate-mu-function-body *(ebp+8) %eax %ebx *(ebp+0xc) *(ebp+0x10))
+        # *curr-function = new-function
+        8b/-> *esi 0/r32/eax
+        89/<- *edi 0/r32/eax
+        8b/-> *(esi+4) 0/r32/eax
+        89/<- *(edi+4) 0/r32/eax
+        # curr-function = &new-function->next
+        # . var tmp/eax: (addr function) = lookup(new-function)
+        (lookup *esi *(esi+4))  # => eax
+        # . curr-function = &tmp->next
+        8d/copy-address *(eax+0x20) 7/r32/edi  # Function-next
+        # reclaim new-function
+        81 0/subop/add %esp 8/imm32
+        #
+        e9/jump $parse-mu:line-loop/disp32
+      }
+      # if (slice-equal?(word-slice, "sig")) parse a function signature
+      # Function signatures are for providing types to SubX functions.
+      {
+$parse-mu:sig:
+        (slice-equal? %edx "sig")  # => eax
+        3d/compare-eax-and 0/imm32/false
+        0f 84/jump-if-= break/disp32
+        # edi = curr-function
+        57/push-edi
+        8b/-> *(ebp-4) 7/r32/edi
+        # var new-function/esi: (handle function)
+        68/push 0/imm32
+        68/push 0/imm32
+        89/<- %esi 4/r32/esp
+        # populate-mu-function(line, in, vars, new-function)
+        (allocate Heap *Function-size %esi)
+        # var new-function-addr/eax: (addr function)
+        (lookup *esi *(esi+4))  # => eax
+        #
+        (populate-mu-function-signature %ecx %eax *(ebp+0xc) *(ebp+0x10))
+        # *curr-signature = new-function
+        8b/-> *esi 0/r32/eax
+        89/<- *edi 0/r32/eax
+        8b/-> *(esi+4) 0/r32/eax
+        89/<- *(edi+4) 0/r32/eax
+        # curr-signature = &new-function->next
+        # . var tmp/eax: (addr function) = lookup(new-function)
+        (lookup *esi *(esi+4))  # => eax
+        # . curr-function = &tmp->next
+        8d/copy-address *(eax+0x20) 7/r32/edi  # Function-next
+        # reclaim new-function
+        81 0/subop/add %esp 8/imm32
+        # save curr-function
+        89/<- *(ebp-4) 7/r32/edi
+        # restore edi
+        5f/pop-to-edi
+        #
+        e9/jump $parse-mu:line-loop/disp32
+      }
+      # if (slice-equal?(word-slice, "type")) parse a type (struct/record) definition
+      {
+$parse-mu:type:
+        (slice-equal? %edx "type")  # => eax
+        3d/compare-eax-and 0/imm32
+        0f 84/jump-if-= break/disp32
+        (next-mu-token %ecx %edx)
+        # var type-id/eax: int
+        (pos-or-insert-slice Type-id %edx)  # => eax
+        # spill
+        51/push-ecx
+        # var new-type/ecx: (handle typeinfo)
+        68/push 0/imm32
+        68/push 0/imm32
+        89/<- %ecx 4/r32/esp
+        (find-or-create-typeinfo %eax %ecx)
+        #
+        (lookup *ecx *(ecx+4))  # => eax
+        # TODO: ensure that 'line' has nothing else but '{'
+#? (dump-typeinfos "=== aaa\n")
+        (populate-mu-type *(ebp+8) %eax *(ebp+0xc) *(ebp+0x10))  # => eax
+#? (dump-typeinfos "=== zzz\n")
+        # reclaim new-type
+        81 0/subop/add %esp 8/imm32
+        # restore
+        59/pop-to-ecx
+        e9/jump $parse-mu:line-loop/disp32
+      }
+      # otherwise abort
+      e9/jump $parse-mu:error1/disp32
+    } # end line loop
+$parse-mu:end:
+    # . reclaim locals
+    81 0/subop/add %esp 0x20c/imm32  # line
+    81 0/subop/add %esp 0xc08/imm32  # vars
+    81 0/subop/add %esp 8/imm32
+    # . restore registers
+    5f/pop-to-edi
+    5e/pop-to-esi
+    5b/pop-to-ebx
+    5a/pop-to-edx
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . reclaim local
+    81 0/subop/add %esp 4/imm32
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+$parse-mu:error1:
+    # error("unexpected top-level command: " word-slice "\n")
+    (write-buffered *(ebp+0xc) "unexpected top-level command: ")
+    (write-slice-buffered *(ebp+0xc) %edx)
+    (write-buffered *(ebp+0xc) "\n")
+    (flush *(ebp+0xc))
+    (stop *(ebp+0x10) 1)
+    # never gets here
+
+$parse-mu:error2:
+    # error(vars->top " vars not reclaimed after fn '" new-function->name "'\n")
+    (write-int32-hex-buffered *(ebp+0xc) *ebx)
+    (write-buffered *(ebp+0xc) " vars not reclaimed after fn '")
+    (write-slice-buffered *(ebp+0xc) *eax)  # Function-name
+    (write-buffered *(ebp+0xc) "'\n")
+    (flush *(ebp+0xc))
+    (stop *(ebp+0x10) 1)
+    # never gets here
+
+# scenarios considered:
+# ✗ fn foo  # no block
+# ✓ fn foo {
+# ✗ fn foo { {
+# ✗ fn foo { }
+# ✗ fn foo { } {
+# ✗ fn foo x {
+# ✗ fn foo x: {
+# ✓ fn foo x: int {
+# ✓ fn foo x: int {
+# ✓ fn foo x: int -> _/eax: int {
+# TODO:
+#   disallow outputs of type `(... addr ...)`
+#   disallow inputs of type `(... addr ... addr ...)`
+populate-mu-function-header:  # first-line: (addr stream byte), out: (addr function), vars: (addr stack live-var), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # pseudocode:
+    #   var word-slice: slice
+    #   next-mu-token(first-line, word-slice)
+    #   if slice-empty?(word-slice) abort
+    #   assert(word-slice not in '{' '}' '->')
+    #   out->name = slice-to-string(word-slice)
+    #   ## inouts
+    #   while true
+    #     word-slice = next-mu-token(first-line)
+    #     if slice-empty?(word-slice) abort
+    #     if (word-slice == '{') goto done
+    #     if (word-slice == '->') break
+    #     assert(word-slice != '}')
+    #     var v: (handle var) = parse-var-with-type(word-slice, first-line)
+    #     assert(v->register == null)
+    #     # v->block-depth is implicitly 0
+    #     out->inouts = append(v, out->inouts)
+    #     push(vars, {v, false})
+    #   ## outputs
+    #   while true
+    #     word-slice = next-mu-token(first-line)
+    #     if slice-empty?(word-slice) abort
+    #     if (word-slice == '{') break
+    #     assert(word-slice not in '}' '->')
+    #     var v: (handle var) = parse-var-with-type(word-slice, first-line)
+    #     assert(v->register != null)
+    #     assert(v->name == "_")
+    #     out->outputs = append(v, out->outputs)
+    #   done:
+    #
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    52/push-edx
+    53/push-ebx
+    57/push-edi
+    # edi = out
+    8b/-> *(ebp+0xc) 7/r32/edi
+    # var word-slice/ecx: slice
+    68/push 0/imm32/end
+    68/push 0/imm32/start
+    89/<- %ecx 4/r32/esp
+    # var v/ebx: (handle var)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %ebx 4/r32/esp
+    # read function name
+    (next-mu-token *(ebp+8) %ecx)
+    # error checking
+    # if slice-empty?(word-slice) abort
+    (slice-empty? %ecx)  # => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
+    # if (word-slice == '{') abort
+    (slice-equal? %ecx "{")   # => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
+    # if (word-slice == '->') abort
+    (slice-equal? %ecx "->")   # => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
+    # if (word-slice == '}') abort
+    (slice-equal? %ecx "}")   # => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
+    # if word-slice already defined, abort
+    (function-exists? %ecx)  # => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 85/jump-if-!= $populate-mu-function-header:error-duplicate/disp32
+    #
+    (slice-starts-with? %ecx "break")  # => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 85/jump-if-!= $populate-mu-function-header:error-break/disp32
+    (slice-starts-with? %ecx "loop")  # => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 85/jump-if-!= $populate-mu-function-header:error-loop/disp32
+    (slice-equal? %ecx "lookup")  # => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 85/jump-if-!= $populate-mu-function-header:error-lookup/disp32
+    # save function name
+    (slice-to-string Heap %ecx %edi)  # Function-name
+    # save function inouts
+    {
+$populate-mu-function-header:check-for-inout:
+      (next-mu-token *(ebp+8) %ecx)
+      # if slice-empty?(word-slice) abort
+      (slice-empty? %ecx)  # => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
+      # if (word-slice == '{') goto done
+      (slice-equal? %ecx "{")   # => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 85/jump-if-!= $populate-mu-function-header:done/disp32
+      # if (word-slice == '->') break
+      (slice-equal? %ecx "->")   # => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 85/jump-if-!= break/disp32
+      # if (word-slice == '}') abort
+      (slice-equal? %ecx "}")   # => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
+      # v = parse-var-with-type(word-slice, first-line)
+      (lookup *edi *(edi+4))  # Function-name Function-name => eax
+      (parse-var-with-type %ecx *(ebp+8) %ebx %eax *(ebp+0x14) *(ebp+0x18))
+      # if (v->register != null) abort
+      # . eax: (addr var) = lookup(v)
+      (lookup *ebx *(ebx+4))  # => eax
+      81 7/subop/compare *(eax+0x18) 0/imm32  # Var-register
+      0f 85/jump-if-!= $populate-mu-function-header:error2/disp32
+      # if function name is not "main"
+      #    and v->type contains an 'addr' anywhere except the start, abort
+      {
+        (lookup *edi *(edi+4))  # Function-name Function-name => eax
+        (string-equal? %eax "main")  # => eax
+        3d/compare-eax-and 0/imm32/false
+        75/jump-if-!= break/disp8
+        (lookup *ebx *(ebx+4))  # => eax
+        (addr-payload-contains-addr? %eax)  # => eax
+        3d/compare-eax-and 0/imm32/false
+        0f 85/jump-if-!= $populate-mu-function-header:error-nested-addr-inout/disp32
+      }
+      # v->block-depth is implicitly 0
+      #
+      # out->inouts = append(v, out->inouts)
+      8d/copy-address *(edi+8) 0/r32/eax  # Function-inouts
+      (append-list Heap  *ebx *(ebx+4)  *(edi+8) *(edi+0xc)  %eax)  # Function-inouts, Function-inouts
+      # push(vars, {v, false})
+      (push *(ebp+0x10) *ebx)
+      (push *(ebp+0x10) *(ebx+4))
+      (push *(ebp+0x10) 0)  # false
+      #
+      e9/jump loop/disp32
+    }
+    # save function outputs
+    {
+$populate-mu-function-header:check-for-out:
+      (next-mu-token *(ebp+8) %ecx)
+      # if slice-empty?(word-slice) abort
+      (slice-empty? %ecx)  # => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
+      # if (word-slice == '{') break
+      (slice-equal? %ecx "{")   # => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 85/jump-if-!= break/disp32
+      # if (word-slice == '->') abort
+      (slice-equal? %ecx "->")   # => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
+      # if (word-slice == '}') abort
+      (slice-equal? %ecx "}")   # => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
+      # v = parse-var-with-type(word-slice, first-line)
+      (lookup *edi *(edi+4))  # Function-name Function-name => eax
+      (parse-var-with-type %ecx *(ebp+8) %ebx %eax *(ebp+0x14) *(ebp+0x18))
+      # assert(var->register != null)
+      # . eax: (addr var) = lookup(v)
+      (lookup *ebx *(ebx+4))  # => eax
+      81 7/subop/compare *(eax+0x18) 0/imm32  # Var-register
+      0f 84/jump-if-= $populate-mu-function-header:error3/disp32
+      # if (var->name != "_") abort
+      (lookup *eax *(eax+4))  # Var-name Var-name => eax
+      (string-equal? %eax "_")  # => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 84/jump-if-= $populate-mu-function-header:error4/disp32
+      # if v->type is an addr, abort
+      (lookup *ebx *(ebx+4))  # => eax
+      (lookup *(eax+8) *(eax+0xc))  # Var-type Var-type => eax
+      (is-mu-addr-type? %eax)  # => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 85/jump-if-!= $populate-mu-function-header:error-addr-output/disp32
+      # out->outputs = append(v, out->outputs)
+      8d/copy-address *(edi+0x10) 0/r32/eax  # Function-outputs
+      (append-list Heap  *ebx *(ebx+4)  *(edi+0x10) *(edi+0x14)  %eax)  # Function-outputs, Function-outputs
+      #
+      e9/jump loop/disp32
+    }
+$populate-mu-function-header:done:
+    (check-no-tokens-left *(ebp+8))
+$populate-mu-function-header:end:
+    # . reclaim locals
+    81 0/subop/add %esp 0x10/imm32
+    # . restore registers
+    5f/pop-to-edi
+    5b/pop-to-ebx
+    5a/pop-to-edx
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+$populate-mu-function-header:error1:
+    # error("function header not in form 'fn <name> {'")
+    (write-buffered *(ebp+0x14) "function header not in form 'fn <name> [inouts] [-> outputs] {' -- '")
+    (flush *(ebp+0x14))
+    (rewind-stream *(ebp+8))
+    (write-stream-data *(ebp+0x14) *(ebp+8))
+    (write-buffered *(ebp+0x14) "'\n")
+    (flush *(ebp+0x14))
+    (stop *(ebp+0x18) 1)
+    # never gets here
+
+$populate-mu-function-header:error2:
+    # error("fn " fn ": function inout '" var "' cannot be in a register")
+    (write-buffered *(ebp+0x14) "fn ")
+    50/push-eax
+    (lookup *edi *(edi+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x14) %eax)
+    58/pop-to-eax
+    (write-buffered *(ebp+0x14) ": function inout '")
+    (lookup *eax *(eax+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x14) %eax)
+    (write-buffered *(ebp+0x14) "' cannot be in a register")
+    (flush *(ebp+0x14))
+    (stop *(ebp+0x18) 1)
+    # never gets here
+
+$populate-mu-function-header:error3:
+    # error("fn " fn ": function output '" var "' must be in a register")
+    (write-buffered *(ebp+0x14) "fn ")
+    50/push-eax
+    (lookup *edi *(edi+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x14) %eax)
+    58/pop-to-eax
+    (write-buffered *(ebp+0x14) ": function output '")
+    (lookup *ebx *(ebx+4))  # => eax
+    (lookup *eax *(eax+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x14) %eax)
+    (write-buffered *(ebp+0x14) "' must be in a register, in instruction '")
+    (rewind-stream *(ebp+8))
+    (write-stream-data *(ebp+0x14) *(ebp+8))
+    (write-buffered *(ebp+0x14) "'\n")
+    (flush *(ebp+0x14))
+    (stop *(ebp+0x18) 1)
+    # never gets here
+
+$populate-mu-function-header:error4:
+    # error("fn " fn ": function outputs cannot be named; rename '" var "' in the header to '_'")
+    (write-buffered *(ebp+0x14) "fn ")
+    50/push-eax
+    (lookup *edi *(edi+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x14) %eax)
+    58/pop-to-eax
+    (write-buffered *(ebp+0x14) ": function outputs cannot be named; rename '")
+    (lookup *ebx *(ebx+4))  # => eax
+    (lookup *eax *(eax+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x14) %eax)
+    (write-buffered *(ebp+0x14) "' in the header to '_'\n")
+    (flush *(ebp+0x14))
+    (stop *(ebp+0x18) 1)
+    # never gets here
+
+$populate-mu-function-header:error-duplicate:
+    (write-buffered *(ebp+0x14) "fn ")
+    (write-slice-buffered *(ebp+0x14) %ecx)
+    (write-buffered *(ebp+0x14) " defined more than once\n")
+    (flush *(ebp+0x14))
+    (stop *(ebp+0x18) 1)
+    # never gets here
+
+$populate-mu-function-header:error-break:
+    (write-buffered *(ebp+0x14) "Sorry, I've reserved all function names starting with 'break' for now. Please contact mu@akkartik.com.\n")
+    (flush *(ebp+0x14))
+    (stop *(ebp+0x18) 1)
+    # never gets here
+
+$populate-mu-function-header:error-loop:
+    (write-buffered *(ebp+0x14) "Sorry, I've reserved all function names starting with 'loop' for now. Please contact mu@akkartik.com.\n")
+    (flush *(ebp+0x14))
+    (stop *(ebp+0x18) 1)
+    # never gets here
+
+$populate-mu-function-header:error-lookup:
+    (write-buffered *(ebp+0x14) "cannot define a function called 'lookup'\n")
+    (flush *(ebp+0x14))
+    (stop *(ebp+0x18) 1)
+    # never gets here
+
+$populate-mu-function-header:error-addr-output:
+    # error("fn " fn ": output cannot have an addr type; that could allow unsafe addresses to escape the function")
+    (write-buffered *(ebp+0x14) "fn ")
+    50/push-eax
+    (lookup *edi *(edi+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x14) %eax)
+    58/pop-to-eax
+    (write-buffered *(ebp+0x14) ": output cannot have an addr type; that could allow unsafe addresses to escape the function\n")
+    (flush *(ebp+0x14))
+    (stop *(ebp+0x18) 1)
+    # never gets here
+
+$populate-mu-function-header:error-nested-addr-inout:
+    # error("fn " fn ": inout '" var "' cannot contain 'addr' anywhere in the type except the first word; that could allow unsafe addresses to escape the function")
+    (write-buffered *(ebp+0x14) "fn ")
+    (lookup *edi *(edi+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x14) %eax)
+    (write-buffered *(ebp+0x14) ": inout '")
+    (lookup *ebx *(ebx+4))  # => eax
+    (lookup *eax *(eax+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x14) %eax)
+    (write-buffered *(ebp+0x14) "' cannot contain 'addr' anywhere in the type except the first word; that could allow unsafe addresses to escape the function\n")
+    (flush *(ebp+0x14))
+    (stop *(ebp+0x18) 1)
+    # never gets here
+
+# scenarios considered:
+# ✓ fn foo
+# ✗ fn foo {
+# ✓ fn foo x
+# ✓ fn foo x: int
+# ✓ fn foo x: int -> _/eax: int
+# TODO:
+#   disallow outputs of type `(... addr ...)`
+#   disallow inputs of type `(... addr ... addr ...)`
+populate-mu-function-signature:  # first-line: (addr stream byte), out: (addr function), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # pseudocode:
+    #   var word-slice: slice
+    #   next-mu-token(first-line, word-slice)
+    #   assert(word-slice not in '{' '}' '->')
+    #   out->name = slice-to-string(word-slice)
+    #   ## inouts
+    #   while true
+    #     word-slice = next-mu-token(first-line)
+    #     if slice-empty?(word-slice) break
+    #     if (word-slice == '->') break
+    #     assert(word-slice not in '{' '}')
+    #     var v: (handle var) = parse-var-with-type(word-slice, first-line)
+    #     assert(v->register == null)
+    #     # v->block-depth is implicitly 0
+    #     out->inouts = append(v, out->inouts)
+    #   ## outputs
+    #   while true
+    #     word-slice = next-mu-token(first-line)
+    #     if slice-empty?(word-slice) break
+    #     assert(word-slice not in '{' '}' '->')
+    #     var v: (handle var) = parse-var-with-type(word-slice, first-line)
+    #     assert(v->register != null)
+    #     out->outputs = append(v, out->outputs)
+    #
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    52/push-edx
+    53/push-ebx
+    57/push-edi
+    # edi = out
+    8b/-> *(ebp+0xc) 7/r32/edi
+    # var word-slice/ecx: slice
+    68/push 0/imm32/end
+    68/push 0/imm32/start
+    89/<- %ecx 4/r32/esp
+    # var v/ebx: (handle var)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %ebx 4/r32/esp
+    # read function name
+    (next-mu-token *(ebp+8) %ecx)
+    # error checking
+    # if (word-slice == '{') abort
+    (slice-equal? %ecx "{")   # => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 85/jump-if-!= $populate-mu-function-signature:error1/disp32
+    # if (word-slice == '->') abort
+    (slice-equal? %ecx "->")   # => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 85/jump-if-!= $populate-mu-function-signature:error1/disp32
+    # if (word-slice == '}') abort
+    (slice-equal? %ecx "}")   # => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 85/jump-if-!= $populate-mu-function-signature:error1/disp32
+    # if word-slice already defined, abort
+    (function-exists? %ecx)  # => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 85/jump-if-!= $populate-mu-function-signature:error-duplicate/disp32
+    #
+    (slice-starts-with? %ecx "break")  # => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 85/jump-if-!= $populate-mu-function-signature:error-break/disp32
+    (slice-starts-with? %ecx "loop")  # => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 85/jump-if-!= $populate-mu-function-signature:error-loop/disp32
+    # save function name
+    (slice-to-string Heap %ecx %edi)  # Function-name
+    # save function inouts
+    {
+$populate-mu-function-signature:check-for-inout:
+      (next-mu-token *(ebp+8) %ecx)
+      (slice-empty? %ecx)  # => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 85/jump-if-!= break/disp32
+      # if (word-slice == '->') break
+      (slice-equal? %ecx "->")   # => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 85/jump-if-!= break/disp32
+      # if (word-slice == '{') abort
+      (slice-equal? %ecx "{")   # => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 85/jump-if-!= $populate-mu-function-signature:error1/disp32
+      # if (word-slice == '}') abort
+      (slice-equal? %ecx "}")   # => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 85/jump-if-!= $populate-mu-function-signature:error1/disp32
+      # v = parse-var-with-type(word-slice, first-line)
+      (lookup *edi *(edi+4))  # Function-name Function-name => eax
+      (parse-var-with-type %ecx *(ebp+8) %ebx %eax *(ebp+0x10) *(ebp+0x14))
+      # if (v->register != null) abort
+      # . eax: (addr var) = lookup(v)
+      (lookup *ebx *(ebx+4))  # => eax
+      81 7/subop/compare *(eax+0x18) 0/imm32  # Var-register
+      0f 85/jump-if-!= $populate-mu-function-signature:error2/disp32
+      # if function name is not "main"
+      #    and v->type contains an 'addr' anywhere except the start, abort
+      {
+        (lookup *edi *(edi+4))  # Function-name Function-name => eax
+        (string-equal? %eax "main")  # => eax
+        3d/compare-eax-and 0/imm32/false
+        75/jump-if-!= break/disp8
+        (lookup *ebx *(ebx+4))  # => eax
+        (addr-payload-contains-addr? %eax)  # => eax
+        3d/compare-eax-and 0/imm32/false
+        0f 85/jump-if-!= $populate-mu-function-signature:error-nested-addr-inout/disp32
+      }
+      # assert(v->register == null)
+      # . eax: (addr var) = lookup(v)
+      (lookup *ebx *(ebx+4))  # => eax
+      81 7/subop/compare *(eax+0x18) 0/imm32  # Var-register
+      0f 85/jump-if-!= $populate-mu-function-signature:error2/disp32
+      # v->block-depth is implicitly 0
+      #
+      # out->inouts = append(v, out->inouts)
+      8d/copy-address *(edi+8) 0/r32/eax  # Function-inouts
+      (append-list Heap  *ebx *(ebx+4)  *(edi+8) *(edi+0xc)  %eax)  # Function-inouts, Function-inouts
+      #
+      e9/jump loop/disp32
+    }
+    # save function outputs
+    {
+$populate-mu-function-signature:check-for-out:
+      (next-mu-token *(ebp+8) %ecx)
+      (slice-empty? %ecx)  # => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 85/jump-if-!= break/disp32
+      # if (word-slice == '{') abort
+      (slice-equal? %ecx "{")   # => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 85/jump-if-!= $populate-mu-function-signature:error1/disp32
+      # if (word-slice == '->') abort
+      (slice-equal? %ecx "->")   # => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 85/jump-if-!= $populate-mu-function-signature:error1/disp32
+      # if (word-slice == '}') abort
+      (slice-equal? %ecx "}")   # => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 85/jump-if-!= $populate-mu-function-signature:error1/disp32
+      # v = parse-var-with-type(word-slice, first-line)
+      (lookup *edi *(edi+4))  # Function-name Function-name => eax
+      (parse-var-with-type %ecx *(ebp+8) %ebx %eax *(ebp+0x10) *(ebp+0x14))
+      # assert(var->register != null)
+      # . eax: (addr var) = lookup(v)
+      (lookup *ebx *(ebx+4))  # => eax
+      81 7/subop/compare *(eax+0x18) 0/imm32  # Var-register
+      0f 84/jump-if-= $populate-mu-function-signature:error3/disp32
+      # if (var->name != "_") abort
+      (lookup *eax *(eax+4))  # Var-name Var-name => eax
+      (string-equal? %eax "_")  # => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 84/jump-if-= $populate-mu-function-signature:error4/disp32
+      # if function name is not "lookup"
+      #    and v->type is an addr, abort
+      {
+        (lookup *edi *(edi+4))  # Function-name Function-name => eax
+        (string-equal? %eax "lookup")  # => eax
+        3d/compare-eax-and 0/imm32/false
+        75/jump-if-!= break/disp8
+        (lookup *ebx *(ebx+4))  # => eax
+        (lookup *(eax+8) *(eax+0xc))  # Var-type Var-type => eax
+        (is-mu-addr-type? %eax)  # => eax
+        3d/compare-eax-and 0/imm32/false
+        0f 85/jump-if-!= $populate-mu-function-signature:error-addr-output/disp32
+      }
+      # out->outputs = append(v, out->outputs)
+      8d/copy-address *(edi+0x10) 0/r32/eax  # Function-outputs
+      (append-list Heap  *ebx *(ebx+4)  *(edi+0x10) *(edi+0x14)  %eax)  # Function-outputs, Function-outputs
+      #
+      e9/jump loop/disp32
+    }
+$populate-mu-function-signature:done:
+    (check-no-tokens-left *(ebp+8))
+$populate-mu-function-signature:end:
+    # . reclaim locals
+    81 0/subop/add %esp 0x10/imm32
+    # . restore registers
+    5f/pop-to-edi
+    5b/pop-to-ebx
+    5a/pop-to-edx
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+$populate-mu-function-signature:error1:
+    # error("function signature not in form 'fn <name> {'")
+    (write-buffered *(ebp+0x10) "function signature not in form 'fn <name> [inouts] [-> outputs] {' -- '")
+    (flush *(ebp+0x10))
+    (rewind-stream *(ebp+8))
+    (write-stream-data *(ebp+0x10) *(ebp+8))
+    (write-buffered *(ebp+0x10) "'\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$populate-mu-function-signature:error2:
+    # error("fn " fn ": function inout '" var "' cannot be in a register")
+    (write-buffered *(ebp+0x10) "fn ")
+    50/push-eax
+    (lookup *edi *(edi+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    58/pop-to-eax
+    (write-buffered *(ebp+0x10) ": function inout '")
+    (lookup *eax *(eax+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "' cannot be in a register")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$populate-mu-function-signature:error3:
+    # error("fn " fn ": function output '" var "' must be in a register")
+    (write-buffered *(ebp+0x10) "fn ")
+    50/push-eax
+    (lookup *edi *(edi+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    58/pop-to-eax
+    (write-buffered *(ebp+0x10) ": function output '")
+    (lookup *ebx *(ebx+4))  # => eax
+    (lookup *eax *(eax+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "' must be in a register, in instruction '")
+    (rewind-stream *(ebp+8))
+    (write-stream-data *(ebp+0x10) *(ebp+8))
+    (write-buffered *(ebp+0x10) "'\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$populate-mu-function-signature:error4:
+    # error("fn " fn ": function outputs cannot be named; rename '" var "' in the header to '_'")
+    (write-buffered *(ebp+0x10) "fn ")
+    50/push-eax
+    (lookup *edi *(edi+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    58/pop-to-eax
+    (write-buffered *(ebp+0x10) ": function outputs cannot be named; rename '")
+    (lookup *ebx *(ebx+4))  # => eax
+    (lookup *eax *(eax+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "' in the header to '_'\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$populate-mu-function-signature:error-duplicate:
+    (write-buffered *(ebp+0x10) "fn ")
+    (write-slice-buffered *(ebp+0x10) %ecx)
+    (write-buffered *(ebp+0x10) " defined more than once\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$populate-mu-function-signature:error-break:
+    (write-buffered *(ebp+0x10) "Sorry, I've reserved all function names starting with 'break' for now. Please contact mu@akkartik.com.\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$populate-mu-function-signature:error-loop:
+    (write-buffered *(ebp+0x10) "Sorry, I've reserved all function names starting with 'loop' for now. Please contact mu@akkartik.com.\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$populate-mu-function-signature:error-addr-output:
+    # error("fn " fn ": output cannot have an addr type; that could allow unsafe addresses to escape the function")
+    (write-buffered *(ebp+0x10) "fn ")
+    50/push-eax
+    (lookup *edi *(edi+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    58/pop-to-eax
+    (write-buffered *(ebp+0x10) ": output cannot have an addr type; that could allow unsafe addresses to escape the function\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$populate-mu-function-signature:error-nested-addr-inout:
+    # error("fn " fn ": inout '" var "' cannot contain 'addr' anywhere in the type except the first word; that could allow unsafe addresses to escape the function")
+    (write-buffered *(ebp+0x10) "fn ")
+    (lookup *edi *(edi+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": inout '")
+    (lookup *ebx *(ebx+4))  # => eax
+    (lookup *eax *(eax+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "' cannot contain 'addr' anywhere in the type except the first word; that could allow unsafe addresses to escape the function\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+addr-payload-contains-addr?:  # v: (addr var) -> result/eax: boolean
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # var t/eax: (addr type-tree) = v->type
+    8b/-> *(ebp+8) 0/r32/eax
+    (lookup *(eax+8) *(eax+0xc))  # Var-type Var-type => eax
+    # if t->right contains addr, return true
+    (lookup *(eax+0xc) *(eax+0x10))  # Type-tree-right Type-tree-right => eax
+    (type-tree-contains? %eax 2)  # addr => eax
+    # we don't have to look at t->left as long as it's guaranteed to be an atom
+$addr-payload-contains-addr?:end:
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+type-tree-contains?:  # t: (addr type-tree), n: type-id -> result/eax: boolean
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    51/push-ecx
+    # if t is null, return false
+    8b/-> *(ebp+8) 0/r32/eax
+    3d/compare-eax-and 0/imm32
+    0f 84/jump-if-= $type-tree-contains?:end/disp32  # eax changes type
+    # if t is an atom, return (t->value == n)
+    81 7/subop/compare *eax 0/imm32/false
+    {
+      74/jump-if-= break/disp8
+      8b/-> *(ebp+0xc) 1/r32/ecx
+      39/compare *(eax+4) 1/r32/ecx  # Type-tree-value
+      0f 94/set-if-= %al
+      81 4/subop/and %eax 0xff/imm32
+      eb/jump $type-tree-contains?:end/disp8
+    }
+    # if t->left contains n, return true
+    (lookup *(eax+4) *(eax+8))  # Type-tree-left Type-tree-left => eax
+    (type-tree-contains? %eax *(ebp+0xc))  # => eax
+    3d/compare-eax-and 0/imm32/false
+    75/jump-if-!= $type-tree-contains?:end/disp8
+    # otherwise return whether t->right contains n
+    8b/-> *(ebp+8) 0/r32/eax
+    (lookup *(eax+0xc) *(eax+0x10))  # Type-tree-right Type-tree-right => eax
+    (type-tree-contains? %eax *(ebp+0xc))  # => eax
+$type-tree-contains?:end:
+    # . restore registers
+    59/pop-to-ecx
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+function-exists?:  # s: (addr slice) -> result/eax: boolean
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    51/push-ecx
+    # var curr/ecx: (addr function) = functions
+    (lookup *_Program-functions *_Program-functions->payload)  # => eax
+    89/<- %ecx 0/r32/eax
+    {
+      # if (curr == null) break
+      81 7/subop/compare %ecx 0/imm32
+      74/jump-if-= break/disp8
+      # if (curr->name == s) return true
+      {
+        (lookup *ecx *(ecx+4))  # Function-name Function-name => eax
+        (slice-equal? *(ebp+8) %eax)  # => eax
+        3d/compare-eax-and 0/imm32/false
+        74/jump-if-= break/disp8
+        b8/copy-to-eax 1/imm32/true
+        e9/jump $function-exists?:end/disp32
+      }
+      # curr = curr->next
+      (lookup *(ecx+0x20) *(ecx+0x24))  # Function-next Function-next => eax
+      89/<- %ecx 0/r32/eax
+      #
+      eb/jump loop/disp8
+    }
+    # var curr/ecx: (addr function) = signatures
+    (lookup *_Program-signatures *_Program-signatures->payload)  # => eax
+    89/<- %ecx 0/r32/eax
+    {
+      # if (curr == null) break
+      81 7/subop/compare %ecx 0/imm32
+      74/jump-if-= break/disp8
+      # if (curr->name == s) return true
+      {
+        (lookup *ecx *(ecx+4))  # Function-name Function-name => eax
+        (slice-equal? *(ebp+8) %eax)  # => eax
+        3d/compare-eax-and 0/imm32/false
+        74/jump-if-= break/disp8
+        b8/copy-to-eax 1/imm32/true
+        eb/jump $function-exists?:end/disp8
+      }
+      # curr = curr->next
+      (lookup *(ecx+0x20) *(ecx+0x24))  # Function-next Function-next => eax
+      89/<- %ecx 0/r32/eax
+      #
+      eb/jump loop/disp8
+    }
+    # return false
+    b8/copy-to-eax 0/imm32/false
+$function-exists?:end:
+    # . restore registers
+    59/pop-to-ecx
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-function-header-with-arg:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    8b/-> *Primitive-type-ids 0/r32/eax
+    89/<- *Type-id 0/r32/eax  # stream-write
+    c7 0/subop/copy *_Program-functions 0/imm32
+    c7 0/subop/copy *_Program-functions->payload 0/imm32
+    c7 0/subop/copy *_Program-types 0/imm32
+    c7 0/subop/copy *_Program-types->payload 0/imm32
+    c7 0/subop/copy *_Program-signatures 0/imm32
+    c7 0/subop/copy *_Program-signatures->payload 0/imm32
+    (clear-stream _test-input-stream)
+    (write _test-input-stream "foo n: int {\n")
+    # var result/ecx: function
+    2b/subtract *Function-size 4/r32/esp
+    89/<- %ecx 4/r32/esp
+    (zero-out %ecx *Function-size)
+    # var vars/ebx: (stack live-var 16)
+    81 5/subop/subtract %esp 0xc0/imm32
+    68/push 0xc0/imm32/size
+    68/push 0/imm32/top
+    89/<- %ebx 4/r32/esp
+    # convert
+    (populate-mu-function-header _test-input-stream %ecx %ebx Stderr 0)
+    # check result->name
+    (lookup *ecx *(ecx+4))  # Function-name Function-name => eax
+    (check-strings-equal %eax "foo" "F - test-function-header-with-arg/name")
+    # var v/edx: (addr var) = result->inouts->value
+    (lookup *(ecx+8) *(ecx+0xc))  # Function-inouts Function-inouts => eax
+    (lookup *eax *(eax+4))  # List-value List-value => eax
+    89/<- %edx 0/r32/eax
+    # check v->name
+    (lookup *edx *(edx+4))  # Var-name Var-name => eax
+    (check-strings-equal %eax "n" "F - test-function-header-with-arg/inout:0")
+    # check v->type
+    (lookup *(edx+8) *(edx+0xc))  # Var-type Var-type => eax
+    (check-ints-equal *eax 1 "F - test-function-header-with-arg/inout:0/type:0")  # Type-tree-is-atom
+    (check-ints-equal *(eax+4) 1 "F - test-function-header-with-arg/inout:0/type:1")  # Type-tree-value
+    (check-ints-equal *(eax+0xc) 0 "F - test-function-header-with-arg/inout:0/type:2")  # Type-tree-right
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-function-header-with-multiple-args:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    8b/-> *Primitive-type-ids 0/r32/eax
+    89/<- *Type-id 0/r32/eax  # stream-write
+    c7 0/subop/copy *_Program-functions 0/imm32
+    c7 0/subop/copy *_Program-functions->payload 0/imm32
+    c7 0/subop/copy *_Program-types 0/imm32
+    c7 0/subop/copy *_Program-types->payload 0/imm32
+    c7 0/subop/copy *_Program-signatures 0/imm32
+    c7 0/subop/copy *_Program-signatures->payload 0/imm32
+    (clear-stream _test-input-stream)
+    (write _test-input-stream "foo a: int, b: int c: int {\n")
+    # result/ecx: function
+    2b/subtract *Function-size 4/r32/esp
+    89/<- %ecx 4/r32/esp
+    (zero-out %ecx *Function-size)
+    # var vars/ebx: (stack live-var 16)
+    81 5/subop/subtract %esp 0xc0/imm32
+    68/push 0xc0/imm32/size
+    68/push 0/imm32/top
+    89/<- %ebx 4/r32/esp
+    # convert
+    (populate-mu-function-header _test-input-stream %ecx %ebx Stderr 0)
+    # check result->name
+    (lookup *ecx *(ecx+4))  # Function-name Function-name => eax
+    (check-strings-equal %eax "foo" "F - test-function-header-with-multiple-args/name")
+    # var inouts/edx: (addr list var) = lookup(result->inouts)
+    (lookup *(ecx+8) *(ecx+0xc))  # Function-inouts Function-inouts => eax
+    89/<- %edx 0/r32/eax
+$test-function-header-with-multiple-args:inout0:
+    # var v/ebx: (addr var) = lookup(inouts->value)
+    (lookup *edx *(edx+4))  # List-value List-value => eax
+    89/<- %ebx 0/r32/eax
+    # check v->name
+    (lookup *ebx *(ebx+4))  # Var-name Var-name => eax
+    (check-strings-equal %eax "a" "F - test-function-header-with-multiple-args/inout:0")  # Var-name
+    # check v->type
+    (lookup *(ebx+8) *(ebx+0xc))  # Var-type Var-type => eax
+    (check-ints-equal *eax 1 "F - test-function-header-with-multiple-args/inout:0/type:0")  # Type-tree-is-atom
+    (check-ints-equal *(eax+4) 1 "F - test-function-header-with-multiple-args/inout:0/type:1")  # Type-tree-value
+    (check-ints-equal *(eax+0xc) 0 "F - test-function-header-with-multiple-args/inout:0/type:2")  # Type-tree-right
+$test-function-header-with-multiple-args:inout1:
+    # inouts = lookup(inouts->next)
+    (lookup *(edx+8) *(edx+0xc))  # List-next List-next => eax
+    89/<- %edx 0/r32/eax
+    # v = lookup(inouts->value)
+    (lookup *edx *(edx+4))  # List-value List-value => eax
+    89/<- %ebx 0/r32/eax
+    # check v->name
+    (lookup *ebx *(ebx+4))  # Var-name Var-name => eax
+    (check-strings-equal %eax "b" "F - test-function-header-with-multiple-args/inout:1")  # Var-name
+    # check v->type
+    (lookup *(ebx+8) *(ebx+0xc))  # Var-type Var-type => eax
+    (check-ints-equal *eax 1 "F - test-function-header-with-multiple-args/inout:1/type:0")  # Type-tree-is-atom
+    (check-ints-equal *(eax+4) 1 "F - test-function-header-with-multiple-args/inout:1/type:1")  # Type-tree-value
+    (check-ints-equal *(eax+0xc) 0 "F - test-function-header-with-multiple-args/inout:1/type:2")  # Type-tree-right
+$test-function-header-with-multiple-args:inout2:
+    # inouts = lookup(inouts->next)
+    (lookup *(edx+8) *(edx+0xc))  # List-next List-next => eax
+    89/<- %edx 0/r32/eax
+    # v = lookup(inouts->value)
+    (lookup *edx *(edx+4))  # List-value List-value => eax
+    89/<- %ebx 0/r32/eax
+    # check v->name
+    (lookup *ebx *(ebx+4))  # Var-name Var-name => eax
+    (check-strings-equal %eax "c" "F - test-function-header-with-multiple-args/inout:2")  # Var-name
+    # check v->type
+    (lookup *(ebx+8) *(ebx+0xc))  # Var-type Var-type => eax
+    (check-ints-equal *eax 1 "F - test-function-header-with-multiple-args/inout:2/type:0")  # Type-tree-is-atom
+    (check-ints-equal *(eax+4) 1 "F - test-function-header-with-multiple-args/inout:2/type:1")  # Type-tree-value
+    (check-ints-equal *(eax+0xc) 0 "F - test-function-header-with-multiple-args/inout:2/type:2")  # Type-tree-right
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-function-header-with-multiple-args-and-outputs:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    8b/-> *Primitive-type-ids 0/r32/eax
+    89/<- *Type-id 0/r32/eax  # stream-write
+    c7 0/subop/copy *_Program-functions 0/imm32
+    c7 0/subop/copy *_Program-functions->payload 0/imm32
+    c7 0/subop/copy *_Program-types 0/imm32
+    c7 0/subop/copy *_Program-types->payload 0/imm32
+    c7 0/subop/copy *_Program-signatures 0/imm32
+    c7 0/subop/copy *_Program-signatures->payload 0/imm32
+    (clear-stream _test-input-stream)
+    (write _test-input-stream "foo a: int, b: int, c: int -> _/ecx: int _/edx: int {\n")
+    # result/ecx: function
+    2b/subtract *Function-size 4/r32/esp
+    89/<- %ecx 4/r32/esp
+    (zero-out %ecx *Function-size)
+    # var vars/ebx: (stack live-var 16)
+    81 5/subop/subtract %esp 0xc0/imm32
+    68/push 0xc0/imm32/size
+    68/push 0/imm32/top
+    89/<- %ebx 4/r32/esp
+    # convert
+    (populate-mu-function-header _test-input-stream %ecx %ebx Stderr 0)
+    # check result->name
+    (lookup *ecx *(ecx+4))  # Function-name Function-name => eax
+    (check-strings-equal %eax "foo" "F - test-function-header-with-multiple-args-and-outputs/name")
+    # var inouts/edx: (addr list var) = lookup(result->inouts)
+    (lookup *(ecx+8) *(ecx+0xc))  # Function-inouts Function-inouts => eax
+    89/<- %edx 0/r32/eax
+$test-function-header-with-multiple-args-and-outputs:inout0:
+    # var v/ebx: (addr var) = lookup(inouts->value)
+    (lookup *edx *(edx+4))  # List-value List-value => eax
+    89/<- %ebx 0/r32/eax
+    # check v->name
+    (lookup *ebx *(ebx+4))  # Var-name Var-name => eax
+    (check-strings-equal %eax "a" "F - test-function-header-with-multiple-args-and-outputs/inout:0")
+    # check v->type
+    (lookup *(ebx+8) *(ebx+0xc))  # Var-type Var-type => eax
+    (check-ints-equal *eax 1 "F - test-function-header-with-multiple-args-and-outputs/inout:0/type:0")  # Type-tree-is-atom
+    (check-ints-equal *(eax+4) 1 "F - test-function-header-with-multiple-args-and-outputs/inout:0/type:1")  # Type-tree-value
+    (check-ints-equal *(eax+0xc) 0 "F - test-function-header-with-multiple-args-and-outputs/inout:0/type:2")  # Type-tree-right
+$test-function-header-with-multiple-args-and-outputs:inout1:
+    # inouts = lookup(inouts->next)
+    (lookup *(edx+8) *(edx+0xc))  # List-next List-next => eax
+    89/<- %edx 0/r32/eax
+    # v = lookup(inouts->value)
+    (lookup *edx *(edx+4))  # List-value List-value => eax
+    89/<- %ebx 0/r32/eax
+    # check v->name
+    (lookup *ebx *(ebx+4))  # Var-name Var-name => eax
+    (check-strings-equal %eax "b" "F - test-function-header-with-multiple-args-and-outputs/inout:1")
+    # check v->type
+    (lookup *(ebx+8) *(ebx+0xc))  # Var-type Var-type => eax
+    (check-ints-equal *eax 1 "F - test-function-header-with-multiple-args-and-outputs/inout:1/type:0")  # Type-tree-is-atom
+    (check-ints-equal *(eax+4) 1 "F - test-function-header-with-multiple-args-and-outputs/inout:1/type:1")  # Type-tree-value
+    (check-ints-equal *(eax+0xc) 0 "F - test-function-header-with-multiple-args-and-outputs/inout:1/type:2")  # Type-tree-right
+$test-function-header-with-multiple-args-and-outputs:inout2:
+    # inouts = lookup(inouts->next)
+    (lookup *(edx+8) *(edx+0xc))  # List-next List-next => eax
+    89/<- %edx 0/r32/eax
+    # v = lookup(inouts->value)
+    (lookup *edx *(edx+4))  # List-value List-value => eax
+    89/<- %ebx 0/r32/eax
+    # check v->name
+    (lookup *ebx *(ebx+4))  # Var-name Var-name => eax
+    (check-strings-equal %eax "c" "F - test-function-header-with-multiple-args-and-outputs/inout:2")
+    # check v->type
+    (lookup *(ebx+8) *(ebx+0xc))  # Var-type Var-type => eax
+    (check-ints-equal *eax 1 "F - test-function-header-with-multiple-args-and-outputs/inout:2/type:0")  # Type-tree-is-atom
+    (check-ints-equal *(eax+4) 1 "F - test-function-header-with-multiple-args-and-outputs/inout:2/type:1")  # Type-tree-value
+    (check-ints-equal *(eax+0xc) 0 "F - test-function-header-with-multiple-args-and-outputs/inout:2/type:2")  # Type-tree-right
+$test-function-header-with-multiple-args-and-outputs:out0:
+    # var outputs/edx: (addr list var) = lookup(result->outputs)
+    (lookup *(ecx+0x10) *(ecx+0x14))  # Function-outputs Function-outputs => eax
+    89/<- %edx 0/r32/eax
+    # v = lookup(outputs->value)
+    (lookup *edx *(edx+4))  # List-value List-value => eax
+    89/<- %ebx 0/r32/eax
+    # check v->name
+    (lookup *ebx *(ebx+4))  # Var-name Var-name => eax
+    (check-strings-equal %eax "_" "F - test-function-header-with-multiple-args-and-outputs/output:0")
+    # check v->register
+    (lookup *(ebx+0x18) *(ebx+0x1c))  # Var-register Var-register => eax
+    (check-strings-equal %eax "ecx" "F - test-function-header-with-multiple-args-and-outputs/output:0/register")
+    # check v->type
+    (lookup *(ebx+8) *(ebx+0xc))  # Var-type Var-type => eax
+    (check-ints-equal *eax 1 "F - test-function-header-with-multiple-args-and-outputs/output:0/type:0")  # Type-tree-is-atom
+    (check-ints-equal *(eax+4) 1 "F - test-function-header-with-multiple-args-and-outputs/output:0/type:1")  # Type-tree-value
+    (check-ints-equal *(eax+0xc) 0 "F - test-function-header-with-multiple-args-and-outputs/output:0/type:2")  # Type-tree-right
+$test-function-header-with-multiple-args-and-outputs:out1:
+    # outputs = lookup(outputs->next)
+    (lookup *(edx+8) *(edx+0xc))  # List-next List-next => eax
+    89/<- %edx 0/r32/eax
+    # v = lookup(inouts->value)
+    (lookup *edx *(edx+4))  # List-value List-value => eax
+    89/<- %ebx 0/r32/eax
+    # check v->name
+    (lookup *ebx *(ebx+4))  # Var-name Var-name => eax
+    (check-strings-equal %eax "_" "F - test-function-header-with-multiple-args-and-outputs/output:1")
+    # check v->register
+    (lookup *(ebx+0x18) *(ebx+0x1c))  # Var-register Var-register => eax
+    (check-strings-equal %eax "edx" "F - test-function-header-with-multiple-args-and-outputs/output:1/register")
+    # check v->type
+    (lookup *(ebx+8) *(ebx+0xc))  # Var-type Var-type => eax
+    (check-ints-equal *eax 1 "F - test-function-header-with-multiple-args-and-outputs/output:1/type:0")  # Type-tree-is-atom
+    (check-ints-equal *(eax+4) 1 "F - test-function-header-with-multiple-args-and-outputs/output:1/type:1")  # Type-tree-value
+    (check-ints-equal *(eax+0c) 0 "F - test-function-header-with-multiple-args-and-outputs/output:1/type:2")  # Type-tree-right
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+# format for variables with types
+#   x: int
+#   x: int,
+#   x/eax: int
+#   x/eax: int,
+# ignores at most one trailing comma
+# does not support other, non-register metadata
+# WARNING: modifies name
+parse-var-with-type:  # name: (addr slice), first-line: (addr stream byte), out: (addr handle var), fn-name: (addr array byte), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # pseudocode:
+    #   var s: slice
+    #   if (!slice-ends-with(name, ":"))
+    #     abort
+    #   --name->end to skip ':'
+    #   next-token-from-slice(name->start, name->end, '/', s)
+    #   new-var-from-slice(s, out)
+    #   ## register
+    #   next-token-from-slice(s->end, name->end, '/', s)
+    #   if (!slice-empty?(s))
+    #     out->register = slice-to-string(s)
+    #   ## type
+    #   var type: (handle type-tree) = parse-type(first-line)
+    #   out->type = type
+    #
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    52/push-edx
+    53/push-ebx
+    56/push-esi
+    57/push-edi
+    # esi = name
+    8b/-> *(ebp+8) 6/r32/esi
+    # if (!slice-ends-with?(name, ":")) abort
+    8b/-> *(esi+4) 1/r32/ecx  # Slice-end
+    49/decrement-ecx
+    8a/copy-byte *ecx 1/r32/CL
+    81 4/subop/and %ecx 0xff/imm32
+    81 7/subop/compare %ecx 0x3a/imm32/colon
+    0f 85/jump-if-!= $parse-var-with-type:abort/disp32
+    # --name->end to skip ':'
+    ff 1/subop/decrement *(esi+4)
+    # var s/ecx: slice
+    68/push 0/imm32/end
+    68/push 0/imm32/start
+    89/<- %ecx 4/r32/esp
+$parse-var-with-type:parse-name:
+    (next-token-from-slice *esi *(esi+4) 0x2f %ecx)  # Slice-start, Slice-end, '/'
+$parse-var-with-type:create-var:
+    # new-var-from-slice(s, out)
+    (new-var-from-slice Heap %ecx *(ebp+0x10))
+    # save out->register
+$parse-var-with-type:save-register:
+    # . var out-addr/edi: (addr var) = lookup(*out)
+    8b/-> *(ebp+0x10) 7/r32/edi
+    (lookup *edi *(edi+4))  # => eax
+    89/<- %edi 0/r32/eax
+    # . s = next-token(...)
+    (next-token-from-slice *(ecx+4) *(esi+4) 0x2f %ecx)  # s->end, name->end, '/'
+    # . if (!slice-empty?(s)) out->register = slice-to-string(s)
+    {
+$parse-var-with-type:write-register:
+      (slice-empty? %ecx)  # => eax
+      3d/compare-eax-and 0/imm32/false
+      75/jump-if-!= break/disp8
+      # out->register = slice-to-string(s)
+      8d/copy-address *(edi+0x18) 0/r32/eax  # Var-register
+      (slice-to-string Heap %ecx %eax)
+    }
+$parse-var-with-type:save-type:
+    8d/copy-address *(edi+8) 0/r32/eax  # Var-type
+    (parse-type Heap *(ebp+0xc) %eax *(ebp+0x18) *(ebp+0x1c))
+$parse-var-with-type:check-register:
+    (lookup *(edi+0x18) *(edi+0x1c))  # Var-register Var-register => eax
+    3d/compare-eax-and 0/imm32
+    74/jump-if-= $parse-var-with-type:end/disp8
+    (is-float-register? %eax)  # => eax
+    {
+      3d/compare-eax-and 0/imm32/false
+      74/jump-if-= break/disp8
+      # var is in a float register; ensure type is float
+      (lookup *(edi+8) *(edi+0xc))  # Var-type Var-type => eax
+      (is-simple-mu-type? %eax 0xf)  # float => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 84/jump-if-= $parse-var-with-type:error-non-float-in-floating-point-register/disp32
+      eb/jump $parse-var-with-type:end/disp8
+    }
+    # var is not in a float register; ensure type is not float
+    (lookup *(edi+8) *(edi+0xc))  # Var-type Var-type => eax
+    (is-simple-mu-type? %eax 0xf)  # float => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 85/jump-if-!= $parse-var-with-type:error-float-in-integer-register/disp32
+$parse-var-with-type:end:
+    # . reclaim locals
+    81 0/subop/add %esp 8/imm32
+    # . restore registers
+    5f/pop-to-edi
+    5e/pop-to-esi
+    5b/pop-to-ebx
+    5a/pop-to-edx
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+$parse-var-with-type:abort:
+    # error("fn " fn ": var should have form 'name: type' in '" line "'\n")
+    (write-buffered *(ebp+0x18) "fn ")
+    (write-buffered *(ebp+0x18) *(ebp+0x14))
+    (write-buffered *(ebp+0x18) ": var should have form 'name: type' in '")
+    (flush *(ebp+0x18))
+    (rewind-stream *(ebp+0xc))
+    (write-stream-data *(ebp+0x18) *(ebp+0xc))
+    (write-buffered *(ebp+0x18) "'\n")
+    (flush *(ebp+0x18))
+    (stop *(ebp+0x1c) 1)
+    # never gets here
+
+$parse-var-with-type:error-float-in-integer-register:
+    # error("fn " fn ": float var '" var "' should be in a floating-point register\n")
+    (write-buffered *(ebp+0x18) "fn ")
+    (write-buffered *(ebp+0x18) *(ebp+0x14))
+    (write-buffered *(ebp+0x18) ": float var '")
+    (lookup *edi *(edi+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x18) %eax)
+    (write-buffered *(ebp+0x18) "' should be in a floating-point register\n")
+    (flush *(ebp+0x18))
+    (stop *(ebp+0x1c) 1)
+    # never gets here
+
+$parse-var-with-type:error-non-float-in-floating-point-register:
+    # error("fn " fn ": non-float var '" var "' should be in an integer register\n")
+    (write-buffered *(ebp+0x18) "fn ")
+    (write-buffered *(ebp+0x18) *(ebp+0x14))
+    (write-buffered *(ebp+0x18) ": non-float var '")
+    (lookup *edi *(edi+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x18) %eax)
+    (write-buffered *(ebp+0x18) "' should be in an integer register\n")
+    (flush *(ebp+0x18))
+    (stop *(ebp+0x1c) 1)
+    # never gets here
+
+is-float-register?:  # r: (addr array byte) -> result/eax: boolean
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    #
+    (get Mu-registers-unique *(ebp+8) 0xc "Mu-registers-unique")  # => eax
+    81 7/subop/compare *eax 8/imm32/start-of-floating-point-registers
+    0f 9d/set-if->= %al
+    81 4/subop/and %eax 0xff/imm32
+$is-float-register?:end:
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+parse-type:  # ad: (addr allocation-descriptor), in: (addr stream byte), out: (addr handle type-tree), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # pseudocode:
+    #   var s: slice = next-mu-token(in)
+    #   assert s != ""
+    #   assert s != "->"
+    #   assert s != "{"
+    #   assert s != "}"
+    #   if s == ")"
+    #     return
+    #   out = allocate(Type-tree)
+    #   if s != "("
+    #     HACK: if s is an int, parse and return it
+    #     out->is-atom? = true
+    #     if (s[0] == "_")
+    #       out->value = type-parameter
+    #       out->parameter-name = slice-to-string(ad, s)
+    #     else
+    #       out->value = pos-or-insert-slice(Type-id, s)
+    #     return
+    #   out->left = parse-type(ad, in)
+    #   out->right = parse-type-tree(ad, in)
+    #
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    52/push-edx
+    # clear out
+    (zero-out *(ebp+0x10) *Handle-size)
+    # var s/ecx: slice
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %ecx 4/r32/esp
+    # s = next-mu-token(in)
+    (next-mu-token *(ebp+0xc) %ecx)
+#?     (write-buffered Stderr "tok: ")
+#?     (write-slice-buffered Stderr %ecx)
+#?     (write-buffered Stderr "$\n")
+#?     (flush Stderr)
+    # assert s != ""
+    (slice-equal? %ecx "")  # => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 85/jump-if-!= $parse-type:abort/disp32
+    # assert s != "{"
+    (slice-equal? %ecx "{")  # => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 85/jump-if-!= $parse-type:abort/disp32
+    # assert s != "}"
+    (slice-equal? %ecx "}")  # => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 85/jump-if-!= $parse-type:abort/disp32
+    # assert s != "->"
+    (slice-equal? %ecx "->")  # => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 85/jump-if-!= $parse-type:abort/disp32
+    # if (s == ")") return
+    (slice-equal? %ecx ")")  # => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 85/jump-if-!= $parse-type:end/disp32
+    # out = new tree
+    (allocate *(ebp+8) *Type-tree-size *(ebp+0x10))
+    # var out-addr/edx: (addr type-tree) = lookup(*out)
+    8b/-> *(ebp+0x10) 2/r32/edx
+    (lookup *edx *(edx+4))  # => eax
+    89/<- %edx 0/r32/eax
+    {
+      # if (s != "(") break
+      (slice-equal? %ecx "(")  # => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 85/jump-if-!= break/disp32
+      # if s is a number, store it in the type's size field
+      {
+$parse-type:check-for-int:
+        # var tmp/eax: byte = *s->slice
+        8b/-> *ecx 0/r32/eax
+        8a/copy-byte *eax 0/r32/AL
+        81 4/subop/and %eax 0xff/imm32
+        # TODO: raise an error on `var x: (array int a)`
+        (is-decimal-digit? %eax)  # => eax
+        3d/compare-eax-and 0/imm32/false
+        74/jump-if-= break/disp8
+$parse-type:int:
+        # strip out metadata
+        (next-token-from-slice *ecx *(ecx+4) 0x2f %ecx)
+        #
+        (check-mu-hex-int %ecx *(ebp+0x14) *(ebp+0x18))
+        (parse-hex-int-from-slice %ecx)  # => eax
+        c7 0/subop/copy *(edx+4) 9/imm32/type-id-array-capacity  # Type-tree-value
+        89/<- *(edx+8) 0/r32/eax  # Type-tree-value-size
+        e9/jump $parse-type:end/disp32
+      }
+$parse-type:atom:
+      # out->is-atom? = true
+      c7 0/subop/copy *edx 1/imm32/true  # Type-tree-is-atom
+      {
+$parse-type:check-for-type-parameter:
+        # var tmp/eax: byte = *s->slice
+        8b/-> *ecx 0/r32/eax
+        8a/copy-byte *eax 0/r32/AL
+        81 4/subop/and %eax 0xff/imm32
+        # if (tmp != '_') break
+        3d/compare-eax-and 0x5f/imm32/_
+        75/jump-if-!= break/disp8
+$parse-type:type-parameter:
+        # out->value = type-parameter
+        c7 0/subop/copy *(edx+4) 0xa/imm32/type-parameter  # Type-tree-value
+        # out->parameter-name = slice-to-string(ad, s)
+        8d/copy-address *(edx+8) 0/r32/eax  # Type-tree-parameter-name
+        (slice-to-string *(ebp+8) %ecx %eax)
+        e9/jump $parse-type:end/disp32
+      }
+$parse-type:non-type-parameter:
+      # out->value = pos-or-insert-slice(Type-id, s)
+      (pos-or-insert-slice Type-id %ecx)  # => eax
+      89/<- *(edx+4) 0/r32/eax  # Type-tree-value
+      e9/jump $parse-type:end/disp32
+    }
+$parse-type:non-atom:
+    # otherwise s == "("
+    # out->left = parse-type(ad, in)
+    8d/copy-address *(edx+4) 0/r32/eax  # Type-tree-left
+    (parse-type *(ebp+8) *(ebp+0xc) %eax *(ebp+0x14) *(ebp+0x18))
+    # out->right = parse-type-tree(ad, in)
+    8d/copy-address *(edx+0xc) 0/r32/eax  # Type-tree-right
+    (parse-type-tree *(ebp+8) *(ebp+0xc) %eax *(ebp+0x14) *(ebp+0x18))
+$parse-type:end:
+    # . reclaim locals
+    81 0/subop/add %esp 8/imm32
+    # . restore registers
+    5a/pop-to-edx
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+$parse-type:abort:
+    # error("unexpected token when parsing type: '" s "'\n")
+    (write-buffered *(ebp+0x14) "unexpected token when parsing type: '")
+    (write-slice-buffered *(ebp+0x14) %ecx)
+    (write-buffered *(ebp+0x14) "'\n")
+    (flush *(ebp+0x14))
+    (stop *(ebp+0x18) 1)
+    # never gets here
+
+parse-type-tree:  # ad: (addr allocation-descriptor), in: (addr stream byte), out: (addr handle type-tree), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # pseudocode:
+    #   var tmp: (handle type-tree) = parse-type(ad, in)
+    #   if tmp == 0
+    #     return 0
+    #   out = allocate(Type-tree)
+    #   out->left = tmp
+    #   out->right = parse-type-tree(ad, in)
+    #
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    52/push-edx
+    #
+    (zero-out *(ebp+0x10) *Handle-size)
+    # var tmp/ecx: (handle type-tree)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %ecx 4/r32/esp
+    # tmp = parse-type(ad, in)
+    (parse-type *(ebp+8) *(ebp+0xc) %ecx *(ebp+0x14) *(ebp+0x18))
+    # if (tmp == 0) return
+    81 7/subop/compare *ecx 0/imm32
+    74/jump-if-= $parse-type-tree:end/disp8
+    # out = new tree
+    (allocate *(ebp+8) *Type-tree-size *(ebp+0x10))
+    # var out-addr/edx: (addr tree) = lookup(*out)
+    8b/-> *(ebp+0x10) 2/r32/edx
+    (lookup *edx *(edx+4))  # => eax
+    89/<- %edx 0/r32/eax
+    # out->left = tmp
+    8b/-> *ecx 0/r32/eax
+    89/<- *(edx+4) 0/r32/eax  # Type-tree-left
+    8b/-> *(ecx+4) 0/r32/eax
+    89/<- *(edx+8) 0/r32/eax  # Type-tree-left
+    # out->right = parse-type-tree(ad, in)
+    8d/copy-address *(edx+0xc) 0/r32/eax  # Type-tree-right
+    (parse-type-tree *(ebp+8) *(ebp+0xc) %eax *(ebp+0x14) *(ebp+0x18))
+$parse-type-tree:end:
+    # . reclaim locals
+    81 0/subop/add %esp 8/imm32
+    # . restore registers
+    5a/pop-to-edx
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+next-mu-token:  # in: (addr stream byte), out: (addr slice)
+    # pseudocode:
+    # start:
+    #   skip-chars-matching-whitespace(in)
+    #   if in->read >= in->write              # end of in
+    #     out = {0, 0}
+    #     return
+    #   out->start = &in->data[in->read]
+    #   var curr-byte/eax: byte = in->data[in->read]
+    #   if curr->byte == ','                  # comment token
+    #     ++in->read
+    #     goto start
+    #   if curr-byte == '#'                   # comment
+    #     goto done                             # treat as eof
+    #   if curr-byte == '"'                   # string literal
+    #     skip-string(in)
+    #     goto done                           # no metadata
+    #   if curr-byte == '('
+    #     ++in->read
+    #     goto done
+    #   if curr-byte == ')'
+    #     ++in->read
+    #     goto done
+    #   # read a word
+    #   while true
+    #     if in->read >= in->write
+    #       break
+    #     curr-byte = in->data[in->read]
+    #     if curr-byte == ' '
+    #       break
+    #     if curr-byte == '\r'
+    #       break
+    #     if curr-byte == '\n'
+    #       break
+    #     if curr-byte == '('
+    #       break
+    #     if curr-byte == ')'
+    #       break
+    #     if curr-byte == ','
+    #       break
+    #     ++in->read
+    # done:
+    #   out->end = &in->data[in->read]
+    #
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    56/push-esi
+    57/push-edi
+    # esi = in
+    8b/-> *(ebp+8) 6/r32/esi
+    # edi = out
+    8b/-> *(ebp+0xc) 7/r32/edi
+$next-mu-token:start:
+    (skip-chars-matching-whitespace %esi)
+$next-mu-token:check0:
+    # if (in->read >= in->write) return out = {0, 0}
+    # . ecx = in->read
+    8b/-> *(esi+4) 1/r32/ecx
+    # . if (ecx >= in->write) return out = {0, 0}
+    3b/compare<- *esi 1/r32/ecx
+    c7 0/subop/copy *edi 0/imm32
+    c7 0/subop/copy *(edi+4) 0/imm32
+    0f 8d/jump-if->= $next-mu-token:end/disp32
+    # out->start = &in->data[in->read]
+    8d/copy-address *(esi+ecx+0xc) 0/r32/eax
+    89/<- *edi 0/r32/eax
+    # var curr-byte/eax: byte = in->data[in->read]
+    31/xor-with %eax 0/r32/eax
+    8a/copy-byte *(esi+ecx+0xc) 0/r32/AL
+    {
+$next-mu-token:check-for-comma:
+      # if (curr-byte != ',') break
+      3d/compare-eax-and 0x2c/imm32/comma
+      75/jump-if-!= break/disp8
+      # ++in->read
+      ff 0/subop/increment *(esi+4)
+      # restart
+      e9/jump $next-mu-token:start/disp32
+    }
+    {
+$next-mu-token:check-for-comment:
+      # if (curr-byte != '#') break
+      3d/compare-eax-and 0x23/imm32/pound
+      75/jump-if-!= break/disp8
+      # return eof
+      e9/jump $next-mu-token:done/disp32
+    }
+    {
+$next-mu-token:check-for-string-literal:
+      # if (curr-byte != '"') break
+      3d/compare-eax-and 0x22/imm32/dquote
+      75/jump-if-!= break/disp8
+      (skip-string %esi)
+      # return
+      e9/jump $next-mu-token:done/disp32
+    }
+    {
+$next-mu-token:check-for-open-paren:
+      # if (curr-byte != '(') break
+      3d/compare-eax-and 0x28/imm32/open-paren
+      75/jump-if-!= break/disp8
+      # ++in->read
+      ff 0/subop/increment *(esi+4)
+      # return
+      e9/jump $next-mu-token:done/disp32
+    }
+    {
+$next-mu-token:check-for-close-paren:
+      # if (curr-byte != ')') break
+      3d/compare-eax-and 0x29/imm32/close-paren
+      75/jump-if-!= break/disp8
+      # ++in->read
+      ff 0/subop/increment *(esi+4)
+      # return
+      e9/jump $next-mu-token:done/disp32
+    }
+    {
+$next-mu-token:regular-word-without-metadata:
+      # if (in->read >= in->write) break
+      # . ecx = in->read
+      8b/-> *(esi+4) 1/r32/ecx
+      # . if (ecx >= in->write) break
+      3b/compare<- *esi 1/r32/ecx
+      7d/jump-if->= break/disp8
+      # var c/eax: byte = in->data[in->read]
+      31/xor-with %eax 0/r32/eax
+      8a/copy-byte *(esi+ecx+0xc) 0/r32/AL
+      # if (c == ' ') break
+      3d/compare-eax-and 0x20/imm32/space
+      74/jump-if-= break/disp8
+      # if (c == '\r') break
+      3d/compare-eax-and 0xd/imm32/carriage-return
+      74/jump-if-= break/disp8
+      # if (c == '\n') break
+      3d/compare-eax-and 0xa/imm32/newline
+      74/jump-if-= break/disp8
+      # if (c == '(') break
+      3d/compare-eax-and 0x28/imm32/open-paren
+      0f 84/jump-if-= break/disp32
+      # if (c == ')') break
+      3d/compare-eax-and 0x29/imm32/close-paren
+      0f 84/jump-if-= break/disp32
+      # if (c == ',') break
+      3d/compare-eax-and 0x2c/imm32/comma
+      0f 84/jump-if-= break/disp32
+      # ++in->read
+      ff 0/subop/increment *(esi+4)
+      #
+      e9/jump loop/disp32
+    }
+$next-mu-token:done:
+    # out->end = &in->data[in->read]
+    8b/-> *(esi+4) 1/r32/ecx
+    8d/copy-address *(esi+ecx+0xc) 0/r32/eax
+    89/<- *(edi+4) 0/r32/eax
+$next-mu-token:end:
+    # . restore registers
+    5f/pop-to-edi
+    5e/pop-to-esi
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+pos-or-insert-slice:  # arr: (addr stream (addr array byte)), s: (addr slice) -> index/eax: int
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # if (pos-slice(arr, s) != -1) return it
+    (pos-slice *(ebp+8) *(ebp+0xc))  # => eax
+    3d/compare-eax-and -1/imm32
+    75/jump-if-!= $pos-or-insert-slice:end/disp8
+$pos-or-insert-slice:insert:
+    # var s2/eax: (handle array byte)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %eax 4/r32/esp
+    (slice-to-string Heap *(ebp+0xc) %eax)
+    # throw away alloc-id
+    (lookup *eax *(eax+4))  # => eax
+    (write-int *(ebp+8) %eax)
+    (pos-slice *(ebp+8) *(ebp+0xc))  # => eax
+$pos-or-insert-slice:end:
+    # . reclaim locals
+    81 0/subop/add %esp 8/imm32
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+# return the index in an array of strings matching 's', -1 if not found
+# index is denominated in elements, not bytes
+pos-slice:  # arr: (addr stream (addr array byte)), s: (addr slice) -> index/eax: int
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    51/push-ecx
+    52/push-edx
+    53/push-ebx
+    56/push-esi
+#?     (write-buffered Stderr "pos-slice: ")
+#?     (write-slice-buffered Stderr *(ebp+0xc))
+#?     (write-buffered Stderr "\n")
+#?     (flush Stderr)
+    # esi = arr
+    8b/-> *(ebp+8) 6/r32/esi
+    # var index/ecx: int = 0
+    b9/copy-to-ecx 0/imm32
+    # var curr/edx: (addr (addr array byte)) = arr->data
+    8d/copy-address *(esi+0xc) 2/r32/edx
+    # var max/ebx: (addr (addr array byte)) = &arr->data[arr->write]
+    8b/-> *esi 3/r32/ebx
+    8d/copy-address *(esi+ebx+0xc) 3/r32/ebx
+    {
+#?       (write-buffered Stderr "  ")
+#?       (write-int32-hex-buffered Stderr %ecx)
+#?       (write-buffered Stderr "\n")
+#?       (flush Stderr)
+      # if (curr >= max) return -1
+      39/compare %edx 3/r32/ebx
+      b8/copy-to-eax -1/imm32
+      73/jump-if-addr>= $pos-slice:end/disp8
+      # if (slice-equal?(s, *curr)) break
+      (slice-equal? *(ebp+0xc) *edx)  # => eax
+      3d/compare-eax-and 0/imm32/false
+      75/jump-if-!= break/disp8
+      # ++index
+      41/increment-ecx
+      # curr += 4
+      81 0/subop/add %edx 4/imm32
+      #
+      eb/jump loop/disp8
+    }
+    # return index
+    89/<- %eax 1/r32/ecx
+$pos-slice:end:
+#?     (write-buffered Stderr "=> ")
+#?     (write-int32-hex-buffered Stderr %eax)
+#?     (write-buffered Stderr "\n")
+    # . restore registers
+    5e/pop-to-esi
+    5b/pop-to-ebx
+    5a/pop-to-edx
+    59/pop-to-ecx
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-parse-var-with-type:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    8b/-> *Primitive-type-ids 0/r32/eax
+    89/<- *Type-id 0/r32/eax  # stream-write
+    # (eax..ecx) = "x:"
+    b8/copy-to-eax "x:"/imm32
+    8b/-> *eax 1/r32/ecx
+    8d/copy-address *(eax+ecx+4) 1/r32/ecx
+    05/add-to-eax 4/imm32
+    # var slice/ecx: slice = {eax, ecx}
+    51/push-ecx
+    50/push-eax
+    89/<- %ecx 4/r32/esp
+    # _test-input-stream contains "int"
+    (clear-stream _test-input-stream)
+    (write _test-input-stream "int")
+    # var v/edx: (handle var)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    #
+    (parse-var-with-type %ecx _test-input-stream %edx 0 Stderr 0)
+    # var v-addr/edx: (addr var) = lookup(v)
+    (lookup *edx *(edx+4))  # => eax
+    89/<- %edx 0/r32/eax
+    # check v-addr->name
+    (lookup *edx *(edx+4))  # Var-name Var-name => eax
+    (check-strings-equal %eax "x" "F - test-parse-var-with-type/name")
+    # check v-addr->type
+    (lookup *(edx+8) *(edx+0xc))  # Var-type Var-type => eax
+    (check-ints-equal *eax 1 "F - test-parse-var-with-type/type:0")  # Type-tree-is-atom
+    (check-ints-equal *(eax+4) 1 "F - test-parse-var-with-type/type:1")  # Type-tree-value
+    (check-ints-equal *(eax+0xc) 0 "F - test-parse-var-with-type/type:2")  # Type-tree-right
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-parse-var-with-type-and-register:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    8b/-> *Primitive-type-ids 0/r32/eax
+    89/<- *Type-id 0/r32/eax  # stream-write
+    # (eax..ecx) = "x/eax:"
+    b8/copy-to-eax "x/eax:"/imm32
+    8b/-> *eax 1/r32/ecx
+    8d/copy-address *(eax+ecx+4) 1/r32/ecx
+    05/add-to-eax 4/imm32
+    # var slice/ecx: slice = {eax, ecx}
+    51/push-ecx
+    50/push-eax
+    89/<- %ecx 4/r32/esp
+    # _test-input-stream contains "int"
+    (clear-stream _test-input-stream)
+    (write _test-input-stream "int")
+    # var v/edx: (handle var)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    #
+    (parse-var-with-type %ecx _test-input-stream %edx 0 Stderr 0)
+    # var v-addr/edx: (addr var) = lookup(v)
+    (lookup *edx *(edx+4))  # => eax
+    89/<- %edx 0/r32/eax
+    # check v-addr->name
+    (lookup *edx *(edx+4))  # Var-name Var-name => eax
+    (check-strings-equal %eax "x" "F - test-parse-var-with-type-and-register/name")
+    # check v-addr->register
+    (lookup *(edx+0x18) *(edx+0x1c))  # Var-register Var-register => eax
+    (check-strings-equal %eax "eax" "F - test-parse-var-with-type-and-register/register")
+    # check v-addr->type
+    (lookup *(edx+8) *(edx+0xc))  # Var-type Var-type => eax
+    (check-ints-equal *eax 1 "F - test-parse-var-with-type-and-register/type:0")  # Type-tree-is-atom
+    (check-ints-equal *(eax+4) 1 "F - test-parse-var-with-type-and-register/type:1")  # Type-tree-left
+    (check-ints-equal *(eax+0xc) 0 "F - test-parse-var-with-type-and-register/type:2")  # Type-tree-right
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-parse-var-with-trailing-characters:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    8b/-> *Primitive-type-ids 0/r32/eax
+    89/<- *Type-id 0/r32/eax  # stream-write
+    # (eax..ecx) = "x:"
+    b8/copy-to-eax "x:"/imm32
+    8b/-> *eax 1/r32/ecx
+    8d/copy-address *(eax+ecx+4) 1/r32/ecx
+    05/add-to-eax 4/imm32
+    # var slice/ecx: slice = {eax, ecx}
+    51/push-ecx
+    50/push-eax
+    89/<- %ecx 4/r32/esp
+    # _test-input-stream contains "int,"
+    (clear-stream _test-input-stream)
+    (write _test-input-stream "int,")
+    # var v/edx: (handle var)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    #
+    (parse-var-with-type %ecx _test-input-stream %edx 0 Stderr 0)
+    # var v-addr/edx: (addr var) = lookup(v)
+    (lookup *edx *(edx+4))  # => eax
+    89/<- %edx 0/r32/eax
+    # check v-addr->name
+    (lookup *edx *(edx+4))  # Var-name Var-name => eax
+    (check-strings-equal %eax "x" "F - test-parse-var-with-trailing-characters/name")
+    # check v-addr->register
+    (check-ints-equal *(edx+0x18) 0 "F - test-parse-var-with-trailing-characters/register")  # Var-register
+    # check v-addr->type
+    (lookup *(edx+8) *(edx+0xc))  # Var-type Var-type => eax
+    (check-ints-equal *eax 1 "F - test-parse-var-with-trailing-characters/type:0")  # Type-tree-is-atom
+    (check-ints-equal *(eax+4) 1 "F - test-parse-var-with-trailing-characters/type:1")  # Type-tree-left
+    (check-ints-equal *(eax+0xc) 0 "F - test-parse-var-with-trailing-characters/type:1")  # Type-tree-right
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-parse-var-with-register-and-trailing-characters:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    8b/-> *Primitive-type-ids 0/r32/eax
+    89/<- *Type-id 0/r32/eax  # stream-write
+    # (eax..ecx) = "x/eax:"
+    b8/copy-to-eax "x/eax:"/imm32
+    8b/-> *eax 1/r32/ecx
+    8d/copy-address *(eax+ecx+4) 1/r32/ecx
+    05/add-to-eax 4/imm32
+    # var slice/ecx: slice = {eax, ecx}
+    51/push-ecx
+    50/push-eax
+    89/<- %ecx 4/r32/esp
+    # _test-input-stream contains "int,"
+    (clear-stream _test-input-stream)
+    (write _test-input-stream "int,")
+    # var v/edx: (handle var)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    #
+    (parse-var-with-type %ecx _test-input-stream %edx 0 Stderr 0)
+    # var v-addr/edx: (addr var) = lookup(v)
+    (lookup *edx *(edx+4))  # => eax
+    89/<- %edx 0/r32/eax
+    # check v-addr->name
+    (lookup *edx *(edx+4))  # Var-name Var-name => eax
+    (check-strings-equal %eax "x" "F - test-parse-var-with-register-and-trailing-characters/name")
+    # check v-addr->register
+    (lookup *(edx+0x18) *(edx+0x1c))  # Var-register Var-register => eax
+    (check-strings-equal %eax "eax" "F - test-parse-var-with-register-and-trailing-characters/register")
+    # check v-addr->type
+    (lookup *(edx+8) *(edx+0xc))  # Var-type Var-type => eax
+    (check-ints-equal *eax 1 "F - test-parse-var-with-register-and-trailing-characters/type:0")  # Type-tree-is-atom
+    (check-ints-equal *(eax+4) 1 "F - test-parse-var-with-register-and-trailing-characters/type:1")  # Type-tree-left
+    (check-ints-equal *(eax+0xc) 0 "F - test-parse-var-with-register-and-trailing-characters/type:2")  # Type-tree-right
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-parse-var-with-compound-type:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    8b/-> *Primitive-type-ids 0/r32/eax
+    89/<- *Type-id 0/r32/eax  # stream-write
+    # (eax..ecx) = "x:"
+    b8/copy-to-eax "x:"/imm32
+    8b/-> *eax 1/r32/ecx
+    8d/copy-address *(eax+ecx+4) 1/r32/ecx
+    05/add-to-eax 4/imm32
+    # var slice/ecx: slice = {eax, ecx}
+    51/push-ecx
+    50/push-eax
+    89/<- %ecx 4/r32/esp
+    # _test-input-stream contains "(addr int)"
+    (clear-stream _test-input-stream)
+    (write _test-input-stream "(addr int)")
+    # var v/edx: (handle var)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    #
+    (parse-var-with-type %ecx _test-input-stream %edx 0 Stderr 0)
+    # var v-addr/edx: (addr var) = lookup(v)
+    (lookup *edx *(edx+4))  # => eax
+    89/<- %edx 0/r32/eax
+    # check v-addr->name
+    (lookup *edx *(edx+4))  # Var-name Var-name => eax
+    (check-strings-equal %eax "x" "F - test-parse-var-with-compound-type/name")
+    # check v-addr->register
+    (check-ints-equal *(edx+0x18) 0 "F - test-parse-var-with-compound-type/register")  # Var-register
+    # - check v-addr->type
+    # var type/edx: (addr type-tree) = var->type
+    (lookup *(edx+8) *(edx+0xc))  # Var-type Var-type => eax
+    89/<- %edx 0/r32/eax
+    # type is a non-atom
+    (check-ints-equal *edx 0 "F - test-parse-var-with-compound-type/type:0")  # Type-tree-is-atom
+    # type->left == atom(addr)
+    (lookup *(edx+4) *(edx+8))  # Type-tree-left Type-tree-left => eax
+    (check-ints-equal *eax 1 "F - test-parse-var-with-compound-type/type:1")  # Type-tree-is-atom
+    (check-ints-equal *(eax+4) 2 "F - test-parse-var-with-compound-type/type:2")  # Type-tree-value
+    # type->right->left == atom(int)
+    (lookup *(edx+0xc) *(edx+0x10))  # Type-tree-right Type-tree-right => eax
+    (lookup *(eax+4) *(eax+8))  # Type-tree-left Type-tree-left => eax
+    (check-ints-equal *eax 1 "F - test-parse-var-with-compound-type/type:3")  # Type-tree-is-atom
+    (check-ints-equal *(eax+4) 1 "F - test-parse-var-with-compound-type/type:4")  # Type-tree-value
+    # type->right->right == null
+    (check-ints-equal *(eax+0xc) 0 "F - test-parse-var-with-compound-type/type:5")  # Type-tree-right
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+# identifier starts with a letter or '$' or '_'
+# no constraints at the moment on later letters
+# all we really want to do so far is exclude '{', '}' and '->'
+is-identifier?:  # in: (addr slice) -> result/eax: boolean
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # if (slice-empty?(in)) return false
+    (slice-empty? *(ebp+8))  # => eax
+    3d/compare-eax-and 0/imm32/false
+    75/jump-if-!= $is-identifier?:false/disp8
+    # var c/eax: byte = *in->start
+    8b/-> *(ebp+8) 0/r32/eax
+    8b/-> *eax 0/r32/eax
+    8a/copy-byte *eax 0/r32/AL
+    81 4/subop/and %eax 0xff/imm32
+    # if (c == '$') return true
+    3d/compare-eax-and 0x24/imm32/$
+    74/jump-if-= $is-identifier?:true/disp8
+    # if (c == '_') return true
+    3d/compare-eax-and 0x5f/imm32/_
+    74/jump-if-= $is-identifier?:true/disp8
+    # drop case
+    25/and-eax-with 0x5f/imm32
+    # if (c < 'A') return false
+    3d/compare-eax-and 0x41/imm32/A
+    7c/jump-if-< $is-identifier?:false/disp8
+    # if (c > 'Z') return false
+    3d/compare-eax-and 0x5a/imm32/Z
+    7f/jump-if-> $is-identifier?:false/disp8
+    # otherwise return true
+$is-identifier?:true:
+    b8/copy-to-eax 1/imm32/true
+    eb/jump $is-identifier?:end/disp8
+$is-identifier?:false:
+    b8/copy-to-eax 0/imm32/false
+$is-identifier?:end:
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-is-identifier-dollar:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # (eax..ecx) = "$a"
+    b8/copy-to-eax "$a"/imm32
+    8b/-> *eax 1/r32/ecx
+    8d/copy-address *(eax+ecx+4) 1/r32/ecx
+    05/add-to-eax 4/imm32
+    # var slice/ecx: slice = {eax, ecx}
+    51/push-ecx
+    50/push-eax
+    89/<- %ecx 4/r32/esp
+    #
+    (is-identifier? %ecx)
+    (check-ints-equal %eax 1 "F - test-is-identifier-dollar")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-is-identifier-underscore:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # (eax..ecx) = "_a"
+    b8/copy-to-eax "_a"/imm32
+    8b/-> *eax 1/r32/ecx
+    8d/copy-address *(eax+ecx+4) 1/r32/ecx
+    05/add-to-eax 4/imm32
+    # var slice/ecx: slice = {eax, ecx}
+    51/push-ecx
+    50/push-eax
+    89/<- %ecx 4/r32/esp
+    #
+    (is-identifier? %ecx)
+    (check-ints-equal %eax 1 "F - test-is-identifier-underscore")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-is-identifier-a:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # (eax..ecx) = "a$"
+    b8/copy-to-eax "a$"/imm32
+    8b/-> *eax 1/r32/ecx
+    8d/copy-address *(eax+ecx+4) 1/r32/ecx
+    05/add-to-eax 4/imm32
+    # var slice/ecx: slice = {eax, ecx}
+    51/push-ecx
+    50/push-eax
+    89/<- %ecx 4/r32/esp
+    #
+    (is-identifier? %ecx)
+    (check-ints-equal %eax 1 "F - test-is-identifier-a")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-is-identifier-z:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # (eax..ecx) = "z$"
+    b8/copy-to-eax "z$"/imm32
+    8b/-> *eax 1/r32/ecx
+    8d/copy-address *(eax+ecx+4) 1/r32/ecx
+    05/add-to-eax 4/imm32
+    # var slice/ecx: slice = {eax, ecx}
+    51/push-ecx
+    50/push-eax
+    89/<- %ecx 4/r32/esp
+    #
+    (is-identifier? %ecx)
+    (check-ints-equal %eax 1 "F - test-is-identifier-z")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-is-identifier-A:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # (eax..ecx) = "A$"
+    b8/copy-to-eax "A$"/imm32
+    8b/-> *eax 1/r32/ecx
+    8d/copy-address *(eax+ecx+4) 1/r32/ecx
+    05/add-to-eax 4/imm32
+    # var slice/ecx: slice = {eax, ecx}
+    51/push-ecx
+    50/push-eax
+    89/<- %ecx 4/r32/esp
+    #
+    (is-identifier? %ecx)
+    (check-ints-equal %eax 1 "F - test-is-identifier-A")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-is-identifier-Z:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # (eax..ecx) = "Z$"
+    b8/copy-to-eax "Z$"/imm32
+    8b/-> *eax 1/r32/ecx
+    8d/copy-address *(eax+ecx+4) 1/r32/ecx
+    05/add-to-eax 4/imm32
+    # var slice/ecx: slice = {eax, ecx}
+    51/push-ecx
+    50/push-eax
+    89/<- %ecx 4/r32/esp
+    #
+    (is-identifier? %ecx)
+    (check-ints-equal %eax 1 "F - test-is-identifier-Z")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-is-identifier-at:
+    # character before 'A' is invalid
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # (eax..ecx) = "@a"
+    b8/copy-to-eax "@a"/imm32
+    8b/-> *eax 1/r32/ecx
+    8d/copy-address *(eax+ecx+4) 1/r32/ecx
+    05/add-to-eax 4/imm32
+    # var slice/ecx: slice = {eax, ecx}
+    51/push-ecx
+    50/push-eax
+    89/<- %ecx 4/r32/esp
+    #
+    (is-identifier? %ecx)
+    (check-ints-equal %eax 0 "F - test-is-identifier-@")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-is-identifier-square-bracket:
+    # character after 'Z' is invalid
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # (eax..ecx) = "[a"
+    b8/copy-to-eax "[a"/imm32
+    8b/-> *eax 1/r32/ecx
+    8d/copy-address *(eax+ecx+4) 1/r32/ecx
+    05/add-to-eax 4/imm32
+    # var slice/ecx: slice = {eax, ecx}
+    51/push-ecx
+    50/push-eax
+    89/<- %ecx 4/r32/esp
+    #
+    (is-identifier? %ecx)
+    (check-ints-equal %eax 0 "F - test-is-identifier-@")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-is-identifier-backtick:
+    # character before 'a' is invalid
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # (eax..ecx) = "`a"
+    b8/copy-to-eax "`a"/imm32
+    8b/-> *eax 1/r32/ecx
+    8d/copy-address *(eax+ecx+4) 1/r32/ecx
+    05/add-to-eax 4/imm32
+    # var slice/ecx: slice = {eax, ecx}
+    51/push-ecx
+    50/push-eax
+    89/<- %ecx 4/r32/esp
+    #
+    (is-identifier? %ecx)
+    (check-ints-equal %eax 0 "F - test-is-identifier-backtick")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-is-identifier-curly-brace-open:
+    # character after 'z' is invalid; also used for blocks
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # (eax..ecx) = "{a"
+    b8/copy-to-eax "{a"/imm32
+    8b/-> *eax 1/r32/ecx
+    8d/copy-address *(eax+ecx+4) 1/r32/ecx
+    05/add-to-eax 4/imm32
+    # var slice/ecx: slice = {eax, ecx}
+    51/push-ecx
+    50/push-eax
+    89/<- %ecx 4/r32/esp
+    #
+    (is-identifier? %ecx)
+    (check-ints-equal %eax 0 "F - test-is-identifier-curly-brace-open")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-is-identifier-curly-brace-close:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # (eax..ecx) = "}a"
+    b8/copy-to-eax "}a"/imm32
+    8b/-> *eax 1/r32/ecx
+    8d/copy-address *(eax+ecx+4) 1/r32/ecx
+    05/add-to-eax 4/imm32
+    # var slice/ecx: slice = {eax, ecx}
+    51/push-ecx
+    50/push-eax
+    89/<- %ecx 4/r32/esp
+    #
+    (is-identifier? %ecx)
+    (check-ints-equal %eax 0 "F - test-is-identifier-curly-brace-close")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-is-identifier-hyphen:
+    # disallow leading '-' since '->' has special meaning
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # (eax..ecx) = "-a"
+    b8/copy-to-eax "-a"/imm32
+    8b/-> *eax 1/r32/ecx
+    8d/copy-address *(eax+ecx+4) 1/r32/ecx
+    05/add-to-eax 4/imm32
+    # var slice/ecx: slice = {eax, ecx}
+    51/push-ecx
+    50/push-eax
+    89/<- %ecx 4/r32/esp
+    #
+    (is-identifier? %ecx)
+    (check-ints-equal %eax 0 "F - test-is-identifier-hyphen")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+populate-mu-function-body:  # in: (addr buffered-file), out: (addr function), vars: (addr stack live-var), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    56/push-esi
+    57/push-edi
+    # esi = in
+    8b/-> *(ebp+8) 6/r32/esi
+    # edi = out
+    8b/-> *(ebp+0xc) 7/r32/edi
+    # initialize some global state
+    c7 0/subop/copy *Curr-block-depth 1/imm32
+    # parse-mu-block(in, vars, out, out->body)
+    8d/copy-address *(edi+0x18) 0/r32/eax  # Function-body
+    (parse-mu-block %esi *(ebp+0x10) %edi %eax *(ebp+0x14) *(ebp+0x18))
+$populate-mu-function-body:end:
+    # . restore registers
+    5f/pop-to-edi
+    5e/pop-to-esi
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+# parses a block, assuming that the leading '{' has already been read by the caller
+parse-mu-block:  # in: (addr buffered-file), vars: (addr stack live-var), fn: (addr function), out: (addr handle block), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # pseudocode:
+    #   var line: (stream byte 512)
+    #   var word-slice: slice
+    #   allocate(Heap, Stmt-size, out)
+    #   var out-addr: (addr block) = lookup(*out)
+    #   out-addr->tag = 0/block
+    #   out-addr->var = some unique name
+    #   push(vars, {out-addr->var, false})
+    #   while true                                  # line loop
+    #     clear-stream(line)
+    #     read-line-buffered(in, line)
+    #     if (line->write == 0) break               # end of file
+    #     word-slice = next-mu-token(line)
+    #     if slice-empty?(word-slice)               # end of line
+    #       continue
+    #     else if slice-starts-with?(word-slice, "#")
+    #       continue
+    #     else if slice-equal?(word-slice, "{")
+    #       assert(no-tokens-in(line))
+    #       block = parse-mu-block(in, vars, fn)
+    #       append-to-block(out-addr, block)
+    #     else if slice-equal?(word-slice, "}")
+    #       break
+    #     else if slice-ends-with?(word-slice, ":")
+    #       # TODO: error-check the rest of 'line'
+    #       --word-slice->end to skip ':'
+    #       named-block = parse-mu-named-block(word-slice, in, vars, fn)
+    #       append-to-block(out-addr, named-block)
+    #     else if slice-equal?(word-slice, "var")
+    #       var-def = parse-mu-var-def(line, vars, fn)
+    #       append-to-block(out-addr, var-def)
+    #     else
+    #       stmt = parse-mu-stmt(line, vars, fn)
+    #       append-to-block(out-addr, stmt)
+    #   pop(vars)
+    #
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    52/push-edx
+    53/push-ebx
+    57/push-edi
+    # var line/ecx: (stream byte 512)
+    81 5/subop/subtract %esp 0x200/imm32
+    68/push 0x200/imm32/size
+    68/push 0/imm32/read
+    68/push 0/imm32/write
+    89/<- %ecx 4/r32/esp
+    # var word-slice/edx: slice
+    68/push 0/imm32/end
+    68/push 0/imm32/start
+    89/<- %edx 4/r32/esp
+    # allocate into out
+    (allocate Heap *Stmt-size *(ebp+0x14))
+    # var out-addr/edi: (addr block) = lookup(*out)
+    8b/-> *(ebp+0x14) 7/r32/edi
+    (lookup *edi *(edi+4))  # => eax
+    89/<- %edi 0/r32/eax
+    # out-addr->tag is 0 (block) by default
+    # set out-addr->var
+    8d/copy-address *(edi+0xc) 0/r32/eax  # Block-var
+    (new-block-name *(ebp+0x10) %eax)
+    # push(vars, out-addr->var)
+    (push *(ebp+0xc) *(edi+0xc))  # Block-var
+    (push *(ebp+0xc) *(edi+0x10))  # Block-var
+    (push *(ebp+0xc) 0)  # false
+    # increment *Curr-block-depth
+    ff 0/subop/increment *Curr-block-depth
+    {
+$parse-mu-block:line-loop:
+      # line = read-line-buffered(in)
+      (clear-stream %ecx)
+      (read-line-buffered *(ebp+8) %ecx)
+#?       (write-buffered Stderr "line: ")
+#?       (write-stream-data Stderr %ecx)
+#? #?       (write-buffered Stderr Newline)  # line has its own newline
+#?       (flush Stderr)
+#?       (rewind-stream %ecx)
+      # if (line->write == 0) break
+      81 7/subop/compare *ecx 0/imm32
+      0f 84/jump-if-= break/disp32
+#?       (write-buffered Stderr "vars:\n")
+#?       (dump-vars *(ebp+0xc))
+      # word-slice = next-mu-token(line)
+      (next-mu-token %ecx %edx)
+#?       (write-buffered Stderr "word: ")
+#?       (write-slice-buffered Stderr %edx)
+#?       (write-buffered Stderr Newline)
+#?       (flush Stderr)
+      # if slice-empty?(word-slice) continue
+      (slice-empty? %edx)
+      3d/compare-eax-and 0/imm32/false
+      0f 85/jump-if-!= loop/disp32
+      # if (slice-starts-with?(word-slice, '#') continue
+      # . eax = *word-slice->start
+      8b/-> *edx 0/r32/eax
+      8a/copy-byte *eax 0/r32/AL
+      81 4/subop/and %eax 0xff/imm32
+      # . if (eax == '#') continue
+      3d/compare-eax-and 0x23/imm32/hash
+      0f 84/jump-if-= loop/disp32
+      # if slice-equal?(word-slice, "{")
+      {
+$parse-mu-block:check-for-block:
+        (slice-equal? %edx "{")
+        3d/compare-eax-and 0/imm32/false
+        74/jump-if-= break/disp8
+        (check-no-tokens-left %ecx)
+        # parse new block and append
+        # . var tmp/eax: (handle block)
+        68/push 0/imm32
+        68/push 0/imm32
+        89/<- %eax 4/r32/esp
+        # .
+        (parse-mu-block *(ebp+8) *(ebp+0xc) *(ebp+0x10) %eax *(ebp+0x18) *(ebp+0x1c))
+        (append-to-block Heap %edi  *eax *(eax+4))
+        # . reclaim tmp
+        81 0/subop/add %esp 8/imm32
+        # .
+        e9/jump $parse-mu-block:line-loop/disp32
+      }
+      # if slice-equal?(word-slice, "}") break
+$parse-mu-block:check-for-end:
+      (slice-equal? %edx "}")
+      3d/compare-eax-and 0/imm32/false
+      0f 85/jump-if-!= break/disp32
+      # if slice-ends-with?(word-slice, ":") parse named block and append
+      {
+$parse-mu-block:check-for-named-block:
+        # . eax = *(word-slice->end-1)
+        8b/-> *(edx+4) 0/r32/eax
+        48/decrement-eax
+        8a/copy-byte *eax 0/r32/AL
+        81 4/subop/and %eax 0xff/imm32
+        # . if (eax != ':') break
+        3d/compare-eax-and 0x3a/imm32/colon
+        0f 85/jump-if-!= break/disp32
+        # TODO: error-check the rest of 'line'
+        #
+        # skip ':'
+        ff 1/subop/decrement *(edx+4)  # Slice-end
+        # var tmp/eax: (handle block)
+        68/push 0/imm32
+        68/push 0/imm32
+        89/<- %eax 4/r32/esp
+        #
+        (parse-mu-named-block %edx *(ebp+8) *(ebp+0xc) *(ebp+0x10) %eax *(ebp+0x18) *(ebp+0x1c))
+        (append-to-block Heap %edi  *eax *(eax+4))
+        # reclaim tmp
+        81 0/subop/add %esp 8/imm32
+        #
+        e9/jump $parse-mu-block:line-loop/disp32
+      }
+      # if slice-equal?(word-slice, "var")
+      {
+$parse-mu-block:check-for-var:
+        (slice-equal? %edx "var")
+        3d/compare-eax-and 0/imm32/false
+        74/jump-if-= break/disp8
+        # var tmp/eax: (handle block)
+        68/push 0/imm32
+        68/push 0/imm32
+        89/<- %eax 4/r32/esp
+        #
+        (parse-mu-var-def %ecx *(ebp+0xc) %eax *(ebp+0x10) *(ebp+0x18) *(ebp+0x1c))
+        (append-to-block Heap %edi  *eax *(eax+4))
+        # reclaim tmp
+        81 0/subop/add %esp 8/imm32
+        #
+        e9/jump $parse-mu-block:line-loop/disp32
+      }
+$parse-mu-block:regular-stmt:
+      # otherwise
+      # var tmp/eax: (handle block)
+      68/push 0/imm32
+      68/push 0/imm32
+      89/<- %eax 4/r32/esp
+      #
+      (parse-mu-stmt %ecx *(ebp+0xc) *(ebp+0x10) %eax *(ebp+0x18) *(ebp+0x1c))
+      (append-to-block Heap %edi  *eax *(eax+4))
+      # reclaim tmp
+      81 0/subop/add %esp 8/imm32
+      #
+      e9/jump loop/disp32
+    } # end line loop
+    (clean-up-blocks *(ebp+0xc) *Curr-block-depth *(ebp+0x10))
+    # decrement *Curr-block-depth
+    ff 1/subop/decrement *Curr-block-depth
+    # pop(vars)
+    (pop *(ebp+0xc))  # => eax
+    (pop *(ebp+0xc))  # => eax
+    (pop *(ebp+0xc))  # => eax
+$parse-mu-block:end:
+    # . reclaim locals
+    81 0/subop/add %esp 0x214/imm32
+    # . restore registers
+    5f/pop-to-edi
+    5b/pop-to-ebx
+    5a/pop-to-edx
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+$parse-mu-block:abort:
+    # error("'{' or '}' should be on its own line, but got '")
+    (write-buffered *(ebp+0x18) "'{' or '}' should be on its own line, but got '")
+    (rewind-stream %ecx)
+    (write-stream-data *(ebp+0x18) %ecx)
+    (write-buffered *(ebp+0x18) "'\n")
+    (flush *(ebp+0x18))
+    (stop *(ebp+0x1c) 1)
+    # never gets here
+
+new-block-name:  # fn: (addr function), out: (addr handle var)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    52/push-edx
+    # var n/ecx: int = len(fn->name) + 10 for an int + 2 for '$:'
+    8b/-> *(ebp+8) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    8b/-> *eax 0/r32/eax  # String-size
+    05/add-to-eax 0xd/imm32  # 10 + 2 for '$:'
+    89/<- %ecx 0/r32/eax
+    # var name/edx: (stream byte n)
+    29/subtract-from %esp 1/r32/ecx
+    ff 6/subop/push %ecx
+    68/push 0/imm32/read
+    68/push 0/imm32/write
+    89/<- %edx 4/r32/esp
+    (clear-stream %edx)
+    # eax = fn->name
+    8b/-> *(ebp+8) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    # construct result using Next-block-index (and increment it)
+    (write %edx "$")
+    (write %edx %eax)
+    (write %edx ":")
+    (write-int32-hex %edx *Next-block-index)
+    ff 0/subop/increment *Next-block-index
+    # var s/eax: slice = {name->data, name->data + name->write}  (clobbering edx)
+    # . eax = name->write
+    8b/-> *edx 0/r32/eax
+    # . edx = name->data
+    8d/copy-address *(edx+0xc) 2/r32/edx
+    # . eax = name->write + name->data
+    01/add-to %eax 2/r32/edx
+    # . push {edx, eax}
+    ff 6/subop/push %eax
+    ff 6/subop/push %edx
+    89/<- %eax 4/r32/esp
+    # out = new literal(s)
+    (new-literal Heap %eax *(ebp+0xc))
+#?     8b/-> *(ebp+0xc) 0/r32/eax
+#?     (write-buffered Stderr "type allocid in caller after new-literal: ")
+#?     (write-int32-hex-buffered Stderr *(eax+8))
+#?     (write-buffered Stderr " for var ")
+#?     (write-int32-hex-buffered Stderr %eax)
+#?     (write-buffered Stderr Newline)
+#?     (flush Stderr)
+$new-block-name:end:
+    # . reclaim locals
+    81 0/subop/add %ecx 0xc/imm32  # name.{read/write/len}
+    81 0/subop/add %ecx 8/imm32  # slice
+    01/add-to %esp 1/r32/ecx
+    # . restore registers
+    5a/pop-to-edx
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+check-no-tokens-left:  # line: (addr stream byte)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    # var s/ecx: slice
+    68/push 0/imm32/end
+    68/push 0/imm32/start
+    89/<- %ecx 4/r32/esp
+    #
+    (next-mu-token *(ebp+8) %ecx)
+    # if slice-empty?(s) return
+    (slice-empty? %ecx)
+    3d/compare-eax-and 0/imm32/false
+    75/jump-if-!= $check-no-tokens-left:end/disp8
+    # if (slice-starts-with?(s, '#') return
+    # . eax = *s->start
+    8b/-> *edx 0/r32/eax
+    8a/copy-byte *eax 0/r32/AL
+    81 4/subop/and %eax 0xff/imm32
+    # . if (eax == '#') continue
+    3d/compare-eax-and 0x23/imm32/hash
+    74/jump-if-= $check-no-tokens-left:end/disp8
+    # abort
+    (write-buffered Stderr "'{' or '}' should be on its own line, but got '")
+    (rewind-stream %ecx)
+    (write-stream 2 %ecx)
+    (write-buffered Stderr "'\n")
+    (flush Stderr)
+    # . syscall(exit, 1)
+    bb/copy-to-ebx  1/imm32
+    e8/call syscall_exit/disp32
+    # never gets here
+$check-no-tokens-left:end:
+    # . reclaim locals
+    81 0/subop/add %esp 8/imm32
+    # . restore registers
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+parse-mu-named-block:  # name: (addr slice), in: (addr buffered-file), vars: (addr stack live-var), fn: (addr function), out: (addr handle stmt), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # pseudocode:
+    #   var v: (handle var)
+    #   new-literal(name, v)
+    #   push(vars, {v, false})
+    #   parse-mu-block(in, vars, fn, out)
+    #   pop(vars)
+    #   out->tag = block
+    #   out->var = v
+    #
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    57/push-edi
+    # var v/ecx: (handle var)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %ecx 4/r32/esp
+    #
+    (new-literal Heap *(ebp+8) %ecx)
+    # push(vars, v)
+    (push *(ebp+0x10) *ecx)
+    (push *(ebp+0x10) *(ecx+4))
+    (push *(ebp+0x10) 0)  # false
+    #
+    (parse-mu-block *(ebp+0xc) *(ebp+0x10) *(ebp+0x14) *(ebp+0x18) *(ebp+0x1c) *(ebp+0x20))
+    # pop v off vars
+    (pop *(ebp+0x10))  # => eax
+    (pop *(ebp+0x10))  # => eax
+    (pop *(ebp+0x10))  # => eax
+    # var out-addr/edi: (addr stmt) = lookup(*out)
+    8b/-> *(ebp+0x18) 7/r32/edi
+    (lookup *edi *(edi+4))  # => eax
+    89/<- %edi 0/r32/eax
+    # out-addr->tag = named-block
+    c7 0/subop/copy *edi 0/imm32/block  # Stmt-tag
+    # out-addr->var = v
+    8b/-> *ecx 0/r32/eax
+    89/<- *(edi+0xc) 0/r32/eax  # Block-var
+    8b/-> *(ecx+4) 0/r32/eax
+    89/<- *(edi+0x10) 0/r32/eax  # Block-var
+$parse-mu-named-block:end:
+    # . reclaim locals
+    81 0/subop/add %esp 8/imm32
+    # . restore registers
+    5f/pop-to-edi
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+parse-mu-var-def:  # line: (addr stream byte), vars: (addr stack live-var), out: (addr handle stmt), fn: (addr function), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    52/push-edx
+    56/push-esi
+    57/push-edi
+    # edi = out
+    8b/-> *(ebp+0x10) 7/r32/edi
+    # var word-slice/ecx: slice
+    68/push 0/imm32/end
+    68/push 0/imm32/start
+    89/<- %ecx 4/r32/esp
+    # var v/edx: (handle var)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    # v = parse-var-with-type(next-mu-token(line))
+    (next-mu-token *(ebp+8) %ecx)
+    {
+      # just for tests, support null fn
+      8b/-> *(ebp+0x14) 0/r32/eax
+      3d/compare-eax-and 0/imm32
+      74/jump-if-= break/disp8
+      (lookup *eax *(eax+4))  # Var-name Var-name => eax
+    }
+    (parse-var-with-type %ecx *(ebp+8) %edx %eax *(ebp+0x18) *(ebp+0x1c))
+    # var v-addr/esi: (addr var)
+    (lookup *edx *(edx+4))  # => eax
+    89/<- %esi 0/r32/eax
+    # v->block-depth = *Curr-block-depth
+    8b/-> *Curr-block-depth 0/r32/eax
+    89/<- *(esi+0x10) 0/r32/eax  # Var-block-depth
+    # either v has no register and there's no more to this line
+    81 7/subop/compare *(esi+0x18) 0/imm32
+    {
+      75/jump-if-!= break/disp8
+      # if v-addr->type == byte, abort
+      (lookup *(esi+8) *(esi+0xc))  # Var-type Var-type => eax
+      (is-simple-mu-type? %eax 8)  # byte => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 85/jump-if-!= $parse-mu-var-def:error-byte-on-stack/disp32
+      # ensure that there's nothing else on this line
+      (next-mu-token *(ebp+8) %ecx)
+      (slice-empty? %ecx)  # => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 84/jump-if-= $parse-mu-var-def:error2/disp32
+      #
+      (new-var-def Heap  *edx *(edx+4)  %edi)
+      e9/jump $parse-mu-var-def:update-vars/disp32
+    }
+    # or v has a register and there's more to this line
+    {
+      0f 84/jump-if-= break/disp32
+      # if v-addr->type == byte, check for unsupported registers
+      {
+        (lookup *(esi+8) *(esi+0xc))  # Var-type Var-type => eax
+        (is-simple-mu-type? %eax 8)  # byte => eax
+        3d/compare-eax-and 0/imm32/false
+        74/jump-if-= break/disp8
+        (lookup *(esi+0x18) *(esi+0x1c))  # => eax
+        (string-equal? %eax "esi")  # => eax
+        3d/compare-eax-and 0/imm32/false
+        0f 85/jump-if-!= $parse-mu-var-def:error-byte-registers/disp32
+        (lookup *(esi+0x18) *(esi+0x1c))  # => eax
+        (string-equal? %eax "edi")  # => eax
+        3d/compare-eax-and 0/imm32/false
+        0f 85/jump-if-!= $parse-mu-var-def:error-byte-registers/disp32
+      }
+      # TODO: vars of type 'byte' should only be initialized by clearing to 0
+      # ensure that the next word is '<-'
+      (next-mu-token *(ebp+8) %ecx)
+      (slice-equal? %ecx "<-")  # => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 84/jump-if-= $parse-mu-var-def:error1/disp32
+      #
+      (new-reg-var-def Heap  *edx *(edx+4)  %edi)
+      (lookup *edi *(edi+4))  # => eax
+      (add-operation-and-inputs-to-stmt %eax *(ebp+8) *(ebp+0xc) *(ebp+0x14) *(ebp+0x18) *(ebp+0x1c))
+    }
+$parse-mu-var-def:update-vars:
+    # push 'v' at end of function
+    (push *(ebp+0xc) *edx)
+    (push *(ebp+0xc) *(edx+4))
+    (push *(ebp+0xc) 0)  # Live-var-register-spilled is unused during parsing
+$parse-mu-var-def:end:
+    # . reclaim locals
+    81 0/subop/add %esp 0x10/imm32
+    # . restore registers
+    5f/pop-to-edi
+    5e/pop-to-esi
+    5a/pop-to-edx
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+$parse-mu-var-def:error1:
+    (rewind-stream *(ebp+8))
+    # error("register variable requires a valid instruction to initialize but got '" line "'\n")
+    (write-buffered *(ebp+0x18) "register variable requires a valid instruction to initialize but got '")
+    (flush *(ebp+0x18))
+    (write-stream-data *(ebp+0x18) *(ebp+8))
+    (write-buffered *(ebp+0x18) "'\n")
+    (flush *(ebp+0x18))
+    (stop *(ebp+0x1c) 1)
+    # never gets here
+
+$parse-mu-var-def:error2:
+    # error("fn " fn ": var " var ": variables on the stack can't take an initializer\n")
+    (write-buffered *(ebp+0x18) "fn ")
+    8b/-> *(ebp+0x14) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x18) %eax)
+    (write-buffered *(ebp+0x18) ": var ")
+    # var v-addr/eax: (addr var) = lookup(v)
+    (lookup *edx *(edx+4))  # => eax
+    (lookup *eax *(eax+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x18) %eax)
+    (write-buffered *(ebp+0x18) ": variables on the stack can't take an initializer\n")
+    (flush *(ebp+0x18))
+    (stop *(ebp+0x1c) 1)
+    # never gets here
+
+$parse-mu-var-def:error-byte-on-stack:
+    # error("fn " fn ": var '" var "' of type 'byte' cannot be on the stack\n")
+    (write-buffered *(ebp+0x18) "fn ")
+    8b/-> *(ebp+0x14) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x18) %eax)
+    (write-buffered *(ebp+0x18) ": var '")
+    # var v-addr/eax: (addr var) = lookup(v)
+    (lookup *edx *(edx+4))  # => eax
+    (lookup *eax *(eax+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x18) %eax)
+    (write-buffered *(ebp+0x18) "' of type 'byte' cannot be on the stack\n")
+    (flush *(ebp+0x18))
+    (stop *(ebp+0x1c) 1)
+    # never gets here
+
+$parse-mu-var-def:error-byte-registers:
+    # error("fn " fn ": var '" var "' of type 'byte' cannot be in esi or edi\n")
+    (write-buffered *(ebp+0x18) "fn ")
+    8b/-> *(ebp+0x14) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x18) %eax)
+    (write-buffered *(ebp+0x18) ": var '")
+    # var v-addr/eax: (addr var) = lookup(v)
+    (lookup *edx *(edx+4))  # => eax
+    (lookup *eax *(eax+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x18) %eax)
+    (write-buffered *(ebp+0x18) "' of type 'byte' cannot be in esi or edi\n")
+    (flush *(ebp+0x18))
+    (stop *(ebp+0x1c) 1)
+    # never gets here
+
+test-parse-mu-var-def:
+    # 'var n: int'
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    8b/-> *Primitive-type-ids 0/r32/eax
+    89/<- *Type-id 0/r32/eax  # stream-write
+    (clear-stream _test-input-stream)
+    (write _test-input-stream "n: int\n")  # caller has consumed the 'var'
+    c7 0/subop/copy *Curr-block-depth 1/imm32
+    # var out/esi: (handle stmt)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %esi 4/r32/esp
+    # var vars/ecx: (stack (addr var) 16)
+    81 5/subop/subtract %esp 0xc0/imm32
+    68/push 0xc0/imm32/size
+    68/push 0/imm32/top
+    89/<- %ecx 4/r32/esp
+    (clear-stack %ecx)
+    # convert
+    (parse-mu-var-def _test-input-stream %ecx %esi 0 Stderr 0)
+    # var out-addr/esi: (addr stmt)
+    (lookup *esi *(esi+4))  # => eax
+    89/<- %esi 0/r32/eax
+    #
+    (check-ints-equal *esi 2 "F - test-parse-mu-var-def/tag")  # Stmt-tag is var-def
+    # var v/ecx: (addr var) = lookup(out->var)
+    (lookup *(esi+4) *(esi+8))  # Vardef-var Vardef-var => eax
+    89/<- %ecx 0/r32/eax
+    # v->name
+    (lookup *ecx *(ecx+4))  # Var-name Var-name => eax
+    (check-strings-equal %eax "n" "F - test-parse-mu-var-def/var-name")
+    # v->register
+    (check-ints-equal *(ecx+0x18) 0 "F - test-parse-mu-var-def/var-register")  # Var-register
+    # v->block-depth
+    (check-ints-equal *(ecx+0x10) 1 "F - test-parse-mu-var-def/output-block-depth")  # Var-block-depth
+    # v->type == int
+    (lookup *(ecx+8) *(ecx+0xc))  # Var-type Var-type => eax
+    (check-ints-equal *eax 1 "F - test-parse-mu-var-def/var-type:0")  # Type-tree-is-atom
+    (check-ints-equal *(eax+4) 1 "F - test-parse-mu-var-def/var-type:1")  # Type-tree-value
+    (check-ints-equal *(eax+0xc) 0 "F - test-parse-mu-var-def/var-type:2")  # Type-tree-right
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-parse-mu-reg-var-def:
+    # 'var n/eax: int <- copy 0'
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    8b/-> *Primitive-type-ids 0/r32/eax
+    89/<- *Type-id 0/r32/eax  # stream-write
+    (clear-stream _test-input-stream)
+    (write _test-input-stream "n/eax: int <- copy 0\n")  # caller has consumed the 'var'
+    c7 0/subop/copy *Curr-block-depth 1/imm32
+    # var out/esi: (handle stmt)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %esi 4/r32/esp
+    # var vars/ecx: (stack (addr var) 16)
+    81 5/subop/subtract %esp 0xc0/imm32
+    68/push 0xc0/imm32/size
+    68/push 0/imm32/top
+    89/<- %ecx 4/r32/esp
+    (clear-stack %ecx)
+    # convert
+    (parse-mu-var-def _test-input-stream %ecx %esi 0 Stderr 0)
+    # var out-addr/esi: (addr stmt)
+    (lookup *esi *(esi+4))  # => eax
+    89/<- %esi 0/r32/eax
+    #
+    (check-ints-equal *esi 3 "F - test-parse-mu-reg-var-def/tag")  # Stmt-tag is reg-var-def
+    # var v/ecx: (addr var) = lookup(out->outputs->value)
+    # . eax: (addr stmt-var) = lookup(out->outputs)
+    (lookup *(esi+0x14) *(esi+0x18))  # Regvardef-outputs Regvardef-outputs => eax
+    # .
+    (check-ints-equal *(eax+8) 0 "F - test-parse-mu-reg-var-def/single-output")  # Stmt-var-next
+    # . eax: (addr var) = lookup(eax->value)
+    (lookup *eax *(eax+4))  # Stmt-var-value Stmt-var-value => eax
+    # . ecx = eax
+    89/<- %ecx 0/r32/eax
+    # v->name
+    (lookup *ecx *(ecx+4))  # Var-name Var-name => eax
+    (check-strings-equal %eax "n" "F - test-parse-mu-reg-var-def/output-name")  # Var-name
+    # v->register
+    (lookup *(ecx+0x18) *(ecx+0x1c))  # Var-register Var-register => eax
+    (check-strings-equal %eax "eax" "F - test-parse-mu-reg-var-def/output-register")
+    # v->block-depth
+    (check-ints-equal *(ecx+0x10) 1 "F - test-parse-mu-reg-var-def/output-block-depth")  # Var-block-depth
+    # v->type == int
+    (lookup *(ecx+8) *(ecx+0xc))  # Var-type Var-type => eax
+    (check-ints-equal *eax 1 "F - test-parse-mu-reg-var-def/output-type:0")  # Type-tree-is-atom
+    (check-ints-equal *(eax+4) 1 "F - test-parse-mu-reg-var-def/output-type:1")  # Type-tree-value
+    (check-ints-equal *(eax+0xc) 0 "F - test-parse-mu-reg-var-def/output-type:2")  # Type-tree-right
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+parse-mu-stmt:  # line: (addr stream byte), vars: (addr stack live-var), fn: (addr function), out: (addr handle stmt), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # pseudocode:
+    #   var name: slice
+    #   allocate(Heap, Stmt-size, out)
+    #   var out-addr: (addr stmt) = lookup(*out)
+    #   out-addr->tag = stmt
+    #   if stmt-has-outputs?(line)
+    #     while true
+    #       name = next-mu-token(line)
+    #       if (name == '<-') break
+    #       assert(is-identifier?(name))
+    #       var v: (handle var) = lookup-var(name, vars)
+    #       out-addr->outputs = append(v, out-addr->outputs)
+    #   add-operation-and-inputs-to-stmt(out-addr, line, vars)
+    #
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    52/push-edx
+    53/push-ebx
+    57/push-edi
+    # var name/ecx: slice
+    68/push 0/imm32/end
+    68/push 0/imm32/start
+    89/<- %ecx 4/r32/esp
+    # var is-deref?/edx: boolean = false
+    ba/copy-to-edx 0/imm32/false
+    # var v: (handle var)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %ebx 4/r32/esp
+    #
+    (allocate Heap *Stmt-size *(ebp+0x14))
+    # var out-addr/edi: (addr stmt) = lookup(*out)
+    8b/-> *(ebp+0x14) 7/r32/edi
+    (lookup *edi *(edi+4))  # => eax
+    89/<- %edi 0/r32/eax
+    # out-addr->tag = 1/stmt
+    c7 0/subop/copy *edi 1/imm32/stmt1  # Stmt-tag
+    {
+      (stmt-has-outputs? *(ebp+8))
+      3d/compare-eax-and 0/imm32/false
+      0f 84/jump-if-= break/disp32
+      {
+$parse-mu-stmt:read-outputs:
+        # name = next-mu-token(line)
+        (next-mu-token *(ebp+8) %ecx)
+        # if slice-empty?(word-slice) break
+        (slice-empty? %ecx)  # => eax
+        3d/compare-eax-and 0/imm32/false
+        0f 85/jump-if-!= break/disp32
+        # if (name == "<-") break
+        (slice-equal? %ecx "<-")  # => eax
+        3d/compare-eax-and 0/imm32/false
+        0f 85/jump-if-!= break/disp32
+        # if slice-starts-with?(name, "*") abort
+        8b/-> *ecx 0/r32/eax  # Slice-start
+        8a/copy-byte *eax 0/r32/AL
+        81 4/subop/and %eax 0xff/imm32
+        3d/compare-eax-and 0x2a/imm32/asterisk
+        0f 84/jump-if-= $parse-mu-stmt:error-output-dereferenced/disp32
+        # assert(is-identifier?(name))
+        (is-identifier? %ecx)  # => eax
+        3d/compare-eax-and 0/imm32/false
+        0f 84/jump-if-= $parse-mu-stmt:abort/disp32
+        #
+        (lookup-var %ecx *(ebp+0xc) %ebx *(ebp+0x10) *(ebp+0x18) *(ebp+0x1c))
+        8d/copy-address *(edi+0x14) 0/r32/eax  # Stmt1-outputs
+        (append-stmt-var Heap  *ebx *(ebx+4)  *(edi+0x14) *(edi+0x18)  0  %eax)  # Stmt1-outputs
+        #
+        e9/jump loop/disp32
+      }
+    }
+    (add-operation-and-inputs-to-stmt %edi *(ebp+8) *(ebp+0xc) *(ebp+0x10) *(ebp+0x18) *(ebp+0x1c))
+$parse-mu-stmt:end:
+    # . reclaim locals
+    81 0/subop/add %esp 0x10/imm32
+    # . restore registers
+    5f/pop-to-edi
+    5b/pop-to-ebx
+    5a/pop-to-edx
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+$parse-mu-stmt:abort:
+    # error("invalid identifier '" name "'\n")
+    (write-buffered *(ebp+0x18) "fn ")
+    8b/-> *(ebp+0x10) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x18) %eax)
+    (write-buffered *(ebp+0x18) ": invalid identifier '")
+    (write-slice-buffered *(ebp+0x18) %ecx)
+    (write-buffered *(ebp+0x18) "'\n")
+    (flush *(ebp+0x18))
+    (stop *(ebp+0x1c) 1)
+    # never gets here
+
+$parse-mu-stmt:error-output-dereferenced:
+    # error("invalid identifier '" name "'\n")
+    (write-buffered *(ebp+0x18) "fn ")
+    8b/-> *(ebp+0x10) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x18) %eax)
+    (write-buffered *(ebp+0x18) ": output '")
+    (write-slice-buffered *(ebp+0x18) %ecx)
+    (write-buffered *(ebp+0x18) "' should write to a register, and therefore cannot be dereferenced\n")
+    (flush *(ebp+0x18))
+    (stop *(ebp+0x1c) 1)
+    # never gets here
+
+add-operation-and-inputs-to-stmt:  # stmt: (addr stmt), line: (addr stream byte), vars: (addr stack live-var), fn: (addr function), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # pseudocode:
+    #   stmt->name = slice-to-string(next-mu-token(line))
+    #   while true
+    #     name = next-mu-token(line)
+    #     v = lookup-var-or-literal(name)
+    #     stmt->inouts = append(v, stmt->inouts)
+    #
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    52/push-edx
+    53/push-ebx
+    56/push-esi
+    57/push-edi
+    # edi = stmt
+    8b/-> *(ebp+8) 7/r32/edi
+    # var name/ecx: slice
+    68/push 0/imm32/end
+    68/push 0/imm32/start
+    89/<- %ecx 4/r32/esp
+    # var is-deref?/edx: boolean = false
+    ba/copy-to-edx 0/imm32/false
+    # var v/esi: (handle var)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %esi 4/r32/esp
+$add-operation-and-inputs-to-stmt:read-operation:
+    (next-mu-token *(ebp+0xc) %ecx)
+    8d/copy-address *(edi+4) 0/r32/eax  # Stmt1-operation or Regvardef-operationStmt1-operation or Regvardef-operation
+    (slice-to-string Heap %ecx %eax)
+    # var is-get?/ebx: boolean = (name == "get")
+    (slice-equal? %ecx "get")  # => eax
+    89/<- %ebx 0/r32/eax
+    {
+$add-operation-and-inputs-to-stmt:read-inouts:
+      # name = next-mu-token(line)
+      (next-mu-token *(ebp+0xc) %ecx)
+      # if slice-empty?(word-slice) break
+      (slice-empty? %ecx)  # => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 85/jump-if-!= break/disp32
+      # if (name == "<-") abort
+      (slice-equal? %ecx "<-")
+      3d/compare-eax-and 0/imm32/false
+      0f 85/jump-if-!= $add-operation-and-inputs-to-stmt:abort/disp32
+      # if (is-get? && second operand) lookup or create offset
+      {
+        81 7/subop/compare %ebx 0/imm32/false
+        74/jump-if-= break/disp8
+        (lookup *(edi+0xc) *(edi+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+        3d/compare-eax-and 0/imm32
+        74/jump-if-= break/disp8
+        (lookup-or-create-constant %eax %ecx %esi)
+#?         (lookup *esi *(esi+4))
+#?         (write-buffered Stderr "creating new output var ")
+#?         (write-int32-hex-buffered Stderr %eax)
+#?         (write-buffered Stderr " for field called ")
+#?         (write-slice-buffered Stderr %ecx)
+#?         (write-buffered Stderr "; var name ")
+#?         (lookup *eax *(eax+4))  # Var-name
+#?         (write-buffered Stderr %eax)
+#?         (write-buffered Stderr Newline)
+#?         (flush Stderr)
+        e9/jump $add-operation-and-inputs-to-stmt:save-var/disp32
+      }
+      # is-deref? = false
+      ba/copy-to-edx 0/imm32/false
+      # if (slice-starts-with?(name, '*')) ++name->start and set is-deref?
+      8b/-> *ecx 0/r32/eax  # Slice-start
+      8a/copy-byte *eax 0/r32/AL
+      81 4/subop/and %eax 0xff/imm32
+      3d/compare-eax-and 0x2a/imm32/asterisk
+      {
+        75/jump-if-!= break/disp8
+$add-operation-and-inputs-to-stmt:inout-is-deref:
+        ff 0/subop/increment *ecx
+        ba/copy-to-edx 1/imm32/true
+      }
+      (lookup-var-or-literal %ecx *(ebp+0x10) %esi *(ebp+0x14) *(ebp+0x18) *(ebp+0x1c))
+      # if (is-deref?) some additional checks
+      81 7/subop/compare %edx 0/imm32/false
+      {
+        74/jump-if-= break/disp8
+        # if var is not in register, abort
+        (lookup *esi *(esi+4))  # => eax
+        81 7/subop/compare *(eax+0x18) 0/imm32  # Var-register
+        0f 84/jump-if-= $add-operation-and-inputs-to-stmt:error-deref-on-stack/disp32
+        # if var is not an address, abort
+        (lookup *(eax+8) *(eax+0xc))  # Var-type Var-type => eax
+        (is-mu-addr-type? %eax)  # => eax
+        3d/compare-eax-and 0/imm32/false
+        0f 84/jump-if-= $add-operation-and-inputs-to-stmt:error-deref-non-addr/disp32
+      }
+$add-operation-and-inputs-to-stmt:save-var:
+      8d/copy-address *(edi+0xc) 0/r32/eax
+      (append-stmt-var Heap  *esi *(esi+4)  *(edi+0xc) *(edi+0x10)  %edx  %eax)  # Stmt1-inouts or Regvardef-inouts
+      #
+      e9/jump loop/disp32
+    }
+$add-operation-and-inputs-to-stmt:end:
+    # . reclaim locals
+    81 0/subop/add %esp 0x10/imm32
+    # . restore registers
+    5f/pop-to-edi
+    5e/pop-to-esi
+    5b/pop-to-ebx
+    5a/pop-to-edx
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+$add-operation-and-inputs-to-stmt:abort:
+    # error("fn ___: invalid identifier in '" line "'\n")
+    (write-buffered *(ebp+0x18) "fn ")
+    8b/-> *(ebp+0x14) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x18) %eax)
+    (rewind-stream *(ebp+0xc))
+    (write-buffered *(ebp+0x18) ": invalid identifier in '")
+    (write-stream-data *(ebp+0x18) *(ebp+0xc))
+    (write-buffered *(ebp+0x18) "'\n")
+    (flush *(ebp+0x18))
+    (stop *(ebp+0x1c) 1)
+    # never gets here
+
+$add-operation-and-inputs-to-stmt:error-deref-on-stack:
+    # error("fn ___: cannot dereference var ___ on stack\n")
+    (write-buffered *(ebp+0x18) "fn ")
+    8b/-> *(ebp+0x14) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x18) %eax)
+    (rewind-stream *(ebp+0xc))
+    (write-buffered *(ebp+0x18) ": cannot dereference var '")
+    (lookup *esi *(esi+4))  # => eax
+    (lookup *eax *(eax+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x18) %eax)
+    (write-buffered *(ebp+0x18) "' on stack\n")
+    (flush *(ebp+0x18))
+    (stop *(ebp+0x1c) 1)
+    # never gets here
+
+$add-operation-and-inputs-to-stmt:error-deref-non-addr:
+    # error("fn ___: cannot dereference non-addr var ___\n")
+    (write-buffered *(ebp+0x18) "fn ")
+    8b/-> *(ebp+0x14) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x18) %eax)
+    (rewind-stream *(ebp+0xc))
+    (write-buffered *(ebp+0x18) ": cannot dereference non-addr var '")
+    (lookup *esi *(esi+4))  # => eax
+    (lookup *eax *(eax+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x18) %eax)
+    (write-buffered *(ebp+0x18) "'\n")
+    (flush *(ebp+0x18))
+    (stop *(ebp+0x1c) 1)
+    # never gets here
+
+stmt-has-outputs?:  # line: (addr stream byte) -> result/eax: boolean
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    51/push-ecx
+    # var word-slice/ecx: slice
+    68/push 0/imm32/end
+    68/push 0/imm32/start
+    89/<- %ecx 4/r32/esp
+    # result = false
+    b8/copy-to-eax 0/imm32/false
+    (rewind-stream *(ebp+8))
+    {
+      (next-mu-token *(ebp+8) %ecx)
+      # if slice-empty?(word-slice) break
+      (slice-empty? %ecx)
+      3d/compare-eax-and 0/imm32/false
+      b8/copy-to-eax 0/imm32/false/result  # restore result (if we're here it's still false)
+      0f 85/jump-if-!= break/disp32
+      # if slice-starts-with?(word-slice, '#') break
+      # . eax = *word-slice->start
+      8b/-> *ecx 0/r32/eax
+      8a/copy-byte *eax 0/r32/AL
+      81 4/subop/and %eax 0xff/imm32
+      # . if (eax == '#') break
+      3d/compare-eax-and 0x23/imm32/hash
+      b8/copy-to-eax 0/imm32/false/result  # restore result (if we're here it's still false)
+      0f 84/jump-if-= break/disp32
+      # if slice-equal?(word-slice, '<-') return true
+      (slice-equal? %ecx "<-")
+      3d/compare-eax-and 0/imm32/false
+      74/jump-if-= loop/disp8
+      b8/copy-to-eax 1/imm32/true
+    }
+$stmt-has-outputs:end:
+    (rewind-stream *(ebp+8))
+    # . reclaim locals
+    81 0/subop/add %esp 8/imm32
+    # . restore registers
+    59/pop-to-ecx
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+# if 'name' starts with a digit, create a new literal var for it
+# otherwise return first 'name' from the top (back) of 'vars' and abort if not found
+lookup-var-or-literal:  # name: (addr slice), vars: (addr stack live-var), out: (addr handle var), fn: (addr function), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    56/push-esi
+    # esi = name
+    8b/-> *(ebp+8) 6/r32/esi
+    # if slice-empty?(name) abort
+    (slice-empty? %esi)  # => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 85/jump-if-!= $lookup-var-or-literal:abort/disp32
+    # var c/ecx: byte = *name->start
+    8b/-> *esi 1/r32/ecx
+    8a/copy-byte *ecx 1/r32/CL
+    81 4/subop/and %ecx 0xff/imm32
+    # if (is-decimal-digit?(c) || c == '-') return new var(name)
+    {
+      81 7/subop/compare %ecx 0x2d/imm32/dash
+      74/jump-if-= $lookup-var-or-literal:literal/disp8
+      (is-decimal-digit? %ecx)  # => eax
+      3d/compare-eax-and 0/imm32/false
+      74/jump-if-= break/disp8
+$lookup-var-or-literal:literal:
+      (new-literal-integer Heap %esi *(ebp+0x10) *(ebp+0x14) *(ebp+0x18) *(ebp+0x1c))
+      eb/jump $lookup-var-or-literal:end/disp8
+    }
+    # else if (c == '"') return new var(name)
+    {
+      81 7/subop/compare %ecx 0x22/imm32/dquote
+      75/jump-if-!= break/disp8
+$lookup-var-or-literal:literal-string:
+      (new-literal-string Heap %esi *(ebp+0x10))
+      eb/jump $lookup-var-or-literal:end/disp8
+    }
+    # otherwise return lookup-var(name, vars)
+    {
+$lookup-var-or-literal:var:
+      (lookup-var %esi *(ebp+0xc) *(ebp+0x10) *(ebp+0x14) *(ebp+0x18) *(ebp+0x1c))
+    }
+$lookup-var-or-literal:end:
+    # . restore registers
+    5e/pop-to-esi
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+$lookup-var-or-literal:abort:
+    (write-buffered *(ebp+0x18) "fn ")
+    8b/-> *(ebp+0x14) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x18) %eax)
+    (write-buffered *(ebp+0x18) ": empty variable!")
+    (flush *(ebp+0x18))
+    (stop *(ebp+0x1c) 1)
+    # never gets here
+
+# return first 'name' from the top (back) of 'vars' and abort if not found
+lookup-var:  # name: (addr slice), vars: (addr stack live-var), out: (addr handle var), fn: (addr function), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    #
+    (lookup-var-helper *(ebp+8) *(ebp+0xc) *(ebp+0x10) *(ebp+0x14) *(ebp+0x18) *(ebp+0x1c))
+    # if (*out == 0) abort
+    8b/-> *(ebp+0x10) 0/r32/eax
+    81 7/subop/compare *eax 0/imm32
+    74/jump-if-= $lookup-var:abort/disp8
+$lookup-var:end:
+    # . restore registers
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+$lookup-var:abort:
+    (write-buffered *(ebp+0x18) "fn ")
+    8b/-> *(ebp+0x14) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x18) %eax)
+    (write-buffered *(ebp+0x18) ": unknown variable '")
+    (write-slice-buffered *(ebp+0x18) *(ebp+8))
+    (write-buffered *(ebp+0x18) "'\n")
+    (flush *(ebp+0x18))
+    (stop *(ebp+0x1c) 1)
+    # never gets here
+
+# return first 'name' from the top (back) of 'vars', and 0/null if not found
+# ensure that 'name' if in a register is the topmost variable in that register
+lookup-var-helper:  # name: (addr slice), vars: (addr stack live-var), out: (addr handle var), fn: (addr function), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # pseudocode:
+    #   var curr: (addr handle var) = &vars->data[vars->top - 12]
+    #   var min = vars->data
+    #   while curr >= min
+    #     var v: (handle var) = *curr
+    #     if v->name == name
+    #       return
+    #     curr -= 12
+    #
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    52/push-edx
+    53/push-ebx
+    56/push-esi
+    57/push-edi
+    # clear out
+    (zero-out *(ebp+0x10) *Handle-size)
+    # esi = vars
+    8b/-> *(ebp+0xc) 6/r32/esi
+    # ebx = vars->top
+    8b/-> *esi 3/r32/ebx
+    # if (vars->top > vars->size) abort
+    3b/compare<- *(esi+4) 0/r32/eax
+    0f 8f/jump-if-> $lookup-var-helper:error1/disp32
+    # var min/edx: (addr handle var) = vars->data
+    8d/copy-address *(esi+8) 2/r32/edx
+    # var curr/ebx: (addr handle var) = &vars->data[vars->top - 12]
+    8d/copy-address *(esi+ebx-4) 3/r32/ebx  # vars + 8 + vars->type - 12
+    # var var-in-reg/edi: 16 addrs
+    68/push 0/imm32
+    68/push 0/imm32
+    68/push 0/imm32
+    68/push 0/imm32
+    68/push 0/imm32
+    68/push 0/imm32
+    68/push 0/imm32
+    68/push 0/imm32
+    68/push 0/imm32
+    68/push 0/imm32
+    68/push 0/imm32
+    68/push 0/imm32
+    68/push 0/imm32
+    68/push 0/imm32
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edi 4/r32/esp
+    {
+$lookup-var-helper:loop:
+      # if (curr < min) return
+      39/compare %ebx 2/r32/edx
+      0f 82/jump-if-addr< break/disp32
+      # var v/ecx: (addr var) = lookup(*curr)
+      (lookup *ebx *(ebx+4))  # => eax
+      89/<- %ecx 0/r32/eax
+      # var vn/eax: (addr array byte) = lookup(v->name)
+      (lookup *ecx *(ecx+4))  # Var-name Var-name => eax
+      # if (vn == name) return curr
+      (slice-equal? *(ebp+8) %eax)  # => eax
+      3d/compare-eax-and 0/imm32/false
+      {
+        74/jump-if-= break/disp8
+$lookup-var-helper:found:
+        # var vr/eax: (addr array byte) = lookup(v->register)
+        (lookup *(ecx+0x18) *(ecx+0x1c))  # Var-register Var-register => eax
+        3d/compare-eax-and 0/imm32
+        {
+          74/jump-if-= break/disp8
+$lookup-var-helper:found-register:
+          # var reg/eax: int = get(Registers, vr)
+          (get Mu-registers-unique %eax 0xc "Mu-registers-unique")  # => eax
+          8b/-> *eax 0/r32/eax
+          # if (var-in-reg[reg]) error
+          8b/-> *(edi+eax<<2) 0/r32/eax
+          3d/compare-eax-and 0/imm32
+          0f 85/jump-if-!= $lookup-var-helper:error2/disp32
+        }
+$lookup-var-helper:return:
+        # esi = out
+        8b/-> *(ebp+0x10) 6/r32/esi
+        # *out = *curr
+        8b/-> *ebx 0/r32/eax
+        89/<- *esi 0/r32/eax
+        8b/-> *(ebx+4) 0/r32/eax
+        89/<- *(esi+4) 0/r32/eax
+        # return
+        eb/jump $lookup-var-helper:end/disp8
+      }
+      # 'name' not yet found; update var-in-reg if v in register
+      # . var vr/eax: (addr array byte) = lookup(v->register)
+      (lookup *(ecx+0x18) *(ecx+0x1c))  # Var-register Var-register => eax
+      # . if (vr == 0) continue
+      3d/compare-eax-and 0/imm32
+      74/jump-if-= $lookup-var-helper:continue/disp8
+      # . var reg/eax: int = get(Registers, vr)
+      (get Mu-registers-unique %eax 0xc "Mu-registers-unique")  # => eax
+      8b/-> *eax 0/r32/eax
+      # . var-in-reg[reg] = v
+      89/<- *(edi+eax<<2) 1/r32/ecx
+$lookup-var-helper:continue:
+      # curr -= 12
+      81 5/subop/subtract %ebx 0xc/imm32
+      e9/jump loop/disp32
+    }
+$lookup-var-helper:end:
+    # . reclaim locals
+    81 0/subop/add %esp 0x40/imm32
+    # . restore registers
+    5f/pop-to-edi
+    5e/pop-to-esi
+    5b/pop-to-ebx
+    5a/pop-to-edx
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+$lookup-var-helper:error1:
+    (write-buffered *(ebp+0x18) "fn ")
+    8b/-> *(ebp+0x14) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x18) %eax)
+    (write-buffered *(ebp+0x18) ": malformed stack when looking up '")
+    (write-slice-buffered *(ebp+0x18) *(ebp+8))
+    (write-buffered *(ebp+0x18) "'\n")
+    (flush *(ebp+0x18))
+    (stop *(ebp+0x1c) 1)
+    # never gets here
+
+$lookup-var-helper:error2:
+    # eax contains the conflicting var at this point
+    (write-buffered *(ebp+0x18) "fn ")
+    50/push-eax
+    8b/-> *(ebp+0x14) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x18) %eax)
+    58/pop-eax
+    (write-buffered *(ebp+0x18) ": register ")
+    50/push-eax
+    (lookup *(eax+0x18) *(eax+0x1c))  # Var-register Var-register => eax
+    (write-buffered *(ebp+0x18) %eax)
+    58/pop-to-eax
+    (write-buffered *(ebp+0x18) " reads var '")
+    (write-slice-buffered *(ebp+0x18) *(ebp+8))
+    (write-buffered *(ebp+0x18) "' after writing var '")
+    (lookup *eax *(eax+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x18) %eax)
+    (write-buffered *(ebp+0x18) "'\n")
+    (flush *(ebp+0x18))
+    (stop *(ebp+0x1c) 1)
+    # never gets here
+
+dump-vars:  # vars: (addr stack live-var)
+    # pseudocode:
+    #   var curr: (addr handle var) = &vars->data[vars->top - 12]
+    #   var min = vars->data
+    #   while curr >= min
+    #     var v: (handle var) = *curr
+    #     print v
+    #     curr -= 12
+    #
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    52/push-edx
+    53/push-ebx
+    56/push-esi
+    # esi = vars
+    8b/-> *(ebp+8) 6/r32/esi
+    # ebx = vars->top
+    8b/-> *esi 3/r32/ebx
+    # var min/edx: (addr handle var) = vars->data
+    8d/copy-address *(esi+8) 2/r32/edx
+    # var curr/ebx: (addr handle var) = &vars->data[vars->top - 12]
+    8d/copy-address *(esi+ebx-4) 3/r32/ebx  # vars + 8 + vars->type - 12
+    {
+$dump-vars:loop:
+      # if (curr < min) return
+      39/compare %ebx 2/r32/edx
+      0f 82/jump-if-addr< break/disp32
+      #
+      (write-buffered Stderr "  var@")
+      (dump-var 2 %ebx)
+      # curr -= 12
+      81 5/subop/subtract %ebx 0xc/imm32
+      e9/jump loop/disp32
+    }
+$dump-vars:end:
+    # . restore registers
+    5e/pop-to-esi
+    5b/pop-to-ebx
+    5a/pop-to-edx
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+== data
+# Like Registers, but no esp or ebp
+Mu-registers:  # (addr stream {(handle array byte), int})
+  # a table is a stream
+  0xa8/imm32/write
+  0/imm32/read
+  0xa8/imm32/length
+  # data
+  # general-purpose registers
+  # it is perfectly ok to use fake alloc-ids -- as long as you never try to reclaim them
+  0x11/imm32/alloc-id $Mu-register-eax/imm32 0/imm32
+  0x11/imm32/alloc-id $Mu-register-ecx/imm32 1/imm32
+  0x11/imm32/alloc-id $Mu-register-edx/imm32 2/imm32
+  0x11/imm32/alloc-id $Mu-register-ebx/imm32 3/imm32
+  0x11/imm32/alloc-id $Mu-register-esi/imm32 6/imm32
+  0x11/imm32/alloc-id $Mu-register-edi/imm32 7/imm32
+  # floating-point registers
+  0x11/imm32/alloc-id $Mu-register-xmm0/imm32 0/imm32
+  0x11/imm32/alloc-id $Mu-register-xmm1/imm32 1/imm32
+  0x11/imm32/alloc-id $Mu-register-xmm2/imm32 2/imm32
+  0x11/imm32/alloc-id $Mu-register-xmm3/imm32 3/imm32
+  0x11/imm32/alloc-id $Mu-register-xmm4/imm32 4/imm32
+  0x11/imm32/alloc-id $Mu-register-xmm5/imm32 5/imm32
+  0x11/imm32/alloc-id $Mu-register-xmm6/imm32 6/imm32
+  0x11/imm32/alloc-id $Mu-register-xmm7/imm32 7/imm32
+
+# Like Mu-registers, but with unique codes for integer and floating-point
+# registers.
+# Don't use this for code-generation, only for checking.
+Mu-registers-unique:  # (addr stream {(handle array byte), int})
+  # a table is a stream
+  0xa8/imm32/write
+  0/imm32/read
+  0xa8/imm32/length
+  # data
+  # general-purpose registers
+  0x11/imm32/alloc-id $Mu-register-eax/imm32 0/imm32
+  0x11/imm32/alloc-id $Mu-register-ecx/imm32 1/imm32
+  0x11/imm32/alloc-id $Mu-register-edx/imm32 2/imm32
+  0x11/imm32/alloc-id $Mu-register-ebx/imm32 3/imm32
+  0x11/imm32/alloc-id $Mu-register-esi/imm32 6/imm32
+  0x11/imm32/alloc-id $Mu-register-edi/imm32 7/imm32
+  # floating-point registers
+  0x11/imm32/alloc-id $Mu-register-xmm0/imm32 8/imm32
+  0x11/imm32/alloc-id $Mu-register-xmm1/imm32 9/imm32
+  0x11/imm32/alloc-id $Mu-register-xmm2/imm32 0xa/imm32
+  0x11/imm32/alloc-id $Mu-register-xmm3/imm32 0xb/imm32
+  0x11/imm32/alloc-id $Mu-register-xmm4/imm32 0xc/imm32
+  0x11/imm32/alloc-id $Mu-register-xmm5/imm32 0xd/imm32
+  0x11/imm32/alloc-id $Mu-register-xmm6/imm32 0xe/imm32
+  0x11/imm32/alloc-id $Mu-register-xmm7/imm32 0xf/imm32
+
+$Mu-register-eax:
+  0x11/imm32/alloc-id
+  3/imm32/size
+  0x65/e 0x61/a 0x78/x
+
+$Mu-register-ecx:
+  0x11/imm32/alloc-id
+  3/imm32/size
+  0x65/e 0x63/c 0x78/x
+
+$Mu-register-edx:
+  0x11/imm32/alloc-id
+  3/imm32/size
+  0x65/e 0x64/d 0x78/x
+
+$Mu-register-ebx:
+  0x11/imm32/alloc-id
+  3/imm32/size
+  0x65/e 0x62/b 0x78/x
+
+$Mu-register-esi:
+  0x11/imm32/alloc-id
+  3/imm32/size
+  0x65/e 0x73/s 0x69/i
+
+$Mu-register-edi:
+  0x11/imm32/alloc-id
+  3/imm32/size
+  0x65/e 0x64/d 0x69/i
+
+$Mu-register-xmm0:
+  0x11/imm32/alloc-id:fake:payload
+  # "xmm0"
+  0x4/imm32/size
+  0x78/x 0x6d/m 0x6d/m 0x30/0
+
+$Mu-register-xmm1:
+  0x11/imm32/alloc-id:fake:payload
+  # "xmm1"
+  0x4/imm32/size
+  0x78/x 0x6d/m 0x6d/m 0x31/1
+
+$Mu-register-xmm2:
+  0x11/imm32/alloc-id:fake:payload
+  # "xmm2"
+  0x4/imm32/size
+  0x78/x 0x6d/m 0x6d/m 0x32/2
+
+$Mu-register-xmm3:
+  0x11/imm32/alloc-id:fake:payload
+  # "xmm3"
+  0x4/imm32/size
+  0x78/x 0x6d/m 0x6d/m 0x33/3
+
+$Mu-register-xmm4:
+  0x11/imm32/alloc-id:fake:payload
+  # "xmm4"
+  0x4/imm32/size
+  0x78/x 0x6d/m 0x6d/m 0x34/4
+
+$Mu-register-xmm5:
+  0x11/imm32/alloc-id:fake:payload
+  # "xmm5"
+  0x4/imm32/size
+  0x78/x 0x6d/m 0x6d/m 0x35/5
+
+$Mu-register-xmm6:
+  0x11/imm32/alloc-id:fake:payload
+  # "xmm6"
+  0x4/imm32/size
+  0x78/x 0x6d/m 0x6d/m 0x36/6
+
+$Mu-register-xmm7:
+  0x11/imm32/alloc-id:fake:payload
+  # "xmm7"
+  0x4/imm32/size
+  0x78/x 0x6d/m 0x6d/m 0x37/7
+
+== code
+
+# push 'out' to 'vars' if not already there; it's assumed to be a fn output
+maybe-define-var:  # out: (handle var), vars: (addr stack live-var)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    # var out-addr/eax: (addr var)
+    (lookup *(ebp+8) *(ebp+0xc))  # => eax
+    #
+    (binding-exists? %eax *(ebp+0x10))  # => eax
+    3d/compare-eax-and 0/imm32/false
+    75/jump-if-!= $maybe-define-var:end/disp8
+    # otherwise update vars
+    (push *(ebp+0x10) *(ebp+8))
+    (push *(ebp+0x10) *(ebp+0xc))
+    (push *(ebp+0x10) 0)  # 'out' is always a fn output; never spill it
+$maybe-define-var:end:
+    # . restore registers
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+# simpler version of lookup-var-helper
+binding-exists?:  # target: (addr var), vars: (addr stack live-var) -> result/eax: boolean
+    # pseudocode:
+    #   var curr: (addr handle var) = &vars->data[vars->top - 12]
+    #   var min = vars->data
+    #   while curr >= min
+    #     var v: (handle var) = *curr
+    #     if v->name == target->name
+    #       return true
+    #     curr -= 12
+    #   return false
+    #
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    51/push-ecx
+    52/push-edx
+    56/push-esi
+    # var target-name/ecx: (addr array byte) = lookup(target->name)
+    8b/-> *(ebp+8) 0/r32/eax
+    (lookup *eax *(eax+4))  # Var-name Var-name => eax
+    89/<- %ecx 0/r32/eax
+    # esi = vars
+    8b/-> *(ebp+0xc) 6/r32/esi
+    # eax = vars->top
+    8b/-> *esi 0/r32/eax
+    # var min/edx: (addr handle var) = vars->data
+    8d/copy-address *(esi+8) 2/r32/edx
+    # var curr/esi: (addr handle var) = &vars->data[vars->top - 12]
+    8d/copy-address *(esi+eax-4) 6/r32/esi  # vars + 8 + vars->type - 12
+    {
+$binding-exists?:loop:
+      # if (curr < min) return
+      39/compare %esi 2/r32/edx
+      0f 82/jump-if-addr< break/disp32
+      # var v/eax: (addr var) = lookup(*curr)
+      (lookup *esi *(esi+4))  # => eax
+      # var vn/eax: (addr array byte) = lookup(v->name)
+      (lookup *eax *(eax+4))  # Var-name Var-name => eax
+      # if (vn == target-name) return true
+      (string-equal? %ecx %eax)  # => eax
+      3d/compare-eax-and 0/imm32/false
+      75/jump-if-!= $binding-exists?:end/disp8  # eax already contains true
+      # curr -= 12
+      81 5/subop/subtract %esi 0xc/imm32
+      e9/jump loop/disp32
+    }
+    b8/copy-to-eax 0/imm32/false
+$binding-exists?:end:
+    # . restore registers
+    5e/pop-to-esi
+    5a/pop-to-edx
+    59/pop-to-ecx
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-parse-mu-stmt:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    8b/-> *Primitive-type-ids 0/r32/eax
+    89/<- *Type-id 0/r32/eax  # stream-write
+    (clear-stream _test-input-stream)
+    (write _test-input-stream "increment n\n")
+    # var vars/ecx: (stack (addr var) 16)
+    81 5/subop/subtract %esp 0xc0/imm32
+    68/push 0xc0/imm32/size
+    68/push 0/imm32/top
+    89/<- %ecx 4/r32/esp
+    (clear-stack %ecx)
+    # var v/edx: (handle var)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    # var s/eax: (handle array byte)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %eax 4/r32/esp
+    # v = new var("n")
+    (copy-array Heap "n" %eax)
+    (new-var Heap *eax *(eax+4) %edx)
+    #
+    (push %ecx *edx)
+    (push %ecx *(edx+4))
+    (push %ecx 0)
+    # var out/eax: (handle stmt)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %eax 4/r32/esp
+    # convert
+    (parse-mu-stmt _test-input-stream %ecx 0 %eax Stderr 0)
+    # var out-addr/edx: (addr stmt) = lookup(*out)
+    (lookup *eax *(eax+4))  # => eax
+    89/<- %edx 0/r32/eax
+    # out->tag
+    (check-ints-equal *edx 1 "F - test-parse-mu-stmt/tag")  # Stmt-tag is Stmt1
+    # out->operation
+    (lookup *(edx+4) *(edx+8))  # Stmt1-operation Stmt1-operation => eax
+    (check-strings-equal %eax "increment" "F - test-parse-mu-stmt/name")  # Stmt1-operation
+    # out->inouts->value->name
+    # . eax = out->inouts
+    (lookup *(edx+0xc) *(edx+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+    # . eax = out->inouts->value
+    (lookup *eax *(eax+4))  # Stmt-var-value Stmt-var-value => eax
+    # . eax = out->inouts->value->name
+    (lookup *eax *(eax+4))  # Var-name Var-name => eax
+    # .
+    (check-strings-equal %eax "n" "F - test-parse-mu-stmt/inout:0")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-parse-mu-stmt-with-comma:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    8b/-> *Primitive-type-ids 0/r32/eax
+    89/<- *Type-id 0/r32/eax  # stream-write
+    (clear-stream _test-input-stream)
+    (write _test-input-stream "copy-to n, 3\n")
+    # var vars/ecx: (stack (addr var) 16)
+    81 5/subop/subtract %esp 0xc0/imm32
+    68/push 0xc0/imm32/size
+    68/push 0/imm32/top
+    89/<- %ecx 4/r32/esp
+    (clear-stack %ecx)
+    # var v/edx: (handle var)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edx 4/r32/esp
+    # var s/eax: (handle array byte)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %eax 4/r32/esp
+    # v = new var("n")
+    (copy-array Heap "n" %eax)
+    (new-var Heap *eax *(eax+4) %edx)
+    #
+    (push %ecx *edx)
+    (push %ecx *(edx+4))
+    (push %ecx 0)
+    # var out/eax: (handle stmt)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %eax 4/r32/esp
+    # convert
+    (parse-mu-stmt _test-input-stream %ecx 0 %eax Stderr 0)
+    # var out-addr/edx: (addr stmt) = lookup(*out)
+    (lookup *eax *(eax+4))  # => eax
+    89/<- %edx 0/r32/eax
+    # out->tag
+    (check-ints-equal *edx 1 "F - test-parse-mu-stmt-with-comma/tag")  # Stmt-tag is Stmt1
+    # out->operation
+    (lookup *(edx+4) *(edx+8))  # Stmt1-operation Stmt1-operation => eax
+    (check-strings-equal %eax "copy-to" "F - test-parse-mu-stmt-with-comma/name")  # Stmt1-operation
+    # out->inouts->value->name
+    # . eax = out->inouts
+    (lookup *(edx+0xc) *(edx+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+    # . eax = out->inouts->value
+    (lookup *eax *(eax+4))  # Stmt-var-value Stmt-var-value => eax
+    # . eax = out->inouts->value->name
+    (lookup *eax *(eax+4))  # Var-name Var-name => eax
+    # .
+    (check-strings-equal %eax "n" "F - test-parse-mu-stmt-with-comma/inout:0")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+new-var:  # ad: (addr allocation-descriptor), name: (handle array byte), out: (addr handle var)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    # ecx = out
+    8b/-> *(ebp+0x14) 1/r32/ecx
+    #
+    (allocate *(ebp+8) *Var-size %ecx)
+    # var out-addr/eax: (addr var)
+    (lookup *ecx *(ecx+4))  # => eax
+    # out-addr->name = name
+    8b/-> *(ebp+0xc) 1/r32/ecx
+    89/<- *eax 1/r32/ecx  # Var-name
+    8b/-> *(ebp+0x10) 1/r32/ecx
+    89/<- *(eax+4) 1/r32/ecx  # Var-name
+#?     (write-buffered Stderr "var ")
+#?     (lookup *(ebp+0xc) *(ebp+0x10))
+#?     (write-buffered Stderr %eax)
+#?     (write-buffered Stderr " at ")
+#?     8b/-> *(ebp+0x14) 1/r32/ecx
+#?     (lookup *ecx *(ecx+4))  # => eax
+#?     (write-int32-hex-buffered Stderr %eax)
+#?     (write-buffered Stderr Newline)
+#?     (flush Stderr)
+$new-var:end:
+    # . restore registers
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+# WARNING: modifies name
+new-literal-integer:  # ad: (addr allocation-descriptor), name: (addr slice), out: (addr handle var), fn: (addr function), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    # first strip out metadata
+    8b/-> *(ebp+0xc) 1/r32/ecx
+    (next-token-from-slice *ecx *(ecx+4) 0x2f *(ebp+0xc))
+    # if (!is-hex-int?(name)) abort
+    (is-hex-int? *(ebp+0xc))  # => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 84/jump-if-= $new-literal-integer:abort/disp32
+    # a little more error-checking
+    (check-mu-hex-int *(ebp+0xc) *(ebp+0x18) *(ebp+0x1c))
+    # out = new var(s)
+    (new-var-from-slice *(ebp+8) *(ebp+0xc) *(ebp+0x10))
+    # var out-addr/ecx: (addr var) = lookup(*out)
+    8b/-> *(ebp+0x10) 0/r32/eax
+    (lookup *eax *(eax+4))  # => eax
+    89/<- %ecx 0/r32/eax
+    # out-addr->block-depth = *Curr-block-depth
+    8b/-> *Curr-block-depth 0/r32/eax
+    89/<- *(ecx+0x10) 0/r32/eax  # Var-block-depth
+    # out-addr->type = new tree()
+    8d/copy-address *(ecx+8) 0/r32/eax  # Var-type
+    (allocate *(ebp+8) *Type-tree-size %eax)
+    (lookup *(ecx+8) *(ecx+0xc))  # Var-type Var-type => eax
+    c7 0/subop/copy *eax 1/imm32/true  # Type-tree-is-atom
+    # nothing else to do; default type is 'literal'
+$new-literal-integer:end:
+    # . reclaim locals
+    81 0/subop/add %esp 8/imm32
+    # . restore registers
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+$new-literal-integer:abort:
+    (write-buffered *(ebp+0x18) "fn ")
+    8b/-> *(ebp+0x14) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x18) %eax)
+    (write-buffered *(ebp+0x18) ": variable '")
+    (write-slice-buffered *(ebp+0x18) *(ebp+0xc))
+    (write-buffered *(ebp+0x18) "' cannot begin with a digit (or do you have a typo in a number?)\n")
+    (flush *(ebp+0x18))
+    (stop *(ebp+0x1c) 1)
+    # never gets here
+
+# precondition: name is a valid hex integer; require a '0x' prefix
+check-mu-hex-int:  # name: (addr slice), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    52/push-edx
+    # ecx = name
+    8b/-> *(ebp+8) 1/r32/ecx
+    # var start/edx: (addr byte) = name->start
+    8b/-> *ecx 2/r32/edx
+    # if (*start == '-') ++start
+    b8/copy-to-eax 0/imm32
+    8a/copy-byte *edx 0/r32/AL
+    3d/compare-eax-and 0x2d/imm32/dash
+    {
+      75/jump-if-!= break/disp8
+      42/increment-edx
+    }
+    # var end/ecx: (addr byte) = name->end
+    8b/-> *(ecx+4) 1/r32/ecx
+    # var len/eax: int = name->end - name->start
+    89/<- %eax 1/r32/ecx
+    29/subtract-from %eax 2/r32/edx
+    # if (len <= 1) return
+    3d/compare-eax-with 1/imm32
+    0f 8e/jump-if-<= $check-mu-hex-int:end/disp32
+$check-mu-hex-int:length->-1:
+    # if slice-starts-with?({start, end}, "0x") return
+    # . var tmp = {start, end}
+    51/push-ecx
+    52/push-edx
+    89/<- %eax 4/r32/esp
+    # .
+    (slice-starts-with? %eax "0x")  # => eax
+    # . reclaim tmp
+    81 0/subop/add %esp 8/imm32
+    # .
+    3d/compare-eax-with 0/imm32/false
+    75/jump-if-!= $check-mu-hex-int:end/disp8
+$check-mu-hex-int:abort:
+    # otherwise abort
+    (write-buffered *(ebp+0xc) "literal integers are always hex in Mu; start '")
+    (write-slice-buffered *(ebp+0xc) *(ebp+8))
+    (write-buffered *(ebp+0xc) "' with a '0x' to be unambiguous, converting it to hexadecimal as necessary.\n")
+    (flush *(ebp+0xc))
+    (stop *(ebp+0x10) 1)
+$check-mu-hex-int:end:
+    # . restore registers
+    5a/pop-to-edx
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+new-literal:  # ad: (addr allocation-descriptor), name: (addr slice), out: (addr handle var)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    # var s/ecx: (handle array byte)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %ecx 4/r32/esp
+    # s = slice-to-string(name)
+    (slice-to-string Heap *(ebp+0xc) %ecx)
+    # allocate to out
+    (new-var *(ebp+8) *ecx *(ecx+4) *(ebp+0x10))
+    # var out-addr/ecx: (addr var) = lookup(*out)
+    8b/-> *(ebp+0x10) 1/r32/ecx
+    (lookup *ecx *(ecx+4))  # => eax
+    89/<- %ecx 0/r32/eax
+    # out-addr->block-depth = *Curr-block-depth
+    8b/-> *Curr-block-depth 0/r32/eax
+    89/<- *(ecx+0x10) 0/r32/eax  # Var-block-depth
+    # out-addr->type/eax = new type
+    8d/copy-address *(ecx+8) 0/r32/eax  # Var-type
+    (allocate *(ebp+8) *Type-tree-size %eax)
+    (lookup *(ecx+8) *(ecx+0xc))  # Var-type Var-type => eax
+    # nothing else to do; default type is 'literal'
+    c7 0/subop/copy *eax 1/imm32/true  # Type-tree-is-atom
+$new-literal:end:
+    # . reclaim locals
+    81 0/subop/add %esp 8/imm32
+    # . restore registers
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+new-literal-string:  # ad: (addr allocation-descriptor), name: (addr slice), out: (addr handle var)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    # var s/ecx: (handle array byte)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %ecx 4/r32/esp
+    # s = slice-to-string(name)
+    (slice-to-string Heap *(ebp+0xc) %ecx)
+    # allocate to out
+    (new-var *(ebp+8) *ecx *(ecx+4) *(ebp+0x10))
+    # var out-addr/ecx: (addr var) = lookup(*out)
+    8b/-> *(ebp+0x10) 1/r32/ecx
+    (lookup *ecx *(ecx+4))  # => eax
+    89/<- %ecx 0/r32/eax
+    # out-addr->block-depth = *Curr-block-depth
+    8b/-> *Curr-block-depth 0/r32/eax
+    89/<- *(ecx+0x10) 0/r32/eax  # Var-block-depth
+    # out-addr->type/eax = new type
+    8d/copy-address *(ecx+8) 0/r32/eax  # Var-type
+    (allocate *(ebp+8) *Type-tree-size %eax)
+    (lookup *(ecx+8) *(ecx+0xc))  # Var-type Var-type => eax
+    # out-addr->type->value = literal-string
+    c7 0/subop/copy *(eax+4) 0x10/imm32/type-id-string-literal  # Type-tree-value
+    # out-addr->type->is-atom? = true
+    c7 0/subop/copy *eax 1/imm32/true  # Type-tree-is-atom
+$new-literal-string:end:
+    # . reclaim locals
+    81 0/subop/add %esp 8/imm32
+    # . restore registers
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+new-var-from-slice:  # ad: (addr allocation-descriptor), name: (addr slice), out: (addr handle var)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    51/push-ecx
+    # var tmp/ecx: (handle array byte)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %ecx 4/r32/esp
+    # tmp = slice-to-string(name)
+    (slice-to-string Heap *(ebp+0xc) %ecx)
+    # out = new-var(tmp)
+    (new-var *(ebp+8) *ecx *(ecx+4) *(ebp+0x10))
+$new-var-from-slice:end:
+    # . reclaim locals
+    81 0/subop/add %esp 8/imm32
+    # . restore registers
+    59/pop-to-ecx
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+new-var-def:  # ad: (addr allocation-descriptor), var: (handle var), out: (addr handle stmt)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    #
+    (allocate *(ebp+8) *Stmt-size *(ebp+0x14))
+    # var out-addr/eax: (addr stmt) = lookup(*out)
+    8b/-> *(ebp+0x14) 0/r32/eax
+    (lookup *eax *(eax+4))  # => eax
+    # out-addr->tag = stmt
+    c7 0/subop/copy *eax 2/imm32/tag/var-on-stack  # Stmt-tag
+    # result->var = var
+    8b/-> *(ebp+0xc) 1/r32/ecx
+    89/<- *(eax+4) 1/r32/ecx  # Vardef-var
+    8b/-> *(ebp+0x10) 1/r32/ecx
+    89/<- *(eax+8) 1/r32/ecx  # Vardef-var
+$new-var-def:end:
+    # . restore registers
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+new-reg-var-def:  # ad: (addr allocation-descriptor), var: (handle var), out: (addr handle stmt)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    # eax = out
+    8b/-> *(ebp+0x14) 0/r32/eax
+    #
+    (allocate *(ebp+8) *Stmt-size %eax)
+    # var out-addr/eax: (addr stmt) = lookup(*out)
+    (lookup *eax *(eax+4))  # => eax
+    # set tag
+    c7 0/subop/copy *eax 3/imm32/tag/var-in-register  # Stmt-tag
+    # set output
+    8d/copy-address *(eax+0x14) 0/r32/eax  # Regvardef-outputs
+    (append-stmt-var Heap  *(ebp+0xc) *(ebp+0x10)  0 0  0  %eax)
+$new-reg-var-def:end:
+    # . restore registers
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+append-list:  # ad: (addr allocation-descriptor), value: (handle _type), list: (handle list _type), out: (addr handle list _type)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    57/push-edi
+    # edi = out
+    8b/-> *(ebp+0x1c) 7/r32/edi
+    # *out = new list
+    (allocate *(ebp+8) *List-size %edi)
+    # var out-addr/edi: (addr list _type) = lookup(*out)
+    (lookup *edi *(edi+4))  # => eax
+    89/<- %edi 0/r32/eax
+    # out-addr->value = value
+    8b/-> *(ebp+0xc) 0/r32/eax
+    89/<- *edi 0/r32/eax  # List-value
+    8b/-> *(ebp+0x10) 0/r32/eax
+    89/<- *(edi+4) 0/r32/eax  # List-value
+    # if (list == null) return
+    81 7/subop/compare *(ebp+0x14) 0/imm32
+    74/jump-if-= $append-list:end/disp8
+    # otherwise append
+$append-list:non-empty-list:
+    # var curr/eax: (addr list _type) = lookup(list)
+    (lookup *(ebp+0x14) *(ebp+0x18))  # => eax
+    # while (curr->next != null) curr = curr->next
+    {
+      81 7/subop/compare *(eax+8) 0/imm32  # List-next
+      74/jump-if-= break/disp8
+      # curr = lookup(curr->next)
+      (lookup *(eax+8) *(eax+0xc))  # List-next, List-next => eax
+      #
+      eb/jump loop/disp8
+    }
+    # edi = out
+    8b/-> *(ebp+0x1c) 7/r32/edi
+    # curr->next = out
+    8b/-> *edi 1/r32/ecx
+    89/<- *(eax+8) 1/r32/ecx  # List-next
+    8b/-> *(edi+4) 1/r32/ecx
+    89/<- *(eax+0xc) 1/r32/ecx  # List-next
+    # out = list
+    8b/-> *(ebp+0x14) 1/r32/ecx
+    89/<- *edi 1/r32/ecx
+    8b/-> *(ebp+0x18) 1/r32/ecx
+    89/<- *(edi+4) 1/r32/ecx
+$append-list:end:
+    # . restore registers
+    5f/pop-to-edi
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+append-stmt-var:  # ad: (addr allocation-descriptor), v: (handle var), vars: (handle stmt-var), is-deref?: boolean, out: (addr handle stmt-var)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    57/push-edi
+    # edi = out
+    8b/-> *(ebp+0x20) 7/r32/edi
+    # out = new stmt-var
+    (allocate *(ebp+8) *Stmt-var-size %edi)
+    # var out-addr/ecx: (addr stmt-var) = lookup(*out)
+    (lookup *edi *(edi+4))  # => eax
+    89/<- %ecx 0/r32/eax
+    # out-addr->value = v
+    8b/-> *(ebp+0xc) 0/r32/eax
+    89/<- *ecx 0/r32/eax  # Stmt-var-value
+    8b/-> *(ebp+0x10) 0/r32/eax
+    89/<- *(ecx+4) 0/r32/eax  # Stmt-var-value
+    # out-addr->is-deref? = is-deref?
+    8b/-> *(ebp+0x1c) 0/r32/eax
+    89/<- *(ecx+0x10) 0/r32/eax  # Stmt-var-is-deref
+    # if (vars == null) return result
+    81 7/subop/compare *(ebp+0x14) 0/imm32/null
+    74/jump-if-= $append-stmt-var:end/disp8
+    # otherwise append
+    # var curr/eax: (addr stmt-var) = lookup(vars)
+    (lookup *(ebp+0x14) *(ebp+0x18))  # => eax
+    # while (curr->next != null) curr = curr->next
+    {
+      81 7/subop/compare *(eax+8) 0/imm32  # Stmt-var-next
+      74/jump-if-= break/disp8
+      # curr = lookup(curr->next)
+      (lookup *(eax+8) *(eax+0xc))  # Stmt-var-next, Stmt-var-next => eax
+      #
+      eb/jump loop/disp8
+    }
+    # curr->next = out
+    8b/-> *edi 1/r32/ecx
+    89/<- *(eax+8) 1/r32/ecx  # Stmt-var-next
+    8b/-> *(edi+4) 1/r32/ecx
+    89/<- *(eax+0xc) 1/r32/ecx  # Stmt-var-next
+    # out = vars
+    8b/-> *(ebp+0x14) 1/r32/ecx
+    89/<- *edi 1/r32/ecx
+    8b/-> *(ebp+0x18) 1/r32/ecx
+    89/<- *(edi+4) 1/r32/ecx
+$append-stmt-var:end:
+    # . restore registers
+    5f/pop-to-edi
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+append-to-block:  # ad: (addr allocation-descriptor), block: (addr block), x: (handle stmt)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    56/push-esi
+    # esi = block
+    8b/-> *(ebp+0xc) 6/r32/esi
+    # block->stmts = append(x, block->stmts)
+    8d/copy-address *(esi+4) 0/r32/eax  # Block-stmts
+    (append-list *(ebp+8)  *(ebp+0x10) *(ebp+0x14)  *(esi+4) *(esi+8)  %eax)  # ad, x, x, Block-stmts, Block-stmts
+$append-to-block:end:
+    # . restore registers
+    5e/pop-to-esi
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+## Parsing types
+# We need to create metadata on user-defined types, and we need to use this
+# metadata as we parse instructions.
+# However, we also want to allow types to be used before their definitions.
+# This means we can't ever assume any type data structures exist.
+
+lookup-or-create-constant:  # container: (addr stmt-var), field-name: (addr slice), out: (addr handle var)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    56/push-esi
+    # var container-type/esi: type-id
+    (container-type *(ebp+8))  # => eax
+    89/<- %esi 0/r32/eax
+    # var tmp/eax: (handle typeinfo) = find-or-create-typeinfo(container-type)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %eax 4/r32/esp
+    (find-or-create-typeinfo %esi %eax)
+    # var tmp-addr/eax: (addr typeinfo) = lookup(tmp)
+    (lookup *eax *(eax+4))  # => eax
+    # result = find-or-create-typeinfo-output-var(typeinfo, field-name)
+#?     (write-buffered Stderr "constant: ")
+#?     (write-slice-buffered Stderr *(ebp+0xc))
+#?     (write-buffered Stderr Newline)
+#?     (flush Stderr)
+    (find-or-create-typeinfo-output-var %eax *(ebp+0xc) *(ebp+0x10))
+#?     8b/-> *(ebp+0x10) 0/r32/eax
+#?     (write-buffered Stderr "@")
+#?     (lookup *eax *(eax+4))
+#?     (write-int32-hex-buffered Stderr %eax)
+#?     (lookup *eax *(eax+4))
+#?     (write-buffered Stderr %eax)
+#?     (write-buffered Stderr Newline)
+#?     (flush Stderr)
+#?     (write-buffered Stderr "offset: ")
+#?     8b/-> *(eax+0x14) 0/r32/eax
+#?     (write-int32-hex-buffered Stderr %eax)
+#?     (write-buffered Stderr Newline)
+#?     (flush Stderr)
+$lookup-or-create-constant:end:
+    # . reclaim locals
+    81 0/subop/add %esp 8/imm32
+    # . restore registers
+    5e/pop-to-esi
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+# if addr var:
+#   container->var->type->right->left->value
+# otherwise
+#   container->var->type->value
+container-type:  # container: (addr stmt-var) -> result/eax: type-id
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    #
+    8b/-> *(ebp+8) 0/r32/eax
+    (lookup *eax *(eax+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *(eax+8) *(eax+0xc))  # Var-type Var-type => eax
+    {
+      81 7/subop/compare *(eax+8) 0/imm32  # Type-tree-right
+      74/jump-if-= break/disp8
+      (lookup *(eax+0xc) *(eax+0x10))  # Type-tree-right Type-tree-right => eax
+      (lookup *(eax+4) *(eax+8))  # Type-tree-left Type-tree-left => eax
+    }
+    8b/-> *(eax+4) 0/r32/eax  # Type-tree-value
+$container-type:end:
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+is-container?:  # t: type-id -> result/eax: boolean
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    #
+    8b/-> *(ebp+8) 0/r32/eax
+    c1/shift 4/subop/left %eax 2/imm8
+    3b/compare 0/r32/eax *Primitive-type-ids
+    0f 9d/set-if->= %al
+    81 4/subop/and %eax 0xff/imm32
+$is-container?:end:
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+find-or-create-typeinfo:  # t: type-id, out: (addr handle typeinfo)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    52/push-edx
+    57/push-edi
+    # edi = out
+    8b/-> *(ebp+0xc) 7/r32/edi
+    # var fields/ecx: (handle table (handle array byte) (handle typeinfo-entry))
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %ecx 4/r32/esp
+    # find-typeinfo(t, out)
+    (find-typeinfo *(ebp+8) %edi)
+    {
+      # if (*out != 0) break
+      81 7/subop/compare *edi 0/imm32
+      0f 85/jump-if-!= break/disp32
+$find-or-create-typeinfo:create:
+      # *out = allocate
+      (allocate Heap *Typeinfo-size %edi)
+      # var tmp/eax: (addr typeinfo) = lookup(*out)
+      (lookup *edi *(edi+4))  # => eax
+#?     (write-buffered Stderr "created typeinfo at ")
+#?     (write-int32-hex-buffered Stderr %eax)
+#?     (write-buffered Stderr " for type-id ")
+#?     (write-int32-hex-buffered Stderr *(ebp+8))
+#?     (write-buffered Stderr Newline)
+#?     (flush Stderr)
+      # tmp->id = t
+      8b/-> *(ebp+8) 2/r32/edx
+      89/<- *eax 2/r32/edx  # Typeinfo-id
+      # tmp->fields = new table
+      # . fields = new table
+      (new-stream Heap 0x40 *Typeinfo-fields-row-size %ecx)
+      # . tmp->fields = fields
+      8b/-> *ecx 2/r32/edx
+      89/<- *(eax+4) 2/r32/edx  # Typeinfo-fields
+      8b/-> *(ecx+4) 2/r32/edx
+      89/<- *(eax+8) 2/r32/edx  # Typeinfo-fields
+      # tmp->next = Program->types
+      8b/-> *_Program-types 1/r32/ecx
+      89/<- *(eax+0x10) 1/r32/ecx  # Typeinfo-next
+      8b/-> *_Program-types->payload 1/r32/ecx
+      89/<- *(eax+0x14) 1/r32/ecx  # Typeinfo-next
+      # Program->types = out
+      8b/-> *edi 1/r32/ecx
+      89/<- *_Program-types 1/r32/ecx
+      8b/-> *(edi+4) 1/r32/ecx
+      89/<- *_Program-types->payload 1/r32/ecx
+    }
+$find-or-create-typeinfo:end:
+    # . reclaim locals
+    81 0/subop/add %esp 8/imm32
+    # . restore registers
+    5f/pop-to-edi
+    5a/pop-to-edx
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+find-typeinfo:  # t: type-id, out: (addr handle typeinfo)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    52/push-edx
+    57/push-edi
+    # ecx = t
+    8b/-> *(ebp+8) 1/r32/ecx
+    # edi = out
+    8b/-> *(ebp+0xc) 7/r32/edi
+    # *out = Program->types
+    8b/-> *_Program-types 0/r32/eax
+    89/<- *edi 0/r32/eax
+    8b/-> *_Program-types->payload 0/r32/eax
+    89/<- *(edi+4) 0/r32/eax
+    {
+$find-typeinfo:loop:
+      # if (*out == 0) break
+      81 7/subop/compare *edi 0/imm32
+      74/jump-if-= break/disp8
+$find-typeinfo:check:
+      # var tmp/eax: (addr typeinfo) = lookup(*out)
+      (lookup *edi *(edi+4))  # => eax
+      # if (tmp->id == t) break
+      39/compare *eax 1/r32/ecx  # Typeinfo-id
+      74/jump-if-= break/disp8
+$find-typeinfo:continue:
+      # *out = tmp->next
+      8b/-> *(eax+0x10) 2/r32/edx  # Typeinfo-next
+      89/<- *edi 2/r32/edx
+      8b/-> *(eax+0x14) 2/r32/edx  # Typeinfo-next
+      89/<- *(edi+4) 2/r32/edx
+      #
+      eb/jump loop/disp8
+    }
+$find-typeinfo:end:
+    # . restore registers
+    5f/pop-to-edi
+    5a/pop-to-edx
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+find-or-create-typeinfo-output-var:  # T: (addr typeinfo), f: (addr slice), out: (addr handle var)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    52/push-edx
+    57/push-edi
+    # var dest/edi: (handle typeinfo-entry)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %edi 4/r32/esp
+    # find-or-create-typeinfo-fields(T, f, dest)
+    (find-or-create-typeinfo-fields *(ebp+8) *(ebp+0xc) %edi)
+    # var dest-addr/edi: (addr typeinfo-entry) = lookup(dest)
+    (lookup *edi *(edi+4))  # => eax
+    89/<- %edi 0/r32/eax
+    # if dest-addr->output-var doesn't exist, create it
+    {
+      81 7/subop/compare *(edi+0xc) 0/imm32  # Typeinfo-entry-output-var
+      0f 85/jump-if-!= break/disp32
+      # dest-addr->output-var = new var(dummy name, type, -1 offset)
+      # . var name/eax: (handle array byte) = "field"
+      68/push 0/imm32
+      68/push 0/imm32
+      89/<- %eax 4/r32/esp
+      (slice-to-string Heap *(ebp+0xc) %eax)
+      # . new var
+      8d/copy-address *(edi+0xc) 2/r32/edx
+      (new-var Heap  *eax *(eax+4)  %edx)
+      # . reclaim name
+      81 0/subop/add %esp 8/imm32
+      # var result/edx: (addr var) = lookup(dest-addr->output-var)
+      (lookup *(edi+0xc) *(edi+0x10))  # => eax
+      89/<- %edx 0/r32/eax
+      # result->type = new constant type
+      8d/copy-address *(edx+8) 0/r32/eax  # Var-type
+      (allocate Heap *Type-tree-size %eax)
+      (lookup *(edx+8) *(edx+0xc))  # => eax
+      c7 0/subop/copy *eax 1/imm32/true  # Type-tree-is-atom
+      c7 0/subop/copy *(eax+4) 6/imm32/constant  # Type-tree-value
+      c7 0/subop/copy *(eax+8) 0/imm32  # Type-tree-left
+      c7 0/subop/copy *(eax+0xc) 0/imm32  # Type-tree-right
+      c7 0/subop/copy *(eax+0x10) 0/imm32  # Type-tree-right
+      # result->offset isn't filled out yet
+      c7 0/subop/copy *(edx+0x14) -1/imm32/uninitialized  # Var-offset
+    }
+    # out = dest-addr->output-var
+    8b/-> *(ebp+0x10) 2/r32/edx
+    8b/-> *(edi+0xc) 0/r32/eax  # Typeinfo-entry-output-var
+    89/<- *edx 0/r32/eax
+    8b/-> *(edi+0x10) 0/r32/eax  # Typeinfo-entry-output-var
+    89/<- *(edx+4) 0/r32/eax
+$find-or-create-typeinfo-output-var:end:
+    # . reclaim locals
+    81 0/subop/add %esp 8/imm32
+    # . restore registers
+    5f/pop-to-edi
+    5a/pop-to-edx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+find-or-create-typeinfo-fields:  # T: (addr typeinfo), f: (addr slice), out: (addr handle typeinfo-entry)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    56/push-esi
+    57/push-edi
+    # eax = lookup(T->fields)
+    8b/-> *(ebp+8) 0/r32/eax
+    (lookup *(eax+4) *(eax+8))  # Typeinfo-fields Typeinfo-fields => eax
+    # edi = out
+    8b/-> *(ebp+0x10) 7/r32/edi
+    # var src/esi: (addr handle typeinfo-entry) = get-or-insert-slice(T->fields, f)
+    (get-or-insert-slice %eax *(ebp+0xc) *Typeinfo-fields-row-size Heap)  # => eax
+    89/<- %esi 0/r32/eax
+    # if src doesn't exist, allocate it
+    {
+      81 7/subop/compare *esi 0/imm32
+      75/jump-if-!= break/disp8
+      (allocate Heap *Typeinfo-entry-size %esi)
+#?       (write-buffered Stderr "handle at ")
+#?       (write-int32-hex-buffered Stderr %esi)
+#?       (write-buffered Stderr ": ")
+#?       (write-int32-hex-buffered Stderr *esi)
+#?       (write-buffered Stderr " ")
+#?       (write-int32-hex-buffered Stderr *(esi+4))
+#?       (write-buffered Stderr Newline)
+#?       (flush Stderr)
+#?       (lookup *esi *(esi+4))
+#?       (write-buffered Stderr "created typeinfo fields at ")
+#?       (write-int32-hex-buffered Stderr %esi)
+#?       (write-buffered Stderr " for ")
+#?       (write-int32-hex-buffered Stderr *(ebp+8))
+#?       (write-buffered Stderr Newline)
+#?       (flush Stderr)
+    }
+    # *out = src
+    # . *edi = *src
+    8b/-> *esi 0/r32/eax
+    89/<- *edi 0/r32/eax
+    8b/-> *(esi+4) 0/r32/eax
+    89/<- *(edi+4) 0/r32/eax
+$find-or-create-typeinfo-fields:end:
+    # . restore registers
+    5f/pop-to-edi
+    5e/pop-to-esi
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+populate-mu-type:  # in: (addr stream byte), t: (addr typeinfo), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # pseudocode:
+    #   var line: (stream byte 512)
+    #   curr-index = 0
+    #   while true
+    #     clear-stream(line)
+    #     read-line-buffered(in, line)
+    #     if line->write == 0
+    #       abort
+    #     word-slice = next-mu-token(line)
+    #     if slice-empty?(word-slice)               # end of line
+    #       continue
+    #     if slice-equal?(word-slice, "}")
+    #       break
+    #     var v: (handle var) = parse-var-with-type(word-slice, line)
+    #     var r: (handle typeinfo-fields) = find-or-create-typeinfo-fields(t, word-slice/v->name)
+    #     TODO: ensure that r->first is null
+    #     r->index = curr-index
+    #     curr-index++
+    #     r->input-var = v
+    #     if r->output-var == 0
+    #       r->output-var = new literal
+    #     TODO: ensure nothing else in line
+    # t->total-size-in-bytes = -2 (not yet initialized)
+    #
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # var curr-index: int at *(ebp-4)
+    68/push 0/imm32
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    52/push-edx
+    53/push-ebx
+    56/push-esi
+    57/push-edi
+    # edi = t
+    8b/-> *(ebp+0xc) 7/r32/edi
+    # var line/ecx: (stream byte 512)
+    81 5/subop/subtract %esp 0x200/imm32
+    68/push 0x200/imm32/size
+    68/push 0/imm32/read
+    68/push 0/imm32/write
+    89/<- %ecx 4/r32/esp
+    # var word-slice/edx: slice
+    68/push 0/imm32/end
+    68/push 0/imm32/start
+    89/<- %edx 4/r32/esp
+    # var v/esi: (handle var)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %esi 4/r32/esp
+    # var r/ebx: (handle typeinfo-entry)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %ebx 4/r32/esp
+    {
+$populate-mu-type:line-loop:
+      (clear-stream %ecx)
+      (read-line-buffered *(ebp+8) %ecx)
+      # if (line->write == 0) abort
+      81 7/subop/compare *ecx 0/imm32
+      0f 84/jump-if-= $populate-mu-type:error1/disp32
+#?       # dump line {{{
+#?       (write 2 "parse-mu: ^")
+#?       (write-stream 2 %ecx)
+#?       (write 2 "$\n")
+#?       (rewind-stream %ecx)
+#?       # }}}
+      (next-mu-token %ecx %edx)
+      # if slice-empty?(word-slice) continue
+      (slice-empty? %edx)  # => eax
+      3d/compare-eax-and 0/imm32
+      0f 85/jump-if-!= loop/disp32
+      # if slice-equal?(word-slice, "}") break
+      (slice-equal? %edx "}")
+      3d/compare-eax-and 0/imm32
+      0f 85/jump-if-!= break/disp32
+$populate-mu-type:parse-element:
+      # v = parse-var-with-type(word-slice, first-line)
+      # must do this first to strip the trailing ':' from word-slice before
+      # using it in find-or-create-typeinfo-fields below
+      # TODO: clean up that mutation in parse-var-with-type
+      (type-name *edi)  # Typeinfo-id => eax
+      (parse-var-with-type %edx %ecx %esi %eax *(ebp+0x10) *(ebp+0x14))
+      # if v is an addr, abort
+      (lookup *esi *(esi+4))  # => eax
+      (lookup *(eax+8) *(eax+0xc))  # Var-type Var-type => eax
+      (is-mu-addr-type? %eax)  # => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 85/jump-if-!= $populate-mu-type:error2/disp32
+      # if v is an array, abort  (we could support it, but initialization gets complex)
+      (lookup *esi *(esi+4))  # => eax
+      (lookup *(eax+8) *(eax+0xc))  # Var-type Var-type => eax
+      (is-mu-array-type? %eax)  # => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 85/jump-if-!= $populate-mu-type:error3/disp32
+      # if v is a byte, abort
+      (lookup *esi *(esi+4))  # => eax
+      (lookup *(eax+8) *(eax+0xc))  # Var-type Var-type => eax
+      (is-simple-mu-type? %eax 8)  # byte => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 85/jump-if-!= $populate-mu-type:error4/disp32
+      # if v is a slice, abort
+      (lookup *esi *(esi+4))  # => eax
+      (lookup *(eax+8) *(eax+0xc))  # Var-type Var-type => eax
+      (is-simple-mu-type? %eax 0xc)  # slice => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 85/jump-if-!= $populate-mu-type:error5/disp32
+      # if v is a stream, abort  (we could support it, but initialization gets even more complex)
+      (lookup *esi *(esi+4))  # => eax
+      (lookup *(eax+8) *(eax+0xc))  # Var-type Var-type => eax
+      (is-mu-stream-type? %eax)  # => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 85/jump-if-!= $populate-mu-type:error6/disp32
+      # var tmp/ecx
+      51/push-ecx
+$populate-mu-type:create-typeinfo-fields:
+      # var r/ebx: (handle typeinfo-entry)
+      (find-or-create-typeinfo-fields %edi %edx %ebx)
+      # r->index = curr-index
+      (lookup *ebx *(ebx+4))  # => eax
+      8b/-> *(ebp-4) 1/r32/ecx
+#?       (write-buffered Stderr "saving index ")
+#?       (write-int32-hex-buffered Stderr %ecx)
+#?       (write-buffered Stderr " at ")
+#?       (write-int32-hex-buffered Stderr %edi)
+#?       (write-buffered Stderr Newline)
+#?       (flush Stderr)
+      89/<- *(eax+8) 1/r32/ecx  # Typeinfo-entry-index
+      # ++curr-index
+      ff 0/subop/increment *(ebp-4)
+$populate-mu-type:set-input-type:
+      # r->input-var = v
+      8b/-> *esi 1/r32/ecx
+      89/<- *eax 1/r32/ecx  # Typeinfo-entry-input-var
+      8b/-> *(esi+4) 1/r32/ecx
+      89/<- *(eax+4) 1/r32/ecx  # Typeinfo-entry-input-var
+      # restore line
+      59/pop-to-ecx
+      {
+$populate-mu-type:create-output-type:
+        # if (r->output-var == 0) create a new var with some placeholder data
+        81 7/subop/compare *(eax+0xc) 0/imm32  # Typeinfo-entry-output-var
+        75/jump-if-!= break/disp8
+        8d/copy-address *(eax+0xc) 0/r32/eax  # Typeinfo-entry-output-var
+        (new-literal Heap %edx %eax)
+      }
+      e9/jump loop/disp32
+    }
+$populate-mu-type:invalidate-total-size-in-bytes:
+    # Offsets and total size may not be accurate here since we may not yet
+    # have encountered the element types.
+    # We'll recompute them separately after parsing the entire program.
+    c7 0/subop/copy *(edi+0xc) -2/imm32/uninitialized  # Typeinfo-total-size-in-bytes
+$populate-mu-type:end:
+    # . reclaim locals
+    81 0/subop/add %esp 0x224/imm32
+    # . restore registers
+    5f/pop-to-edi
+    5e/pop-to-esi
+    5b/pop-to-ebx
+    5a/pop-to-edx
+    59/pop-to-ecx
+    58/pop-to-eax
+    # reclaim curr-index
+    81 0/subop/add %esp 4/imm32
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+$populate-mu-type:error1:
+    # error("incomplete type definition '" t->name "'\n")
+    (write-buffered *(ebp+0x10) "incomplete type definition '")
+    (type-name *edi)  # Typeinfo-id => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$populate-mu-type:error2:
+    (write-buffered *(ebp+0x10) "type ")
+    (type-name *edi)  # Typeinfo-id => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": 'addr' elements not allowed\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$populate-mu-type:error3:
+    (write-buffered *(ebp+0x10) "type ")
+    (type-name *edi)  # Typeinfo-id => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": 'array' elements not allowed for now\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$populate-mu-type:error4:
+    (write-buffered *(ebp+0x10) "type ")
+    (type-name *edi)  # Typeinfo-id => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": 'byte' elements not allowed\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$populate-mu-type:error5:
+    (write-buffered *(ebp+0x10) "type ")
+    (type-name *edi)  # Typeinfo-id => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": 'slice' elements not allowed\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$populate-mu-type:error6:
+    (write-buffered *(ebp+0x10) "type ")
+    (type-name *edi)  # Typeinfo-id => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": 'stream' elements not allowed for now\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+type-name:  # index: int -> result/eax: (addr array byte)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    #
+    (index Type-id *(ebp+8))
+$type-name:end:
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+index:  # arr: (addr stream (handle array byte)), index: int -> result/eax: (addr array byte)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    56/push-esi
+    # TODO: bounds-check index
+    # esi = arr
+    8b/-> *(ebp+8) 6/r32/esi
+    # eax = index
+    8b/-> *(ebp+0xc) 0/r32/eax
+    # eax = *(arr + 12 + index)
+    8b/-> *(esi+eax<<2+0xc) 0/r32/eax
+$index:end:
+    # . restore registers
+    5e/pop-to-esi
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+#######################################################
+# Compute type sizes
+#######################################################
+
+# Compute the sizes of all user-defined types.
+# We'll need the sizes of their elements, which may be other user-defined
+# types, which we will compute as needed.
+
+# Initially, all user-defined types have their sizes set to -2 (invalid)
+populate-mu-type-sizes:  # err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+$populate-mu-type-sizes:total-sizes:
+    # var curr/eax: (addr typeinfo) = lookup(Program->types)
+    (lookup *_Program-types *_Program-types->payload)  # => eax
+    {
+      # if (curr == null) break
+      3d/compare-eax-and 0/imm32/null
+      74/jump-if-= break/disp8
+      (populate-mu-type-sizes-in-type %eax *(ebp+8) *(ebp+0xc))
+      # curr = lookup(curr->next)
+      (lookup *(eax+0x10) *(eax+0x14))  # Typeinfo-next Typeinfo-next => eax
+      eb/jump loop/disp8
+    }
+$populate-mu-type-sizes:offsets:
+    # curr = *Program->types
+    (lookup *_Program-types *_Program-types->payload)  # => eax
+    {
+      # if (curr == null) break
+      3d/compare-eax-and 0/imm32/null
+      74/jump-if-= break/disp8
+      (populate-mu-type-offsets %eax *(ebp+8) *(ebp+0xc))
+      # curr = curr->next
+      (lookup *(eax+0x10) *(eax+0x14))  # Typeinfo-next Typeinfo-next => eax
+      eb/jump loop/disp8
+    }
+$populate-mu-type-sizes:end:
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+# compute sizes of all fields, recursing as necessary
+# sum up all their sizes to arrive at total size
+# fields may be out of order, but that doesn't affect the answer
+populate-mu-type-sizes-in-type:  # T: (addr typeinfo), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    52/push-edx
+    56/push-esi
+    57/push-edi
+    # esi = T
+    8b/-> *(ebp+8) 6/r32/esi
+    # if T is already computed, return
+    81 7/subop/compare *(esi+0xc) 0/imm32  # Typeinfo-total-size-in-bytes
+    0f 8d/jump-if->= $populate-mu-type-sizes-in-type:end/disp32
+    # if T is being computed, abort
+    81 7/subop/compare *(esi+0xc) -1/imm32/being-computed  # Typeinfo-total-size-in-bytes
+    0f 84/jump-if-= $populate-mu-type-sizes-in-type:abort/disp32
+    # tag T (-2 to -1) to avoid infinite recursion
+    c7 0/subop/copy *(esi+0xc) -1/imm32/being-computed  # Typeinfo-total-size-in-bytes
+    # var total-size/edi: int = 0
+    bf/copy-to-edi 0/imm32
+    # - for every field, if it's a user-defined type, compute its size
+    # var table/ecx: (addr table (handle array byte) (handle typeinfo-entry)) = lookup(T->fields)
+    (lookup *(esi+4) *(esi+8))  # Typeinfo-fields Typeinfo-fields => eax
+    89/<- %ecx 0/r32/eax
+    # var table-size/edx: int = table->write
+    8b/-> *ecx 2/r32/edx  # stream-write
+    # var curr/ecx: (addr table_row) = table->data
+    8d/copy-address *(ecx+0xc) 1/r32/ecx
+    # var max/edx: (addr table_row) = table->data + table->write
+    8d/copy-address *(ecx+edx) 2/r32/edx
+    {
+$populate-mu-type-sizes-in-type:loop:
+      # if (curr >= max) break
+      39/compare %ecx 2/r32/edx
+      73/jump-if-addr>= break/disp8
+      # var t/eax: (addr typeinfo-entry) = lookup(curr->value)
+      (lookup *(ecx+8) *(ecx+0xc))  # => eax
+      # if (t->input-var == 0) silently ignore it; we'll emit a nice error message while type-checking
+      81 7/subop/compare *eax 0/imm32  # Typeinfo-entry-input-var
+      74/jump-if-= $populate-mu-type-sizes-in-type:end/disp8
+      # compute size of t->input-var
+      (lookup *eax *(eax+4))  # Typeinfo-entry-input-var Typeinfo-entry-input-var => eax
+      (compute-size-of-var %eax *(ebp+0xc) *(ebp+0x10))  # => eax
+      # result += eax
+      01/add-to %edi 0/r32/eax
+      # curr += row-size
+      81 0/subop/add %ecx 0x10/imm32  # Typeinfo-fields-row-size
+      #
+      eb/jump loop/disp8
+    }
+    # - save result
+    89/<- *(esi+0xc) 7/r32/edi  # Typeinfo-total-size-in-bytes
+$populate-mu-type-sizes-in-type:end:
+    # . restore registers
+    5f/pop-to-edi
+    5e/pop-to-esi
+    5a/pop-to-edx
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+$populate-mu-type-sizes-in-type:abort:
+    (write-buffered *(ebp+0xc) "cycle in type definitions\n")
+    (flush *(ebp+0xc))
+    (stop *(ebp+0x10) 1)
+    # never gets here
+
+# Analogous to size-of, except we need to compute what size-of can just read
+# off the right data structures.
+compute-size-of-var:  # in: (addr var), err: (addr buffered-file), ed: (addr exit-descriptor) -> result/eax: int
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . push registers
+    51/push-ecx
+    # var t/ecx: (addr type-tree) = lookup(v->type)
+    8b/-> *(ebp+8) 1/r32/ecx
+    (lookup *(ecx+8) *(ecx+0xc))  # Var-type Var-type => eax
+    89/<- %ecx 0/r32/eax
+    # if (t->is-atom == false) t = lookup(t->left)
+    {
+      81 7/subop/compare *ecx 0/imm32/false  # Type-tree-is-atom
+      75/jump-if-!= break/disp8
+      (lookup *(ecx+4) *(ecx+8))  # Type-tree-left Type-tree-left => eax
+      89/<- %ecx 0/r32/eax
+    }
+    # TODO: ensure t is an atom
+    (compute-size-of-type-id *(ecx+4) *(ebp+0xc) *(ebp+0x10))  # Type-tree-value => eax
+$compute-size-of-var:end:
+    # . restore registers
+    59/pop-to-ecx
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+compute-size-of-type-id:  # t: type-id, err: (addr buffered-file), ed: (addr exit-descriptor) -> result/eax: int
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    51/push-ecx
+    # var out/ecx: (handle typeinfo)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %ecx 4/r32/esp
+    # eax = t
+    8b/-> *(ebp+8) 0/r32/eax
+    # if t is a literal, return 0
+    3d/compare-eax-and 0/imm32/literal
+    0f 84/jump-if-= $compute-size-of-type-id:end/disp32  # eax changes type from type-id to int
+    # if t is a byte, return 4 (because we don't really support non-multiples of 4)
+    3d/compare-eax-and 8/imm32/byte
+    {
+      75/jump-if-!= break/disp8
+      b8/copy-to-eax 4/imm32
+      eb/jump $compute-size-of-type-id:end/disp8
+    }
+    # if t is a handle, return 8
+    3d/compare-eax-and 4/imm32/handle
+    {
+      75/jump-if-!= break/disp8
+      b8/copy-to-eax 8/imm32
+      eb/jump $compute-size-of-type-id:end/disp8  # eax changes type from type-id to int
+    }
+    # if t is a slice, return 8
+    3d/compare-eax-and 0xc/imm32/slice
+    {
+      75/jump-if-!= break/disp8
+      b8/copy-to-eax 8/imm32
+      eb/jump $compute-size-of-type-id:end/disp8  # eax changes type from type-id to int
+    }
+    # if t is a user-defined type, compute its size
+    # TODO: support non-atom type
+    (find-typeinfo %eax %ecx)
+    {
+      81 7/subop/compare *ecx 0/imm32
+      74/jump-if-= break/disp8
+$compute-size-of-type-id:user-defined:
+      (lookup *ecx *(ecx+4))  # => eax
+      (populate-mu-type-sizes-in-type %eax *(ebp+0xc) *(ebp+0x10))
+      8b/-> *(eax+0xc) 0/r32/eax  # Typeinfo-total-size-in-bytes
+      eb/jump $compute-size-of-type-id:end/disp8
+    }
+    # otherwise return the word size
+    b8/copy-to-eax 4/imm32
+$compute-size-of-type-id:end:
+    # . reclaim locals
+    81 0/subop/add %esp 8/imm32
+    # . restore registers
+    59/pop-to-ecx
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+# at this point we have total sizes for all user-defined types
+# compute offsets for each element
+# complication: fields may be out of order
+populate-mu-type-offsets:  # in: (addr typeinfo), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    52/push-edx
+    53/push-ebx
+    56/push-esi
+    57/push-edi
+#?     (dump-typeinfos "aaa\n")
+    # var curr-offset/edi: int = 0
+    bf/copy-to-edi 0/imm32
+    # var table/ecx: (addr table string_key (handle typeinfo-entry)) = lookup(in->fields)
+    8b/-> *(ebp+8) 1/r32/ecx
+    (lookup *(ecx+4) *(ecx+8))  # Typeinfo-fields Typeinfo-fields => eax
+    89/<- %ecx 0/r32/eax
+    # var num-elems/edx: int = table->write / Typeinfo-fields-row-size
+    8b/-> *ecx 2/r32/edx  # stream-write
+    c1 5/subop/shift-right-logical  %edx 4/imm8
+    # var i/ebx: int = 0
+    bb/copy-to-ebx 0/imm32
+    {
+$populate-mu-type-offsets:loop:
+      39/compare %ebx 2/r32/edx
+      0f 8d/jump-if->= break/disp32
+#?       (write-buffered Stderr "looking up index ")
+#?       (write-int32-hex-buffered Stderr %ebx)
+#?       (write-buffered Stderr " in ")
+#?       (write-int32-hex-buffered Stderr *(ebp+8))
+#?       (write-buffered Stderr Newline)
+#?       (flush Stderr)
+      # var v/esi: (addr typeinfo-entry)
+      (locate-typeinfo-entry-with-index %ecx %ebx *(ebp+0xc) *(ebp+0x10))  # => eax
+      89/<- %esi 0/r32/eax
+      # if v is null, silently move on; we'll emit a nice error message while type-checking
+      81 7/subop/compare %esi 0/imm32  # Typeinfo-entry-input-var
+      74/jump-if-= $populate-mu-type-offsets:end/disp8
+      # if (v->input-var == 0) silently ignore v; we'll emit a nice error message while type-checking
+      81 7/subop/compare *esi 0/imm32  # Typeinfo-entry-input-var
+      74/jump-if-= $populate-mu-type-offsets:end/disp8
+      # v->output-var->offset = curr-offset
+      # . eax: (addr var)
+      (lookup *(esi+0xc) *(esi+0x10))  # Typeinfo-entry-output-var Typeinfo-entry-output-var => eax
+      89/<- *(eax+0x14) 7/r32/edi  # Var-offset
+      # curr-offset += size-of(v->input-var)
+      (lookup *esi *(esi+4))  # Typeinfo-entry-input-var Typeinfo-entry-input-var => eax
+      (size-of %eax)  # => eax
+      01/add-to %edi 0/r32/eax
+      # ++i
+      43/increment-ebx
+      e9/jump loop/disp32
+    }
+$populate-mu-type-offsets:end:
+    # . restore registers
+    5f/pop-to-edi
+    5e/pop-to-esi
+    5b/pop-to-ebx
+    5a/pop-to-edx
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+locate-typeinfo-entry-with-index:  # table: (addr table (handle array byte) (handle typeinfo-entry)), idx: int, err: (addr buffered-file), ed: (addr exit-descriptor) -> result/eax: (addr typeinfo-entry)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    51/push-ecx
+    52/push-edx
+    53/push-ebx
+    56/push-esi
+    57/push-edi
+    # esi = table
+    8b/-> *(ebp+8) 6/r32/esi
+    # var curr/ecx: (addr row (handle array byte) (handle typeinfo-entry)) = table->data
+    8d/copy-address *(esi+0xc) 1/r32/ecx
+    # var max/edx: (addr byte) = &table->data[table->write]
+    8b/-> *esi 2/r32/edx
+    8d/copy-address *(ecx+edx) 2/r32/edx
+    {
+$locate-typeinfo-entry-with-index:loop:
+      39/compare %ecx 2/r32/edx
+      73/jump-if-addr>= break/disp8
+      # var v/eax: (addr typeinfo-entry)
+      (lookup *(ecx+8) *(ecx+0xc))  # => eax
+      # if (v->index == idx) return v
+      8b/-> *(eax+8) 3/r32/ebx  # Typeinfo-entry-index
+#?       (write-buffered Stderr "comparing ")
+#?       (write-int32-hex-buffered Stderr %ebx)
+#?       (write-buffered Stderr " and ")
+#?       (write-int32-hex-buffered Stderr *(ebp+0xc))
+#?       (write-buffered Stderr Newline)
+#?       (flush Stderr)
+      39/compare *(ebp+0xc) 3/r32/ebx
+      74/jump-if-= $locate-typeinfo-entry-with-index:end/disp8
+      # curr += Typeinfo-entry-size
+      81 0/subop/add %ecx 0x10/imm32  # Typeinfo-entry-size
+      #
+      eb/jump loop/disp8
+    }
+    # return 0
+    b8/copy-to-eax 0/imm32
+$locate-typeinfo-entry-with-index:end:
+#?     (write-buffered Stderr "returning ")
+#?     (write-int32-hex-buffered Stderr %eax)
+#?     (write-buffered Stderr Newline)
+#?     (flush Stderr)
+    # . restore registers
+    5f/pop-to-edi
+    5e/pop-to-esi
+    5b/pop-to-ebx
+    5a/pop-to-edx
+    59/pop-to-ecx
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+dump-typeinfos:  # hdr: (addr array byte)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    #
+    (write-buffered Stderr *(ebp+8))
+    (flush Stderr)
+    # var curr/eax: (addr typeinfo) = lookup(Program->types)
+    (lookup *_Program-types *_Program-types->payload)  # => eax
+    {
+      # if (curr == null) break
+      3d/compare-eax-and 0/imm32
+      74/jump-if-= break/disp8
+      (write-buffered Stderr "---\n")
+      (flush Stderr)
+      (dump-typeinfo %eax)
+      # curr = lookup(curr->next)
+      (lookup *(eax+0x10) *(eax+0x14))  # Typeinfo-next Typeinfo-next => eax
+      eb/jump loop/disp8
+    }
+$dump-typeinfos:end:
+    # . restore registers
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+dump-typeinfo:  # in: (addr typeinfo)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    52/push-edx
+    53/push-ebx
+    56/push-esi
+    57/push-edi
+    # esi = in
+    8b/-> *(ebp+8) 6/r32/esi
+    # var table/ecx: (addr table (handle array byte) (handle typeinfo-entry)) = lookup(T->fields)
+    (lookup *(esi+4) *(esi+8))  # Typeinfo-fields Typeinfo-fields => eax
+    89/<- %ecx 0/r32/eax
+    (write-buffered Stderr "id:")
+    (write-int32-hex-buffered Stderr *esi)
+    (write-buffered Stderr "\n")
+    (write-buffered Stderr "fields @ ")
+    (write-int32-hex-buffered Stderr %ecx)
+    (write-buffered Stderr Newline)
+    (flush Stderr)
+    (write-buffered Stderr "  write: ")
+    (write-int32-hex-buffered Stderr *ecx)
+    (write-buffered Stderr Newline)
+    (flush Stderr)
+    (write-buffered Stderr "  read: ")
+    (write-int32-hex-buffered Stderr *(ecx+4))
+    (write-buffered Stderr Newline)
+    (flush Stderr)
+    (write-buffered Stderr "  size: ")
+    (write-int32-hex-buffered Stderr *(ecx+8))
+    (write-buffered Stderr Newline)
+    (flush Stderr)
+    # var table-size/edx: int = table->write
+    8b/-> *ecx 2/r32/edx  # stream-write
+    # var curr/ecx: (addr table_row) = table->data
+    8d/copy-address *(ecx+0xc) 1/r32/ecx
+    # var max/edx: (addr table_row) = table->data + table->write
+    8d/copy-address *(ecx+edx) 2/r32/edx
+    {
+$dump-typeinfo:loop:
+      # if (curr >= max) break
+      39/compare %ecx 2/r32/edx
+      0f 83/jump-if-addr>= break/disp32
+      (write-buffered Stderr "  row:\n")
+      (write-buffered Stderr "    key: ")
+      (write-int32-hex-buffered Stderr *ecx)
+      (write-buffered Stderr ",")
+      (write-int32-hex-buffered Stderr *(ecx+4))
+      (write-buffered Stderr " = '")
+      (lookup *ecx *(ecx+4))
+      (write-buffered Stderr %eax)
+      (write-buffered Stderr "' @ ")
+      (write-int32-hex-buffered Stderr %eax)
+      (write-buffered Stderr Newline)
+      (flush Stderr)
+      (write-buffered Stderr "    value: ")
+      (write-int32-hex-buffered Stderr *(ecx+8))
+      (write-buffered Stderr ",")
+      (write-int32-hex-buffered Stderr *(ecx+0xc))
+      (write-buffered Stderr " = typeinfo-entry@")
+      (lookup *(ecx+8) *(ecx+0xc))
+      (write-int32-hex-buffered Stderr %eax)
+      (write-buffered Stderr Newline)
+      (flush Stderr)
+      (write-buffered Stderr "        input var@")
+      (dump-var 5 %eax)
+      (lookup *(ecx+8) *(ecx+0xc))
+      (write-buffered Stderr "        index: ")
+      (write-int32-hex-buffered Stderr *(eax+8))
+      (write-buffered Stderr Newline)
+      (flush Stderr)
+      (write-buffered Stderr "        output var@")
+      8d/copy-address *(eax+0xc) 0/r32/eax  # Typeinfo-entry-output-var
+      (dump-var 5 %eax)
+      (flush Stderr)
+      # curr += row-size
+      81 0/subop/add %ecx 0x10/imm32  # Typeinfo-fields-row-size
+      #
+      e9/jump loop/disp32
+    }
+$dump-typeinfo:end:
+    # . restore registers
+    5f/pop-to-edi
+    5e/pop-to-esi
+    5b/pop-to-ebx
+    5a/pop-to-edx
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+dump-var:  # indent: int, v: (addr handle var)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    53/push-ebx
+    # eax = v
+    8b/-> *(ebp+0xc) 0/r32/eax
+    #
+    (write-int32-hex-buffered Stderr *eax)
+    (write-buffered Stderr ",")
+    (write-int32-hex-buffered Stderr *(eax+4))
+    (write-buffered Stderr "->")
+    (lookup *eax *(eax+4))
+    (write-int32-hex-buffered Stderr %eax)
+    (write-buffered Stderr Newline)
+    (flush Stderr)
+    {
+      3d/compare-eax-and 0/imm32
+      0f 84/jump-if-= break/disp32
+      (emit-indent Stderr *(ebp+8))
+      (write-buffered Stderr "name: ")
+      89/<- %ebx 0/r32/eax
+      (write-int32-hex-buffered Stderr *ebx)  # Var-name
+      (write-buffered Stderr ",")
+      (write-int32-hex-buffered Stderr *(ebx+4))  # Var-name
+      (write-buffered Stderr "->")
+      (lookup *ebx *(ebx+4))  # Var-name
+      (write-int32-hex-buffered Stderr %eax)
+      {
+        3d/compare-eax-and 0/imm32
+        74/jump-if-= break/disp8
+        (write-buffered Stderr Space)
+        (write-buffered Stderr %eax)
+      }
+      (write-buffered Stderr Newline)
+      (flush Stderr)
+      (emit-indent Stderr *(ebp+8))
+      (write-buffered Stderr "block depth: ")
+      (write-int32-hex-buffered Stderr *(ebx+0x10))  # Var-block-depth
+      (write-buffered Stderr Newline)
+      (flush Stderr)
+      (emit-indent Stderr *(ebp+8))
+      (write-buffered Stderr "stack offset: ")
+      (write-int32-hex-buffered Stderr *(ebx+0x14))  # Var-offset
+      (write-buffered Stderr Newline)
+      (flush Stderr)
+      (emit-indent Stderr *(ebp+8))
+      (write-buffered Stderr "reg: ")
+      (write-int32-hex-buffered Stderr *(ebx+0x18))  # Var-register
+      (write-buffered Stderr ",")
+      (write-int32-hex-buffered Stderr *(ebx+0x1c))  # Var-register
+      (write-buffered Stderr "->")
+      (flush Stderr)
+      (lookup *(ebx+0x18) *(ebx+0x1c))  # Var-register
+      (write-int32-hex-buffered Stderr %eax)
+      {
+        3d/compare-eax-and 0/imm32
+        74/jump-if-= break/disp8
+        (write-buffered Stderr Space)
+        (write-buffered Stderr %eax)
+      }
+      (write-buffered Stderr Newline)
+      (flush Stderr)
+    }
+$dump-var:end:
+    # . restore registers
+    5b/pop-to-ebx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+#######################################################
+# Type-checking
+#######################################################
+
+check-mu-types:  # err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    # var curr/eax: (addr function) = lookup(Program->functions)
+    (lookup *_Program-functions *_Program-functions->payload)  # => eax
+    {
+$check-mu-types:loop:
+      # if (curr == null) break
+      3d/compare-eax-and 0/imm32
+      0f 84/jump-if-= break/disp32
+#?       # dump curr->name {{{
+#?       50/push-eax
+#?       (lookup *eax *(eax+4))  # Function-name Function-name => eax
+#?       (write-buffered Stderr %eax)
+#?       (write-buffered Stderr Newline)
+#?       (flush Stderr)
+#?       58/pop-to-eax
+#?       # }}}
+      (check-mu-function %eax *(ebp+8) *(ebp+0xc))
+      # curr = lookup(curr->next)
+      (lookup *(eax+0x20) *(eax+0x24))  # Function-next Function-next => eax
+      e9/jump loop/disp32
+    }
+$check-mu-types:end:
+    # . restore registers
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+check-mu-function:  # fn: (addr function), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    56/push-esi
+    # esi = f
+    8b/-> *(ebp+8) 6/r32/esi
+    # outputs
+    (lookup *(esi+0x10) *(esi+0x14))  # Function-outputs Function-outputs => eax
+    (check-all-unique-registers %eax %esi *(ebp+0xc) *(ebp+0x10))
+    # body
+    (lookup *(esi+0x18) *(esi+0x1c))  # Function-body Function-body => eax
+    (check-mu-block %eax *(ebp+8) *(ebp+0xc) *(ebp+0x10))
+    # if function has no outputs, we're done
+    81 7/subop/compare *(esi+0x10) 0/imm32
+    74/jump-if-= $check-mu-function:end/disp8
+    # some final checks on body
+    (check-final-stmt-is-return %eax %esi *(ebp+0xc) *(ebp+0x10))
+    (check-no-breaks %eax %esi *(ebp+0xc) *(ebp+0x10))
+$check-mu-function:end:
+    # . restore registers
+    5e/pop-to-esi
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+check-mu-block:  # block: (addr block), fn: (addr function), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    # eax = block
+    8b/-> *(ebp+8) 0/r32/eax
+    # var stmts/eax: (addr list stmt) = lookup(block->statements)
+    (lookup *(eax+4) *(eax+8))  # Block-stmts Block-stmts => eax
+    #
+    {
+$check-mu-block:check-empty:
+      3d/compare-eax-and 0/imm32
+      0f 84/jump-if-= break/disp32
+      # emit block->statements
+      (check-mu-stmt-list %eax *(ebp+0xc) *(ebp+0x10) *(ebp+0x14))
+    }
+$check-mu-block:end:
+    # . restore registers
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+check-mu-stmt-list:  # stmts: (addr list stmt), fn: (addr function), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    56/push-esi
+    # esi = stmts
+    8b/-> *(ebp+8) 6/r32/esi
+    {
+$check-mu-stmt-list:loop:
+      81 7/subop/compare %esi 0/imm32
+      0f 84/jump-if-= break/disp32
+      # var curr-stmt/eax: (addr stmt) = lookup(stmts->value)
+      (lookup *esi *(esi+4))  # List-value List-value => eax
+      {
+$check-mu-stmt-list:check-for-block:
+        81 7/subop/compare *eax 0/imm32/block  # Stmt-tag
+        75/jump-if-!= break/disp8
+$check-mu-stmt-list:block:
+        (check-mu-block %eax *(ebp+0xc) *(ebp+0x10) *(ebp+0x14))
+        eb/jump $check-mu-stmt-list:continue/disp8
+      }
+      {
+$check-mu-stmt-list:check-for-stmt1:
+        81 7/subop/compare *eax 1/imm32/stmt1  # Stmt-tag
+        0f 85/jump-if-!= break/disp32
+$check-mu-stmt-list:stmt1:
+        (check-mu-stmt %eax *(ebp+0xc) *(ebp+0x10) *(ebp+0x14))
+        eb/jump $check-mu-stmt-list:continue/disp8
+      }
+      {
+$check-mu-stmt-list:check-for-reg-var-def:
+        81 7/subop/compare *eax 3/imm32/reg-var-def  # Stmt-tag
+        0f 85/jump-if-!= break/disp32
+$check-mu-stmt-list:reg-var-def:
+        (check-mu-stmt %eax *(ebp+0xc) *(ebp+0x10) *(ebp+0x14))
+        eb/jump $check-mu-stmt-list:continue/disp8
+      }
+$check-mu-stmt-list:continue:
+      # TODO: raise an error on unrecognized Stmt-tag
+      (lookup *(esi+8) *(esi+0xc))  # List-next List-next => eax
+      89/<- %esi 0/r32/eax
+      e9/jump loop/disp32
+    }
+$check-mu-stmt-list:end:
+    # . restore registers
+    5e/pop-to-esi
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+check-mu-stmt:  # stmt: (addr stmt), fn: (addr function), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    # - if stmt's operation matches a primitive, check against it
+    (has-primitive-name? *(ebp+8))  # => eax
+    3d/compare-eax-and 0/imm32/false
+    {
+      74/jump-if-= break/disp8
+      (check-mu-primitive *(ebp+8) *(ebp+0xc) *(ebp+0x10) *(ebp+0x14))
+      e9/jump $check-mu-stmt:end/disp32
+    }
+    # - otherwise find a function to check against
+    # var f/eax: (addr function) = lookup(*Program->functions)
+    (lookup *_Program-functions *_Program-functions->payload)  # => eax
+    (find-matching-function %eax *(ebp+8))  # => eax
+    3d/compare-eax-and 0/imm32
+    {
+      74/jump-if-= break/disp8
+      (check-mu-call *(ebp+8) %eax *(ebp+0xc) *(ebp+0x10) *(ebp+0x14))
+      eb/jump $check-mu-stmt:end/disp8
+    }
+    # var f/eax: (addr function) = lookup(*Program->signatures)
+    (lookup *_Program-signatures *_Program-signatures->payload)  # => eax
+    (find-matching-function %eax *(ebp+8))  # => eax
+    3d/compare-eax-and 0/imm32
+    {
+      74/jump-if-= break/disp8
+      (check-mu-call *(ebp+8) %eax *(ebp+0xc) *(ebp+0x10) *(ebp+0x14))
+      eb/jump $check-mu-stmt:end/disp8
+    }
+    # - otherwise abort
+    e9/jump $check-mu-stmt:unknown-call/disp32
+$check-mu-stmt:end:
+    # . restore registers
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+$check-mu-stmt:unknown-call:
+    (write-buffered *(ebp+0x10) "unknown function '")
+    8b/-> *(ebp+8) 0/r32/eax
+    (lookup *(eax+4) *(eax+8))  # Stmt1-operation Stmt1-operation => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "'\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+has-primitive-name?:  # stmt: (addr stmt) -> result/eax: boolean
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    51/push-ecx
+    56/push-esi
+    # var name/esi: (addr array byte) = lookup(stmt->operation)
+    8b/-> *(ebp+8) 6/r32/esi
+    (lookup *(esi+4) *(esi+8))  # Stmt1-operation Stmt1-operation => eax
+    89/<- %esi 0/r32/eax
+    # if (name == "return") return true
+    (string-equal? %esi "return")  # => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 85/jump-if-!= $has-primitive-name?:end/disp32
+    # if (name == "get") return true
+    (string-equal? %esi "get")  # => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 85/jump-if-!= $has-primitive-name?:end/disp32
+    # if (name == "index") return true
+    (string-equal? %esi "index")  # => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 85/jump-if-!= $has-primitive-name?:end/disp32
+    # if (name == "length") return true
+    (string-equal? %esi "length")  # => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 85/jump-if-!= $has-primitive-name?:end/disp32
+    # if (name == "compute-offset") return true
+    (string-equal? %esi "compute-offset")  # => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 85/jump-if-!= $has-primitive-name?:end/disp32
+    # if (name == "copy-object") return true
+    (string-equal? %esi "copy-object")  # => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 85/jump-if-!= $has-primitive-name?:end/disp32
+    # if (name == "clear-object") return true
+    (string-equal? %esi "clear-object")  # => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 85/jump-if-!= $has-primitive-name?:end/disp32
+    # if (name == "allocate") return true
+    (string-equal? %esi "allocate")  # => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 85/jump-if-!= $has-primitive-name?:end/disp32
+    # if (name == "populate") return true
+    (string-equal? %esi "populate")  # => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 85/jump-if-!= $has-primitive-name?:end/disp32
+    # if (name == "populate-stream") return true
+    (string-equal? %esi "populate-stream")  # => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 85/jump-if-!= $has-primitive-name?:end/disp32
+    # if (name == "read-from-stream") return true
+    (string-equal? %esi "read-from-stream")  # => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 85/jump-if-!= $has-primitive-name?:end/disp32
+    # if (name == "write-to-stream") return true
+    (string-equal? %esi "write-to-stream")  # => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 85/jump-if-!= $has-primitive-name?:end/disp32
+    # var curr/ecx: (addr primitive) = Primitives
+    b9/copy-to-ecx Primitives/imm32
+    {
+$has-primitive-name?:loop:
+      # if (curr == null) break
+      81 7/subop/compare %ecx 0/imm32
+      74/jump-if-= break/disp8
+      # if (primitive->name == name) return true
+      (lookup *ecx *(ecx+4))  # Primitive-name Primitive-name => eax
+#?       (write-buffered Stderr %eax)
+#?       (write-buffered Stderr Newline)
+#?       (flush Stderr)
+      (string-equal? %esi %eax)  # => eax
+      3d/compare-eax-and 0/imm32/false
+      75/jump-if-!= $has-primitive-name?:end/disp8
+$has-primitive-name?:next-primitive:
+      # curr = curr->next
+      (lookup *(ecx+0x3c) *(ecx+0x40))  # Primitive-next Primitive-next => eax
+      89/<- %ecx 0/r32/eax
+      #
+      e9/jump loop/disp32
+    }
+    # return null
+    b8/copy-to-eax 0/imm32
+$has-primitive-name?:end:
+    # . restore registers
+    5e/pop-to-esi
+    59/pop-to-ecx
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+check-mu-primitive:  # stmt: (addr stmt), fn: (addr function), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    # var op/ecx: (addr array byte) = lookup(stmt->operation)
+    8b/-> *(ebp+8) 0/r32/eax
+    (lookup *(eax+4) *(eax+8))  # Stmt1-operation Stmt1-operation => eax
+    89/<- %ecx 0/r32/eax
+    # if (op == "copy") check-mu-copy-stmt
+    {
+      (string-equal? %ecx "copy")  # => eax
+      3d/compare-eax-and 0/imm32/false
+      74/jump-if-= break/disp8
+      (check-mu-copy-stmt *(ebp+8) *(ebp+0xc) *(ebp+0x10) *(ebp+0x14))
+      e9/jump $check-mu-primitive:end/disp32
+    }
+    # if (op == "copy-to") check-mu-copy-to-stmt
+    {
+      (string-equal? %ecx "copy-to")  # => eax
+      3d/compare-eax-and 0/imm32/false
+      74/jump-if-= break/disp8
+      (check-mu-copy-to-stmt *(ebp+8) *(ebp+0xc) *(ebp+0x10) *(ebp+0x14))
+      e9/jump $check-mu-primitive:end/disp32
+    }
+    # if (op == "copy-byte") check-mu-copy-byte-stmt
+    {
+      (string-equal? %ecx "copy-byte")  # => eax
+      3d/compare-eax-and 0/imm32/false
+      74/jump-if-= break/disp8
+      (check-mu-copy-byte-stmt *(ebp+8) *(ebp+0xc) *(ebp+0x10) *(ebp+0x14))
+      e9/jump $check-mu-primitive:end/disp32
+    }
+    # if (op == "copy-byte-to") check-mu-copy-byte-to-stmt
+    {
+      (string-equal? %ecx "copy-byte-to")  # => eax
+      3d/compare-eax-and 0/imm32/false
+      74/jump-if-= break/disp8
+      (check-mu-copy-byte-to-stmt *(ebp+8) *(ebp+0xc) *(ebp+0x10) *(ebp+0x14))
+      e9/jump $check-mu-primitive:end/disp32
+    }
+    # if (op == "compare") check-mu-compare-stmt
+    {
+      (string-equal? %ecx "compare")  # => eax
+      3d/compare-eax-and 0/imm32/false
+      74/jump-if-= break/disp8
+      (check-mu-compare-stmt *(ebp+8) *(ebp+0xc) *(ebp+0x10) *(ebp+0x14))
+      e9/jump $check-mu-primitive:end/disp32
+    }
+    # if (op == "address") check-mu-address-stmt
+    {
+      (string-equal? %ecx "address")  # => eax
+      3d/compare-eax-and 0/imm32/false
+      74/jump-if-= break/disp8
+      (check-mu-address-stmt *(ebp+8) *(ebp+0xc) *(ebp+0x10) *(ebp+0x14))
+      e9/jump $check-mu-primitive:end/disp32
+    }
+    # if (op == "return") check-mu-return-stmt
+    {
+      (string-equal? %ecx "return")  # => eax
+      3d/compare-eax-and 0/imm32/false
+      74/jump-if-= break/disp8
+      (check-mu-return-stmt *(ebp+8) *(ebp+0xc) *(ebp+0x10) *(ebp+0x14))
+      e9/jump $check-mu-primitive:end/disp32
+    }
+    # if (op == "get") check-mu-get-stmt
+    {
+      (string-equal? %ecx "get")  # => eax
+      3d/compare-eax-and 0/imm32/false
+      74/jump-if-= break/disp8
+      (check-mu-get-stmt *(ebp+8) *(ebp+0xc) *(ebp+0x10) *(ebp+0x14))
+      e9/jump $check-mu-primitive:end/disp32
+    }
+    # if (op == "index") check-mu-index-stmt
+    {
+      (string-equal? %ecx "index")  # => eax
+      3d/compare-eax-and 0/imm32/false
+      74/jump-if-= break/disp8
+      (check-mu-index-stmt *(ebp+8) *(ebp+0xc) *(ebp+0x10) *(ebp+0x14))
+      e9/jump $check-mu-primitive:end/disp32
+    }
+    # if (op == "length") check-mu-length-stmt
+    {
+      (string-equal? %ecx "length")  # => eax
+      3d/compare-eax-and 0/imm32/false
+      74/jump-if-= break/disp8
+      (check-mu-length-stmt *(ebp+8) *(ebp+0xc) *(ebp+0x10) *(ebp+0x14))
+      e9/jump $check-mu-primitive:end/disp32
+    }
+    # if (op == "compute-offset") check-mu-compute-offset-stmt
+    {
+      (string-equal? %ecx "compute-offset")  # => eax
+      3d/compare-eax-and 0/imm32/false
+      74/jump-if-= break/disp8
+      (check-mu-compute-offset-stmt *(ebp+8) *(ebp+0xc) *(ebp+0x10) *(ebp+0x14))
+      e9/jump $check-mu-primitive:end/disp32
+    }
+    # if (op == "copy-object") check-mu-copy-object-stmt
+    {
+      (string-equal? %ecx "copy-object")  # => eax
+      3d/compare-eax-and 0/imm32/false
+      74/jump-if-= break/disp8
+      (check-mu-copy-object-stmt *(ebp+8) *(ebp+0xc) *(ebp+0x10) *(ebp+0x14))
+      e9/jump $check-mu-primitive:end/disp32
+    }
+    # if (op == "clear-object") check-mu-clear-object-stmt
+    {
+      (string-equal? %ecx "clear-object")  # => eax
+      3d/compare-eax-and 0/imm32/false
+      74/jump-if-= break/disp8
+      (check-mu-clear-object-stmt *(ebp+8) *(ebp+0xc) *(ebp+0x10) *(ebp+0x14))
+      e9/jump $check-mu-primitive:end/disp32
+    }
+    # if (op == "allocate") check-mu-allocate-stmt
+    {
+      (string-equal? %ecx "allocate")  # => eax
+      3d/compare-eax-and 0/imm32/false
+      74/jump-if-= break/disp8
+      (check-mu-allocate-stmt *(ebp+8) *(ebp+0xc) *(ebp+0x10) *(ebp+0x14))
+      e9/jump $check-mu-primitive:end/disp32
+    }
+    # if (op == "populate") check-mu-populate-stmt
+    {
+      (string-equal? %ecx "populate")  # => eax
+      3d/compare-eax-and 0/imm32/false
+      74/jump-if-= break/disp8
+      (check-mu-populate-stmt *(ebp+8) *(ebp+0xc) *(ebp+0x10) *(ebp+0x14))
+      e9/jump $check-mu-primitive:end/disp32
+    }
+    # if (op == "populate-stream") check-mu-populate-stream-stmt
+    {
+      (string-equal? %ecx "populate-stream")  # => eax
+      3d/compare-eax-and 0/imm32/false
+      74/jump-if-= break/disp8
+      (check-mu-populate-stream-stmt *(ebp+8) *(ebp+0xc) *(ebp+0x10) *(ebp+0x14))
+      e9/jump $check-mu-primitive:end/disp32
+    }
+    # if (op == "read-from-stream") check-mu-read-from-stream-stmt
+    {
+      (string-equal? %ecx "read-from-stream")  # => eax
+      3d/compare-eax-and 0/imm32/false
+      74/jump-if-= break/disp8
+      (check-mu-read-from-stream-stmt *(ebp+8) *(ebp+0xc) *(ebp+0x10) *(ebp+0x14))
+      e9/jump $check-mu-primitive:end/disp32
+    }
+    # if (op == "write-to-stream") check-mu-write-to-stream-stmt
+    {
+      (string-equal? %ecx "write-to-stream")  # => eax
+      3d/compare-eax-and 0/imm32/false
+      74/jump-if-= break/disp8
+      (check-mu-write-to-stream-stmt *(ebp+8) *(ebp+0xc) *(ebp+0x10) *(ebp+0x14))
+      e9/jump $check-mu-primitive:end/disp32
+    }
+    # if (op == "convert") check-mu-convert-stmt
+    {
+      (string-equal? %ecx "convert")  # => eax
+      3d/compare-eax-and 0/imm32/false
+      74/jump-if-= break/disp8
+      (check-mu-convert-stmt *(ebp+8) *(ebp+0xc) *(ebp+0x10) *(ebp+0x14))
+      e9/jump $check-mu-primitive:end/disp32
+    }
+    # otherwise check-numberlike-stmt
+    (check-mu-numberlike-primitive *(ebp+8) *(ebp+0xc) *(ebp+0x10) *(ebp+0x14))
+$check-mu-primitive:end:
+    # . restore registers
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+# by default, Mu primitives should only operate on 'number-like' types
+check-mu-numberlike-primitive:  # stmt: (addr stmt), fn: (addr function), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    56/push-esi
+    # esi = stmt
+    8b/-> *(ebp+8) 6/r32/esi
+    # var gas/ecx: int = 2
+    b9/copy-to-ecx 2/imm32
+    # - check at most 1 output
+    # var output/eax: (addr stmt-var) = stmt->outputs
+    (lookup *(esi+0x14) *(esi+0x18))  # Stmt1-outputs Stmt1-outputs => eax
+    {
+      3d/compare-eax-and 0/imm32
+      74/jump-if-= break/disp8
+$check-mu-numberlike-primitive:output:
+      (check-mu-numberlike-output %eax *(ebp+8) *(ebp+0xc) *(ebp+0x10) *(ebp+0x14))
+      (lookup *(eax+8) *(eax+0xc))  # Stmt-var-next Stmt-var-next => eax
+      3d/compare-eax-and 0/imm32
+      0f 85/jump-if-!= $check-mu-numberlike-primitive:error-too-many-outputs/disp32
+      # check output is in a register
+      # --gas
+      49/decrement-ecx
+    }
+    # - check first inout
+    (lookup *(esi+0xc) *(esi+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+    {
+      3d/compare-eax-and 0/imm32
+      0f 84/jump-if-= $check-mu-numberlike-primitive:end/disp32
+$check-mu-numberlike-primitive:first-inout:
+      (check-mu-numberlike-arg %eax *(ebp+8) *(ebp+0xc) *(ebp+0x10) *(ebp+0x14))
+      # --gas
+      49/decrement-ecx
+    }
+    # - check second inout
+    (lookup *(eax+8) *(eax+0xc))  # Stmt-var-next Stmt-var-next => eax
+    {
+      3d/compare-eax-and 0/imm32
+      74/jump-if-= $check-mu-numberlike-primitive:end/disp8
+$check-mu-numberlike-primitive:second-inout:
+      # is a second inout allowed?
+      81 7/subop/compare %ecx 0/imm32
+      0f 84/jump-if-= $check-mu-numberlike-primitive:error-too-many-inouts/disp32
+$check-mu-numberlike-primitive:second-inout-permitted:
+      (check-mu-numberlike-arg %eax *(ebp+8) *(ebp+0xc) *(ebp+0x10) *(ebp+0x14))
+    }
+$check-mu-numberlike-primitive:third-inout:
+    # if there's a third arg, raise an error
+    81 7/subop/compare *(eax+8) 0/imm32  # Stmt-var-next
+    0f 85/jump-if-!= $check-mu-numberlike-primitive:error-too-many-inouts/disp32
+$check-mu-numberlike-primitive:end:
+    # . restore registers
+    5e/pop-to-esi
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+$check-mu-numberlike-primitive:error-too-many-inouts:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt ")
+    (lookup *(esi+4) *(esi+8))  # Stmt1-operation Stmt1-operation => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": too many inouts; most primitives support at most two arguments, across inouts and outputs\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-numberlike-primitive:error-too-many-outputs:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt ")
+    (lookup *(esi+4) *(esi+8))  # Stmt1-operation Stmt1-operation => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": too many outputs; most primitives support at most one output\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+check-mu-numberlike-arg:  # v: (addr stmt-var), stmt: (addr stmt), fn: (addr function), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    56/push-esi
+    # var t/esi: (addr type-tree) = lookup(v->value->type)
+    8b/-> *(ebp+8) 0/r32/eax
+    (lookup *eax *(eax+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *(eax+8) *(eax+0xc))  # Var-type Var-type => eax
+    89/<- %esi 0/r32/eax
+$check-mu-numberlike-arg:check-literal:
+    # if t is an int, return
+    (is-simple-mu-type? %esi 0)  # literal => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 85/jump-if-!= $check-mu-numberlike-arg:end/disp32
+$check-mu-numberlike-arg:check-addr:
+    # if t is an addr and v is dereferenced, return whether t->payload is an addr
+    {
+      (is-mu-addr-type? %esi)  # => eax
+      3d/compare-eax-and 0/imm32/false
+      74/jump-if-= break/disp8
+      8b/-> *(ebp+8) 0/r32/eax
+      8b/-> *(eax+0x10) 0/r32/eax  # Stmt-var-is-deref
+      3d/compare-eax-and 0/imm32/false
+      {
+        74/jump-if-= break/disp8
+        (lookup *(esi+0xc) *(esi+0x10))  # Type-tree-right Type-tree-right => eax
+        # if t->right is null, t = t->left
+        81 7/subop/compare *(eax+0xc) 0/imm32  # Type-tree-right
+        {
+          75/jump-if-!= break/disp8
+          (lookup *(eax+4) *(eax+8))  # Type-tree-left Type-tree-left => eax
+        }
+        (is-mu-addr-type? %eax)  # => eax
+        3d/compare-eax-and 0/imm32/false
+        74/jump-if-= $check-mu-numberlike-arg:end/disp8
+      }
+    }
+$check-mu-numberlike-arg:output-checks:
+    (check-mu-numberlike-output *(ebp+8) *(ebp+0xc) *(ebp+0x10) *(ebp+0x14) *(ebp+0x18))
+$check-mu-numberlike-arg:end:
+    # . restore registers
+    5e/pop-to-esi
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+check-mu-numberlike-output:  # v: (addr stmt-var), stmt: (addr stmt), fn: (addr function), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    #
+    (is-mu-numberlike-output-var? *(ebp+8))  # => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 84/jump-if-= $check-mu-numberlike-output:fail/disp32
+$check-mu-numberlike-output:end:
+    # . restore registers
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+$check-mu-numberlike-output:fail:
+    # otherwise raise an error
+    (write-buffered *(ebp+0x14) "fn ")
+    8b/-> *(ebp+0x10) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x14) %eax)
+    (write-buffered *(ebp+0x14) ": stmt ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *(eax+4) *(eax+8))  # Stmt1-operation Stmt1-operation => eax
+    (write-buffered *(ebp+0x14) %eax)
+    (write-buffered *(ebp+0x14) ": '")
+    8b/-> *(ebp+8) 0/r32/eax
+    (lookup *eax *(eax+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *eax *(eax+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x14) %eax)
+    (write-buffered *(ebp+0x14) "' must be a non-addr non-offset scalar\n")
+    (flush *(ebp+0x14))
+    (stop *(ebp+0x18) 1)
+    # never gets here
+
+is-mu-numberlike-output-var?:  # v: (addr stmt-var) -> result/eax: boolean
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    #
+    8b/-> *(ebp+8) 0/r32/eax
+    (lookup *eax *(eax+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *(eax+8) *(eax+0xc))  # Var-type Var-type => eax
+    (is-mu-numberlike-output? %eax)  # => eax
+$is-mu-numberlike-output-var?:end:
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+is-mu-numberlike-output?:  # v: (addr type-tree) -> result/eax: boolean
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    56/push-esi
+    # var t/esi: (addr type-tree) = lookup(v->value->type)
+    8b/-> *(ebp+8) 6/r32/esi
+$is-mu-numberlike-output?:check-int:
+    # if t is an int, return
+    (is-simple-mu-type? %esi 1)  # int => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 85/jump-if-!= $is-mu-numberlike-output?:return-true/disp32
+$is-mu-numberlike-output?:check-float:
+    # if t is a float, return
+    (is-simple-mu-type? %esi 0xf)  # float => eax
+    3d/compare-eax-and 0/imm32/false
+    75/jump-if-!= $is-mu-numberlike-output?:return-true/disp8
+$is-mu-numberlike-output?:check-boolean:
+    # if t is a boolean, return
+    (is-simple-mu-type? %esi 5)  # boolean => eax
+    3d/compare-eax-and 0/imm32/false
+    75/jump-if-!= $is-mu-numberlike-output?:return-true/disp8
+$is-mu-numberlike-output?:check-byte:
+    # if t is a byte, return
+    (is-simple-mu-type? %esi 8)  # byte => eax
+    3d/compare-eax-and 0/imm32/false
+    75/jump-if-!= $is-mu-numberlike-output?:return-true/disp8
+$is-mu-numberlike-output?:check-code-point:
+    # if t is a code-point, return
+    (is-simple-mu-type? %esi 0xd)  # code-point => eax
+    3d/compare-eax-and 0/imm32/false
+    75/jump-if-!= $is-mu-numberlike-output?:return-true/disp8
+$is-mu-numberlike-output?:check-grapheme:
+    # if t is a grapheme, return
+    (is-simple-mu-type? %esi 0xe)  # grapheme => eax
+    3d/compare-eax-and 0/imm32/false
+    75/jump-if-!= $is-mu-numberlike-output?:return-true/disp8
+$is-mu-numberlike-output?:return-false:
+    b8/copy-to-eax 0/imm32/false
+    eb/jump $is-mu-numberlike-output?:end/disp8
+$is-mu-numberlike-output?:return-true:
+    b8/copy-to-eax 1/imm32/true
+$is-mu-numberlike-output?:end:
+    # . restore registers
+    5e/pop-to-esi
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+check-mu-copy-stmt:  # stmt: (addr stmt), fn: (addr function), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    52/push-edx
+    56/push-esi
+    57/push-edi
+    # var type-parameters/edx: (addr table (handle array byte) (addr type-tree) 8)
+    81 5/subop/subtract %esp 0x60/imm32
+    68/push 0x60/imm32/size
+    68/push 0/imm32/read
+    68/push 0/imm32/write
+    89/<- %edx 4/r32/esp
+$check-mu-copy-stmt:get-output:
+    # esi = stmt
+    8b/-> *(ebp+8) 6/r32/esi
+    # var output/edi: (addr stmt-var) = stmt->outputs
+    (lookup *(esi+0x14) *(esi+0x18))  # Stmt1-outputs Stmt1-outputs => eax
+    89/<- %edi 0/r32/eax
+    # zero outputs
+    3d/compare-eax-and 0/imm32
+    0f 84/jump-if-= $check-mu-copy-stmt:error-no-output/disp32
+    # > 1 output
+    (lookup *(edi+8) *(edi+0xc))  # Stmt-var-next Stmt-var-next => eax
+    3d/compare-eax-and 0/imm32
+    0f 85/jump-if-!= $check-mu-copy-stmt:error-too-many-outputs/disp32
+$check-mu-copy-stmt:get-inout:
+    # var inout/esi: (addr stmt-var) = stmt->inouts
+    (lookup *(esi+0xc) *(esi+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+    89/<- %esi 0/r32/eax
+    # zero inouts
+    3d/compare-eax-and 0/imm32
+    0f 84/jump-if-= $check-mu-copy-stmt:error-no-inout/disp32
+    # > 1 inout
+    (lookup *(esi+8) *(esi+0xc))  # Stmt-var-next Stmt-var-next => eax
+    3d/compare-eax-and 0/imm32
+    0f 85/jump-if-!= $check-mu-copy-stmt:error-too-many-inouts/disp32
+$check-mu-copy-stmt:types:
+    # if inout is not a scalar, abort
+    (lookup *esi *(esi+4))  # Stmt-var-value Stmt-var-value => eax
+    (size-of %eax)  # => eax
+    3d/compare-eax-and 4/imm32
+    0f 8f/jump-if-> $check-mu-copy-stmt:error-inout-too-large/disp32
+    # var inout-type/ecx: (addr type-tree) = inout->value->type
+    (lookup *esi *(esi+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *(eax+8) *(eax+0xc))  # Var-type Var-type => eax
+    89/<- %ecx 0/r32/eax
+    # if (inout->is-deref?) inout-type = inout-type->payload
+    8b/-> *(esi+0x10) 0/r32/eax  # Stmt-var-is-deref
+    3d/compare-eax-and 0/imm32/false
+    {
+      74/jump-if-= break/disp8
+      (lookup *(ecx+0xc) *(ecx+0x10))  # Type-tree-right Type-tree-right => eax
+      # if inout-type->right is null, t = inout-type->left
+      81 7/subop/compare *(eax+0xc) 0/imm32  # Type-tree-right
+      {
+        75/jump-if-!= break/disp8
+        (lookup *(eax+4) *(eax+8))  # Type-tree-left Type-tree-left => eax
+      }
+      89/<- %ecx 0/r32/eax
+    }
+    # if output not in register, abort
+    (lookup *edi *(edi+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *(eax+0x18) *(eax+0x1c))  # Var-register Var-register => eax
+    3d/compare-eax-and 0/imm32
+    0f 84/jump-if-= $check-mu-copy-stmt:error-output-not-in-register/disp32
+    # var output-type/eax: (addr type-tree) = output->value->type
+    (lookup *edi *(edi+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *(eax+8) *(eax+0xc))  # Var-type Var-type => eax
+    # if (inout-type == output-type) return
+    (type-match? %eax %ecx %edx)  # => eax
+    3d/compare-eax-and 0/imm32
+    0f 85/jump-if-!= $check-mu-copy-stmt:end/disp32
+    # if output is an addr and inout is 0, return
+    {
+      (lookup *edi *(edi+4))  # Stmt-var-value Stmt-var-value => eax
+      (lookup *(eax+8) *(eax+0xc))  # Var-type Var-type => eax
+      (is-mu-addr-type? %eax)  # => eax
+      3d/compare-eax-and 0/imm32/false
+      74/jump-if-= break/disp8
+      (lookup *esi *(esi+4))  # Stmt-var-value Stmt-var-value => eax
+      (lookup *eax *(eax+4))  # Var-name Var-name => eax
+      (string-equal? %eax "0")  # => eax
+      3d/compare-eax-and 0/imm32/false
+      74/jump-if-= break/disp8
+      e9/jump $check-mu-copy-stmt:end/disp32
+    }
+    # if output is an offset and inout is 0, return
+    {
+      (lookup *edi *(edi+4))  # Stmt-var-value Stmt-var-value => eax
+      (lookup *(eax+8) *(eax+0xc))  # Var-type Var-type => eax
+      81 7/subop/compare *eax 0/imm32/false  # Type-tree-is-atom
+      75/jump-if-!= break/disp8
+      (lookup *(eax+4) *(eax+8))  # Type-tree-left Type-tree-left => eax
+      (is-simple-mu-type? %eax 7)  # offset => eax
+      3d/compare-eax-and 0/imm32/false
+      74/jump-if-= break/disp8
+      (lookup *esi *(esi+4))  # Stmt-var-value Stmt-var-value => eax
+      (lookup *eax *(eax+4))  # Var-name Var-name => eax
+      (string-equal? %eax "0")  # => eax
+      3d/compare-eax-and 0/imm32/false
+      74/jump-if-= break/disp8
+      e9/jump $check-mu-copy-stmt:end/disp32
+    }
+    # if output is a byte, abort if inout is not a literal. Otherwise return.
+    {
+      (lookup *edi *(edi+4))  # Stmt-var-value Stmt-var-value => eax
+      (lookup *(eax+8) *(eax+0xc))  # Var-type Var-type => eax
+      (is-simple-mu-type? %eax 8)  # byte => eax
+      3d/compare-eax-and 0/imm32/false
+      74/jump-if-= break/disp8
+      (lookup *esi *(esi+4))  # Stmt-var-value Stmt-var-value => eax
+      (lookup *(eax+8) *(eax+0xc))  # Var-type Var-type => eax
+      (is-simple-mu-type? %eax 0)  # literal => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 84/jump-if-= $check-mu-copy-stmt:error-non-literal-to-byte/disp32
+      eb/jump $check-mu-copy-stmt:end/disp8
+    }
+    # if output is not number-like, abort
+    (check-mu-numberlike-output %edi *(ebp+8) *(ebp+0xc) *(ebp+0x10) *(ebp+0x14))
+$check-mu-copy-stmt:end:
+    # . reclaim locals
+    81 0/subop/add %esp 0x6c/imm32
+    # . restore registers
+    5f/pop-to-edi
+    5e/pop-to-esi
+    5a/pop-to-edx
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+$check-mu-copy-stmt:error-no-inout:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt 'copy' expects an inout\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-copy-stmt:error-too-many-inouts:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt 'copy' must have just one inout\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-copy-stmt:error-no-output:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt 'copy' expects an output\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-copy-stmt:error-output-not-in-register:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt copy: output '")
+    (lookup *edi *(edi+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *eax *(eax+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "' not in a register\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-copy-stmt:error-too-many-outputs:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt 'copy' must have just one output\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-copy-stmt:error-inout-too-large:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt copy: '")
+    (lookup *esi *(esi+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *eax *(eax+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "' is too large to fit in a register\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-copy-stmt:error-non-literal-to-byte:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt copy: cannot copy non-literal to '")
+    (lookup *edi *(edi+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *eax *(eax+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "' of type byte; use copy-byte\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+check-mu-copy-to-stmt:  # stmt: (addr stmt), fn: (addr function), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    52/push-edx
+    53/push-ebx
+    56/push-esi
+    57/push-edi
+    # var type-parameters/edx: (addr table (handle array byte) (addr type-tree) 8)
+    81 5/subop/subtract %esp 0x60/imm32
+    68/push 0x60/imm32/size
+    68/push 0/imm32/read
+    68/push 0/imm32/write
+    89/<- %edx 4/r32/esp
+    # esi = stmt
+    8b/-> *(ebp+8) 6/r32/esi
+$check-mu-copy-to-stmt:check-for-output:
+    # if stmt->outputs abort
+    (lookup *(esi+0x14) *(esi+0x18))  # Stmt1-outputs Stmt1-outputs => eax
+    3d/compare-eax-and 0/imm32
+    0f 85/jump-if-!= $check-mu-copy-to-stmt:error-too-many-outputs/disp32
+$check-mu-copy-to-stmt:get-dest:
+    # var dest/edi: (addr stmt-var) = stmt->inouts
+    (lookup *(esi+0xc) *(esi+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+    89/<- %edi 0/r32/eax
+    # zero inouts
+    3d/compare-eax-and 0/imm32
+    0f 84/jump-if-= $check-mu-copy-to-stmt:error-incorrect-inouts/disp32
+$check-mu-copy-to-stmt:get-src:
+    # var src/esi: (addr stmt-var) = dest->next
+    (lookup *(edi+8) *(edi+0xc))  # Stmt-var-next Stmt-var-next => eax
+    89/<- %esi 0/r32/eax
+    # 1 inout
+    3d/compare-eax-and 0/imm32
+    0f 84/jump-if-= $check-mu-copy-to-stmt:error-incorrect-inouts/disp32
+    # > 2 inouts
+    (lookup *(esi+8) *(esi+0xc))  # Stmt-var-next Stmt-var-next => eax
+    3d/compare-eax-and 0/imm32
+    0f 85/jump-if-!= $check-mu-copy-to-stmt:error-incorrect-inouts/disp32
+$check-mu-copy-to-stmt:types:
+    # if src is not a scalar, abort
+    (lookup *esi *(esi+4))  # Stmt-var-value Stmt-var-value => eax
+    (size-of %eax)  # => eax
+    3d/compare-eax-and 4/imm32
+    0f 8f/jump-if-> $check-mu-copy-to-stmt:error-src-too-large/disp32
+    # var src-type/ecx: (addr type-tree) = src->value->type
+    (lookup *esi *(esi+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *(eax+8) *(eax+0xc))  # Var-type Var-type => eax
+    89/<- %ecx 0/r32/eax
+    # if src not in register or literal, abort
+    # (we can't use stack-offset because it hasn't been computed yet)
+    {
+      (lookup *esi *(esi+4))  # Stmt-var-value Stmt-var-value => eax
+      (lookup *(eax+0x8) *(eax+0xc))  # Var-type Var-type => eax
+      (is-simple-mu-type? %eax 0)  # => eax
+      3d/compare-eax-and 0/imm32
+      75/jump-if-!= break/disp8
+      (lookup *esi *(esi+4))  # Stmt-var-value Stmt-var-value => eax
+      (lookup *(eax+0x18) *(eax+0x1c))  # Var-register Var-register => eax
+      3d/compare-eax-and 0/imm32
+      75/jump-if-!= break/disp8
+      e9/jump $check-mu-copy-to-stmt:error-src-not-literal-or-in-register/disp32
+    }
+    # var dest-type/ebx: (addr type-tree) = dest->value->type
+    (lookup *edi *(edi+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *(eax+8) *(eax+0xc))  # Var-type Var-type => eax
+    89/<- %ebx 0/r32/eax
+    # if (dest->is-deref?) dest-type = dest-type->payload
+$check-mu-copy-to-stmt:check-dest-deref:
+    8b/-> *(edi+0x10) 0/r32/eax  # Stmt-var-is-deref
+    3d/compare-eax-and 0/imm32/false
+    {
+      74/jump-if-= break/disp8
+      (lookup *(ebx+0xc) *(ebx+0x10))  # Type-tree-right Type-tree-right => eax
+$check-mu-copy-to-stmt:dest-is-deref:
+      # if dest-type->right is null, dest-type = dest-type->left
+      81 7/subop/compare *(eax+0xc) 0/imm32  # Type-tree-right
+      {
+        75/jump-if-!= break/disp8
+$check-mu-copy-to-stmt:dest-is-deref2:
+        (lookup *(eax+4) *(eax+8))  # Type-tree-left Type-tree-left => eax
+      }
+      89/<- %ebx 0/r32/eax
+    }
+    # if dest is a byte and src is not a literal, abort
+    {
+$check-mu-copy-to-stmt:final-check-byte:
+      (is-simple-mu-type? %ebx 8)  # byte => eax
+      3d/compare-eax-and 0/imm32/false
+      74/jump-if-= break/disp8
+      (is-simple-mu-type? %ecx 0)  # literal => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 84/jump-if-= $check-mu-copy-to-stmt:error-non-literal-to-byte/disp32
+    }
+    # if (src-type == dest-type) return
+    (type-match? %ebx %ecx %edx)  # => eax
+    3d/compare-eax-and 0/imm32
+    0f 85/jump-if-!= $check-mu-copy-to-stmt:end/disp32
+    # if dest is an addr and src is 0, return
+    {
+$check-mu-copy-to-stmt:final-check-addr:
+      (is-mu-addr-type? %ebx)  # => eax
+      3d/compare-eax-and 0/imm32/false
+      74/jump-if-= break/disp8
+      (lookup *esi *(esi+4))  # Stmt-var-value Stmt-var-value => eax
+      (lookup *eax *(eax+4))  # Var-name Var-name => eax
+      (string-equal? %eax "0")  # => eax
+      3d/compare-eax-and 0/imm32/false
+      74/jump-if-= break/disp8
+      e9/jump $check-mu-copy-to-stmt:end/disp32
+    }
+    # if dest is an offset and src is 0, return
+    {
+$check-mu-copy-to-stmt:final-check-offset:
+      81 7/subop/compare *ebx 0/imm32/false  # Type-tree-is-atom
+      75/jump-if-!= break/disp8
+      (lookup *(ebx+4) *(ebx+8))  # Type-tree-left Type-tree-left => eax
+      (is-simple-mu-type? %eax 7)  # offset => eax
+      3d/compare-eax-and 0/imm32/false
+      74/jump-if-= break/disp8
+      (lookup *esi *(esi+4))  # Stmt-var-value Stmt-var-value => eax
+      (lookup *eax *(eax+4))  # Var-name Var-name => eax
+      (string-equal? %eax "0")  # => eax
+      3d/compare-eax-and 0/imm32/false
+      74/jump-if-= break/disp8
+      e9/jump $check-mu-copy-to-stmt:end/disp32
+    }
+    # if dest is not number-like, abort
+    (check-mu-numberlike-arg %edi *(ebp+8) *(ebp+0xc) *(ebp+0x10) *(ebp+0x14))
+$check-mu-copy-to-stmt:end:
+    # . reclaim locals
+    81 0/subop/add %esp 0x6c/imm32
+    # . restore registers
+    5f/pop-to-edi
+    5e/pop-to-esi
+    5b/pop-to-ebx
+    5a/pop-to-edx
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+$check-mu-copy-to-stmt:error-incorrect-inouts:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt 'copy-to' must have two inouts\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-copy-to-stmt:error-too-many-outputs:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt 'copy-to' must not have any outputs\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-copy-to-stmt:error-src-not-literal-or-in-register:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt copy-to: source (second inout) is in memory\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-copy-to-stmt:error-src-too-large:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt copy-to: '")
+    (lookup *esi *(esi+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *eax *(eax+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "' is too large to copy\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-copy-to-stmt:error-non-literal-to-byte:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt copy-to: cannot copy non-literal to type byte; use copy-byte-to\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+check-mu-copy-byte-stmt:  # stmt: (addr stmt), fn: (addr function), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    52/push-edx
+    56/push-esi
+    57/push-edi
+    # var type-parameters/edx: (addr table (handle array byte) (addr type-tree) 8)
+    81 5/subop/subtract %esp 0x60/imm32
+    68/push 0x60/imm32/size
+    68/push 0/imm32/read
+    68/push 0/imm32/write
+    89/<- %edx 4/r32/esp
+$check-mu-copy-byte-stmt:get-output:
+    # esi = stmt
+    8b/-> *(ebp+8) 6/r32/esi
+    # var output/edi: (addr stmt-var) = stmt->outputs
+    (lookup *(esi+0x14) *(esi+0x18))  # Stmt1-outputs Stmt1-outputs => eax
+    89/<- %edi 0/r32/eax
+    # zero outputs
+    3d/compare-eax-and 0/imm32
+    0f 84/jump-if-= $check-mu-copy-byte-stmt:error-no-output/disp32
+    # > 1 output
+    (lookup *(edi+8) *(edi+0xc))  # Stmt-var-next Stmt-var-next => eax
+    3d/compare-eax-and 0/imm32
+    0f 85/jump-if-!= $check-mu-copy-byte-stmt:error-too-many-outputs/disp32
+$check-mu-copy-byte-stmt:get-inout:
+    # var inout/esi: (addr stmt-var) = stmt->inouts
+    (lookup *(esi+0xc) *(esi+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+    89/<- %esi 0/r32/eax
+    # zero inouts
+    3d/compare-eax-and 0/imm32
+    0f 84/jump-if-= $check-mu-copy-byte-stmt:error-no-inout/disp32
+    # > 1 inout
+    (lookup *(esi+8) *(esi+0xc))  # Stmt-var-next Stmt-var-next => eax
+    3d/compare-eax-and 0/imm32
+    0f 85/jump-if-!= $check-mu-copy-byte-stmt:error-too-many-inouts/disp32
+$check-mu-copy-byte-stmt:types:
+    # if inout is not a scalar, abort
+    (lookup *esi *(esi+4))  # Stmt-var-value Stmt-var-value => eax
+    (size-of %eax)  # => eax
+    3d/compare-eax-and 4/imm32
+    0f 8f/jump-if-> $check-mu-copy-byte-stmt:error-inout-too-large/disp32
+    # var inout-type/ecx: (addr type-tree) = inout->value->type
+    (lookup *esi *(esi+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *(eax+8) *(eax+0xc))  # Var-type Var-type => eax
+    89/<- %ecx 0/r32/eax
+$check-mu-copy-byte-stmt:check-inout-deref:
+    # if (inout->is-deref?) inout-type = inout-type->payload
+    8b/-> *(esi+0x10) 0/r32/eax  # Stmt-var-is-deref
+    3d/compare-eax-and 0/imm32/false
+    {
+      74/jump-if-= break/disp8
+$check-mu-copy-byte-stmt:inout-is-deref:
+      (lookup *(ecx+0xc) *(ecx+0x10))  # Type-tree-right Type-tree-right => eax
+      # if inout-type->right is null, t = inout-type->left
+      81 7/subop/compare *(eax+0xc) 0/imm32  # Type-tree-right
+      {
+        75/jump-if-!= break/disp8
+$check-mu-copy-byte-stmt:inout-is-deref2:
+        (lookup *(eax+4) *(eax+8))  # Type-tree-left Type-tree-left => eax
+      }
+      89/<- %ecx 0/r32/eax
+    }
+    # if output not in register, abort
+    (lookup *edi *(edi+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *(eax+0x18) *(eax+0x1c))  # Var-register Var-register => eax
+    3d/compare-eax-and 0/imm32
+    0f 84/jump-if-= $check-mu-copy-byte-stmt:error-output-not-in-register/disp32
+    # var output-type/eax: (addr type-tree) = output->value->type
+    (lookup *edi *(edi+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *(eax+8) *(eax+0xc))  # Var-type Var-type => eax
+    # if output is not of type byte, abort
+    (is-simple-mu-type? %eax 8)  # byte => eax
+    3d/compare-eax-and 0/imm32
+    0f 84/jump-if-= $check-mu-copy-byte-stmt:error-invalid-output-type/disp32
+$check-mu-copy-byte-stmt:end:
+    # . reclaim locals
+    81 0/subop/add %esp 0x6c/imm32
+    # . restore registers
+    5f/pop-to-edi
+    5e/pop-to-esi
+    5a/pop-to-edx
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+$check-mu-copy-byte-stmt:error-no-inout:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt 'copy-byte' expects an inout\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-copy-byte-stmt:error-too-many-inouts:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt 'copy-byte' must have just one inout\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-copy-byte-stmt:error-no-output:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt 'copy-byte' expects an output\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-copy-byte-stmt:error-output-not-in-register:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt copy-byte: output '")
+    (lookup *edi *(edi+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *eax *(eax+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "' not in a register\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-copy-byte-stmt:error-too-many-outputs:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt 'copy-byte' must have just one output\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-copy-byte-stmt:error-invalid-output-type:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt 'copy-byte' must write to output of type byte\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-copy-byte-stmt:error-inout-too-large:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt copy-byte: '")
+    (lookup *esi *(esi+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *eax *(eax+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "' is too large to fit in a register\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+check-mu-copy-byte-to-stmt:  # stmt: (addr stmt), fn: (addr function), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    52/push-edx
+    53/push-ebx
+    56/push-esi
+    57/push-edi
+    # var type-parameters/edx: (addr table (handle array byte) (addr type-tree) 8)
+    81 5/subop/subtract %esp 0x60/imm32
+    68/push 0x60/imm32/size
+    68/push 0/imm32/read
+    68/push 0/imm32/write
+    89/<- %edx 4/r32/esp
+    # esi = stmt
+    8b/-> *(ebp+8) 6/r32/esi
+$check-mu-copy-byte-to-stmt:check-for-output:
+    # if stmt->outputs abort
+    (lookup *(esi+0x14) *(esi+0x18))  # Stmt1-outputs Stmt1-outputs => eax
+    3d/compare-eax-and 0/imm32
+    0f 85/jump-if-!= $check-mu-copy-byte-to-stmt:error-too-many-outputs/disp32
+$check-mu-copy-byte-to-stmt:get-dest:
+    # var dest/edi: (addr stmt-var) = stmt->inouts
+    (lookup *(esi+0xc) *(esi+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+    89/<- %edi 0/r32/eax
+    # zero inouts
+    3d/compare-eax-and 0/imm32
+    0f 84/jump-if-= $check-mu-copy-byte-to-stmt:error-incorrect-inouts/disp32
+$check-mu-copy-byte-to-stmt:get-src:
+    # var src/esi: (addr stmt-var) = dest->next
+    (lookup *(edi+8) *(edi+0xc))  # Stmt-var-next Stmt-var-next => eax
+    89/<- %esi 0/r32/eax
+    # 1 inout
+    3d/compare-eax-and 0/imm32
+    0f 84/jump-if-= $check-mu-copy-byte-to-stmt:error-incorrect-inouts/disp32
+    # > 2 inouts
+    (lookup *(esi+8) *(esi+0xc))  # Stmt-var-next Stmt-var-next => eax
+    3d/compare-eax-and 0/imm32
+    0f 85/jump-if-!= $check-mu-copy-byte-to-stmt:error-incorrect-inouts/disp32
+$check-mu-copy-byte-to-stmt:types:
+    # if src is not a scalar, abort
+    (lookup *esi *(esi+4))  # Stmt-var-value Stmt-var-value => eax
+    (size-of %eax)  # => eax
+    3d/compare-eax-and 4/imm32
+    0f 8f/jump-if-> $check-mu-copy-byte-to-stmt:error-src-too-large/disp32
+    # if src not in register, abort
+    {
+      (lookup *esi *(esi+4))  # Stmt-var-value Stmt-var-value => eax
+      (lookup *(eax+0x18) *(eax+0x1c))  # Var-register Var-register => eax
+      3d/compare-eax-and 0/imm32
+      75/jump-if-!= break/disp8
+      e9/jump $check-mu-copy-byte-to-stmt:error-src-not-in-register/disp32
+    }
+    # var dest-type/ebx: (addr type-tree) = dest->value->type
+    (lookup *edi *(edi+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *(eax+8) *(eax+0xc))  # Var-type Var-type => eax
+    89/<- %ebx 0/r32/eax
+    # if (dest->is-deref?) dest-type = dest-type->payload
+$check-mu-copy-byte-to-stmt:check-dest-deref:
+    8b/-> *(edi+0x10) 0/r32/eax  # Stmt-var-is-deref
+    3d/compare-eax-and 0/imm32/false
+    {
+      74/jump-if-= break/disp8
+      (lookup *(ebx+0xc) *(ebx+0x10))  # Type-tree-right Type-tree-right => eax
+$check-mu-copy-byte-to-stmt:dest-is-deref:
+      # if dest-type->right is null, dest-type = dest-type->left
+      81 7/subop/compare *(eax+0xc) 0/imm32  # Type-tree-right
+      {
+        75/jump-if-!= break/disp8
+$check-mu-copy-byte-to-stmt:dest-is-deref2:
+        (lookup *(eax+4) *(eax+8))  # Type-tree-left Type-tree-left => eax
+      }
+      89/<- %ebx 0/r32/eax
+    }
+    # if dest is not a byte, abort
+    (is-simple-mu-type? %ebx 8)  # byte => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 84/jump-if-= $check-mu-copy-byte-to-stmt:error-invalid-dest-type/disp32
+$check-mu-copy-byte-to-stmt:end:
+    # . reclaim locals
+    81 0/subop/add %esp 0x6c/imm32
+    # . restore registers
+    5f/pop-to-edi
+    5e/pop-to-esi
+    5b/pop-to-ebx
+    5a/pop-to-edx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+$check-mu-copy-byte-to-stmt:error-incorrect-inouts:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt 'copy-byte-to' must have two inouts\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-copy-byte-to-stmt:error-too-many-outputs:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt 'copy-byte-to' must not have any outputs\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-copy-byte-to-stmt:error-src-not-in-register:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt copy-byte-to: source (second inout) must be in a register\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-copy-byte-to-stmt:error-invalid-dest-type:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt copy-byte-to: '")
+    (lookup *edi *(edi+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *eax *(eax+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "' must be a byte\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-copy-byte-to-stmt:error-src-too-large:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt copy-byte-to: '")
+    (lookup *esi *(esi+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *eax *(eax+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "' is too large to copy\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+check-mu-compare-stmt:  # stmt: (addr stmt), fn: (addr function), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    52/push-edx
+    53/push-ebx
+    56/push-esi
+    57/push-edi
+    # var type-parameters/edx: (addr table (handle array byte) (addr type-tree) 8)
+    81 5/subop/subtract %esp 0x60/imm32
+    68/push 0x60/imm32/size
+    68/push 0/imm32/read
+    68/push 0/imm32/write
+    89/<- %edx 4/r32/esp
+    # esi = stmt
+    8b/-> *(ebp+8) 6/r32/esi
+$check-mu-compare-stmt:check-for-output:
+    # if stmt->outputs abort
+    (lookup *(esi+0x14) *(esi+0x18))  # Stmt1-outputs Stmt1-outputs => eax
+    3d/compare-eax-and 0/imm32
+    0f 85/jump-if-!= $check-mu-compare-stmt:error-too-many-outputs/disp32
+$check-mu-compare-stmt:get-left:
+    # var left/edi: (addr stmt-var) = stmt->inouts
+    (lookup *(esi+0xc) *(esi+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+    89/<- %edi 0/r32/eax
+    # zero inouts
+    3d/compare-eax-and 0/imm32
+    0f 84/jump-if-= $check-mu-compare-stmt:error-incorrect-inouts/disp32
+$check-mu-compare-stmt:get-right:
+    # var right/esi: (addr stmt-var) = left->next
+    (lookup *(edi+8) *(edi+0xc))  # Stmt-var-next Stmt-var-next => eax
+    89/<- %esi 0/r32/eax
+    # 1 inout
+    3d/compare-eax-and 0/imm32
+    0f 84/jump-if-= $check-mu-compare-stmt:error-incorrect-inouts/disp32
+    # > 2 inouts
+    (lookup *(esi+8) *(esi+0xc))  # Stmt-var-next Stmt-var-next => eax
+    3d/compare-eax-and 0/imm32
+    0f 85/jump-if-!= $check-mu-compare-stmt:error-incorrect-inouts/disp32
+    # if both inouts are in memory, abort
+    {
+$check-mu-compare-stmt:both-in-mem:
+      (lookup *esi *(esi+4))  # Stmt-var-value Stmt-var-value => eax
+      (lookup *(eax+0x8) *(eax+0xc))  # Var-type Var-type => eax
+      (is-simple-mu-type? %eax 0)  # => eax
+      3d/compare-eax-and 0/imm32
+      0f 85/jump-if-!= break/disp32
+      (lookup *esi *(esi+4))  # Stmt-var-value Stmt-var-value => eax
+      (lookup *(eax+0x18) *(eax+0x1c))  # Var-register Var-register => eax
+      3d/compare-eax-and 0/imm32
+      75/jump-if-!= break/disp8
+      (lookup *edi *(edi+4))  # Stmt-var-value Stmt-var-value => eax
+      (lookup *(eax+0x8) *(eax+0xc))  # Var-type Var-type => eax
+      (is-simple-mu-type? %eax 0)  # => eax
+      3d/compare-eax-and 0/imm32
+      75/jump-if-!= break/disp8
+      (lookup *edi *(edi+4))  # Stmt-var-value Stmt-var-value => eax
+      (lookup *(eax+0x18) *(eax+0x1c))  # Var-register Var-register => eax
+      3d/compare-eax-and 0/imm32
+      75/jump-if-!= break/disp8
+      e9/jump $check-mu-compare-stmt:error-both-in-memory/disp32
+    }
+$check-mu-compare-stmt:types:
+    # var right-type/ecx: (addr type-tree) = right->value->type
+    (lookup *esi *(esi+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *(eax+8) *(eax+0xc))  # Var-type Var-type => eax
+    89/<- %ecx 0/r32/eax
+    # if (right->is-deref?) right-type = right-type->payload
+    8b/-> *(esi+0x10) 0/r32/eax  # Stmt-var-is-deref
+    3d/compare-eax-and 0/imm32/false
+    {
+      74/jump-if-= break/disp8
+      (lookup *(ecx+0xc) *(ecx+0x10))  # Type-tree-right Type-tree-right => eax
+      # if right-type->right is null, right-type = right-type->left
+      81 7/subop/compare *(eax+0xc) 0/imm32  # Type-tree-right
+      {
+        75/jump-if-!= break/disp8
+        (lookup *(eax+4) *(eax+8))  # Type-tree-left Type-tree-left => eax
+      }
+      89/<- %ecx 0/r32/eax
+    }
+    # if right-type is a literal string, abort
+    (is-simple-mu-type? %ecx 0x10)  # string-literal => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 85/jump-if-!= $check-mu-compare-stmt:error-right-string-literal/disp32
+    # if right is not a scalar, abort
+    (lookup *esi *(esi+4))  # Stmt-var-value Stmt-var-value => eax
+    (size-of %eax)  # => eax
+    3d/compare-eax-and 4/imm32
+    0f 8f/jump-if-> $check-mu-compare-stmt:error-right-too-large/disp32
+    # if left is not a scalar, abort
+    (lookup *edi *(edi+4))  # Stmt-var-value Stmt-var-value => eax
+    (size-of %eax)  # => eax
+    3d/compare-eax-and 4/imm32
+    0f 8f/jump-if-> $check-mu-compare-stmt:error-left-too-large/disp32
+    # var left-type/ebx: (addr type-tree) = left->value->type
+    (lookup *edi *(edi+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *(eax+8) *(eax+0xc))  # Var-type Var-type => eax
+    89/<- %ebx 0/r32/eax
+    # if (left->is-deref?) left-type = left-type->payload
+    8b/-> *(edi+0x10) 0/r32/eax  # Stmt-var-is-deref
+    3d/compare-eax-and 0/imm32/false
+    {
+      74/jump-if-= break/disp8
+      (lookup *(ebx+0xc) *(ebx+0x10))  # Type-tree-right Type-tree-right => eax
+      # if left-type->right is null, left-type = left-type->left
+      81 7/subop/compare *(eax+0xc) 0/imm32  # Type-tree-right
+      {
+        75/jump-if-!= break/disp8
+        (lookup *(eax+4) *(eax+8))  # Type-tree-left Type-tree-left => eax
+      }
+      89/<- %ebx 0/r32/eax
+    }
+    # if (left-type == right-type) return
+    (type-match? %ebx %ecx %edx)  # => eax
+    3d/compare-eax-and 0/imm32
+    0f 85/jump-if-!= $check-mu-compare-stmt:end/disp32
+    # if left is an addr and right is 0, return
+    {
+      (lookup *edi *(edi+4))  # Stmt-var-value Stmt-var-value => eax
+      (lookup *(eax+8) *(eax+0xc))  # Var-type Var-type => eax
+      (is-mu-addr-type? %eax)  # => eax
+      3d/compare-eax-and 0/imm32/false
+      74/jump-if-= break/disp8
+      (lookup *esi *(esi+4))  # Stmt-var-value Stmt-var-value => eax
+      (lookup *eax *(eax+4))  # Var-name Var-name => eax
+      (string-equal? %eax "0")  # => eax
+      3d/compare-eax-and 0/imm32/false
+      74/jump-if-= break/disp8
+      eb/jump $check-mu-compare-stmt:end/disp8
+    }
+    # if left is not number-like, abort
+    (check-mu-numberlike-arg %edi *(ebp+8) *(ebp+0xc) *(ebp+0x10) *(ebp+0x14))
+$check-mu-compare-stmt:end:
+    # . reclaim locals
+    81 0/subop/add %esp 0x6c/imm32
+    # . restore registers
+    5f/pop-to-edi
+    5e/pop-to-esi
+    5b/pop-to-ebx
+    5a/pop-to-edx
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+$check-mu-compare-stmt:error-incorrect-inouts:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt 'compare' must have two inouts\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-compare-stmt:error-too-many-outputs:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt 'compare' must not have any outputs\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-compare-stmt:error-both-in-memory:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt compare: both inouts are in memory\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-compare-stmt:error-left-too-large:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt compare: '")
+    (lookup *edi *(edi+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *eax *(eax+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "' is too large to compare\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-compare-stmt:error-right-too-large:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt compare: '")
+    (lookup *esi *(esi+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *eax *(eax+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "' is too large to compare\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-compare-stmt:error-right-string-literal:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt compare: string literal ")
+    (lookup *esi *(esi+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *eax *(eax+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) " is not supported; use the string-equal? function\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+check-mu-address-stmt:  # stmt: (addr stmt), fn: (addr function), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    52/push-edx
+    56/push-esi
+    57/push-edi
+$check-mu-address-stmt:get-output:
+    # esi = stmt
+    8b/-> *(ebp+8) 6/r32/esi
+    # var output/edi: (addr stmt-var) = stmt->outputs
+    (lookup *(esi+0x14) *(esi+0x18))  # Stmt1-outputs Stmt1-outputs => eax
+    89/<- %edi 0/r32/eax
+    # zero outputs
+    3d/compare-eax-and 0/imm32
+    0f 84/jump-if-= $check-mu-address-stmt:error-no-output/disp32
+    # > 1 output
+    (lookup *(edi+8) *(edi+0xc))  # Stmt-var-next Stmt-var-next => eax
+    3d/compare-eax-and 0/imm32
+    0f 85/jump-if-!= $check-mu-address-stmt:error-too-many-outputs/disp32
+$check-mu-address-stmt:get-inout:
+    # var inout/esi: (addr stmt-var) = stmt->inouts
+    (lookup *(esi+0xc) *(esi+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+    89/<- %esi 0/r32/eax
+    # zero inouts
+    3d/compare-eax-and 0/imm32
+    0f 84/jump-if-= $check-mu-address-stmt:error-no-inout/disp32
+    # > 1 inout
+    (lookup *(esi+8) *(esi+0xc))  # Stmt-var-next Stmt-var-next => eax
+    3d/compare-eax-and 0/imm32
+    0f 85/jump-if-!= $check-mu-address-stmt:error-too-many-inouts/disp32
+$check-mu-address-stmt:types:
+    # if output not in register, abort
+    (lookup *edi *(edi+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *(eax+0x18) *(eax+0x1c))  # Var-register Var-register => eax
+    3d/compare-eax-and 0/imm32
+    0f 84/jump-if-= $check-mu-address-stmt:error-output-not-in-register/disp32
+    # var output-type/edx: (addr type-tree) = output->value->type
+    (lookup *edi *(edi+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *(eax+8) *(eax+0xc))  # Var-type Var-type => eax
+    89/<- %edx 0/r32/eax
+    # if output-type not an addr, abort
+    (is-mu-addr-type? %edx)  # => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 84/jump-if-= $check-mu-address-stmt:error-output-not-address/disp32
+    # output-type = output-type->right
+    (lookup *(edx+0xc) *(edx+0x10))  # Type-tree-right Type-tree-right => eax
+    # if output-type->right is null, output-type = output-type->left
+    81 7/subop/compare *(eax+0xc) 0/imm32  # Type-tree-right
+    {
+      75/jump-if-!= break/disp8
+      (lookup *(eax+4) *(eax+8))  # Type-tree-left Type-tree-left => eax
+    }
+    89/<- %edx 0/r32/eax
+    # var inout-type/ecx: (addr type-tree) = inout->value->type
+    (lookup *esi *(esi+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *(eax+8) *(eax+0xc))  # Var-type Var-type => eax
+    89/<- %ecx 0/r32/eax
+    # if (inout->is-deref?) inout-type = inout-type->payload
+    8b/-> *(esi+0x10) 0/r32/eax  # Stmt-var-is-deref
+    3d/compare-eax-and 0/imm32/false
+    {
+      74/jump-if-= break/disp8
+      (lookup *(ecx+0xc) *(ecx+0x10))  # Type-tree-right Type-tree-right => eax
+      # if inout-type->right is null, t = inout-type->left
+      81 7/subop/compare *(eax+0xc) 0/imm32  # Type-tree-right
+      {
+        75/jump-if-!= break/disp8
+        (lookup *(eax+4) *(eax+8))  # Type-tree-left Type-tree-left => eax
+      }
+      89/<- %ecx 0/r32/eax
+    }
+    # if (inout-type != output-type) abort
+    (type-equal-ignoring-capacity? %edx %ecx)  # => eax
+    3d/compare-eax-and 0/imm32
+    0f 84/jump-if-= $check-mu-address-stmt:error-type-mismatch/disp32
+$check-mu-address-stmt:end:
+    # . restore registers
+    5f/pop-to-edi
+    5e/pop-to-esi
+    5a/pop-to-edx
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+$check-mu-address-stmt:error-no-inout:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt 'address' expects an inout\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-address-stmt:error-too-many-inouts:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt 'address' must have just one inout\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-address-stmt:error-no-output:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt 'address' expects an output\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-address-stmt:error-output-not-in-register:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt address: output '")
+    (lookup *edi *(edi+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *eax *(eax+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "' not in a register\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-address-stmt:error-too-many-outputs:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt 'address' must have just one output\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-address-stmt:error-output-not-address:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt address: output '")
+    (lookup *edi *(edi+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *eax *(eax+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "' is not an addr\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-address-stmt:error-type-mismatch:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt address: output '")
+    (lookup *edi *(edi+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *eax *(eax+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "' cannot hold address of '")
+    (lookup *esi *(esi+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *eax *(eax+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "'\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+type-equal-ignoring-capacity?:  # a: (addr type-tree), b: (addr type-tree) -> result/eax: boolean
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    51/push-ecx
+    52/push-edx
+    53/push-ebx
+    # var curr-a/ecx: (addr type-tree) = a
+    8b/-> *(ebp+8) 1/r32/ecx
+    # var curr-b/ebx: (addr type-tree) = b
+    8b/-> *(ebp+0xc) 3/r32/ebx
+    # if (curr-a->is-atom?) fall back to regular equality
+    81 7/subop/compare *ecx 0/imm32/false  # Type-tree-is-atom
+    0f 85/jump-if-!= $type-equal-ignoring-capacity?:base-case/disp32
+    # if (curr-a->left != curr-b->left) return false
+    (lookup *(ecx+4) *(ecx+8))  # Type-tree-left Type-tree-left => eax
+    89/<- %edx 0/r32/eax
+    (lookup *(ebx+4) *(ebx+8))  # Type-tree-left Type-tree-left => eax
+    (type-equal? %edx %eax)  # => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 84/jump-if-= $type-equal-ignoring-capacity?:end/disp32  # eax switches meaning
+    # if (curr-a->left == "array") curr-a = curr-a->element-type
+    {
+      (is-mu-array? %edx)  # => eax
+      3d/compare-eax-and 0/imm32/false
+      75/jump-if-!= break/disp8
+$type-equal-ignoring-capacity?:array:
+      # curr-a = curr-a->right->left
+      (lookup *(ecx+0xc) *(ecx+0x10))  # Type-tree-right Type-tree-right => eax
+      (lookup *(eax+4) *(eax+8))  # Type-tree-left Type-tree-left => eax
+      89/<- %ecx 0/r32/eax
+      # curr-b = curr-b->right->left
+      (lookup *(ebx+0xc) *(ebx+0x10))  # Type-tree-right Type-tree-right => eax
+      (lookup *(eax+4) *(eax+8))  # Type-tree-left Type-tree-left => eax
+      89/<- %ebx 0/r32/eax
+      eb/jump $type-equal-ignoring-capacity?:base-case/disp8
+    }
+    # if (curr-a->left == "stream") curr-a = curr-a->element-type
+    {
+      (is-mu-stream? %edx)  # => eax
+      3d/compare-eax-and 0/imm32/false
+      75/jump-if-!= break/disp8
+$type-equal-ignoring-capacity?:stream:
+      # curr-a = curr-a->right->left
+      (lookup *(ecx+0xc) *(ecx+0x10))  # Type-tree-right Type-tree-right => eax
+      (lookup *(eax+4) *(eax+8))  # Type-tree-left Type-tree-left => eax
+      89/<- %ecx 0/r32/eax
+      # curr-b = curr-b->right->left
+      (lookup *(ebx+0xc) *(ebx+0x10))  # Type-tree-right Type-tree-right => eax
+      (lookup *(eax+4) *(eax+8))  # Type-tree-left Type-tree-left => eax
+      89/<- %ebx 0/r32/eax
+      eb/jump $type-equal-ignoring-capacity?:base-case/disp8
+    }
+$type-equal-ignoring-capacity?:base-case:
+    # return type-equal?(curr-a, curr-b)
+    (type-equal? %ecx %ebx)  # => eax
+$type-equal-ignoring-capacity?:end:
+    # . restore registers
+    5b/pop-to-ebx
+    5a/pop-to-edx
+    59/pop-to-ecx
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+check-mu-return-stmt:  # stmt: (addr stmt), fn: (addr function), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    52/push-edx
+    53/push-ebx
+    56/push-esi
+    57/push-edi
+    # var type-parameters/edx: (addr table (handle array byte) (addr type-tree) 8)
+    81 5/subop/subtract %esp 0x60/imm32
+    68/push 0x60/imm32/size
+    68/push 0/imm32/read
+    68/push 0/imm32/write
+    89/<- %edx 4/r32/esp
+    # var template/esi: (addr list var) = fn->outputs
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *(eax+0x10) *(eax+0x14))  # Function-outputs Function-outputs => eax
+    89/<- %esi 0/r32/eax
+    # var curr-template/ebx: (addr list var) = fn->outputs
+    89/<- %ebx 0/r32/eax
+    # var curr/edi: (addr stmt-var) = stmt->inouts
+    8b/-> *(ebp+8) 0/r32/eax
+    (lookup *(eax+0xc) *(eax+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+    89/<- %edi 0/r32/eax
+    {
+      # if template is null, break
+      81 7/subop/compare %ebx 0/imm32
+      0f 84/jump-if-= break/disp32
+      # if curr is null, abort
+      81 7/subop/compare %edi 0/imm32
+      0f 84/jump-if-= $check-mu-return-stmt:error-too-few-inouts/disp32
+      # var template-type/ecx: (addr type-tree) = template->value->type
+      (lookup *ebx *(ebx+4))  # List-value List-value => eax
+      (lookup *(eax+8) *(eax+0xc))  # Var-type Var-type => eax
+      89/<- %ecx 0/r32/eax
+      # var curr-type/eax: (addr type-tree) = curr->value->type
+      (lookup *edi *(edi+4))  # Stmt-var-value Stmt-var-value => eax
+      (lookup *(eax+8) *(eax+0xc))  # Var-type Var-type => eax
+      # if (curr->is-deref?) curr-type = payload of curr-type
+      81 7/subop/compare *(edi+0x10) 0/imm32/false  # Stmt-var-is-deref
+      {
+        74/jump-if-= break/disp8
+        (lookup *(eax+0xc) *(eax+0x10))  # Type-tree-right Type-tree-right => eax
+        # if t->right is null, t = t->left
+        81 7/subop/compare *(eax+0xc) 0/imm32  # Type-tree-right
+        75/jump-if-!= break/disp8
+        (lookup *(eax+4) *(eax+8))  # Type-tree-left Type-tree-left => eax
+      }
+      # if curr-type is literal and template-type is float, abort
+      50/push-eax
+      {
+        (is-simple-mu-type? %eax 0)  # literal => eax
+        3d/compare-eax-and 0/imm32/false
+        74/jump-if-= break/disp8
+        (is-simple-mu-type? %ecx 0xf)  # float => eax
+        3d/compare-eax-and 0/imm32/false
+        0f 85/jump-if-!= $check-mu-return-stmt:error-literal-to-float/disp32
+      }
+      58/pop-to-eax
+      # if (curr-type != template-type) abort
+      (type-match? %ecx %eax %edx)  # => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 84/jump-if-= $check-mu-return-stmt:error1/disp32
+      # if register-within-list-with-conflict?(curr, original template, curr-template, stmt) abort
+      (register-within-list-with-conflict? %edi %esi %ebx *(ebp+8))  # => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 85/jump-if-!= $check-mu-return-stmt:error2/disp32
+      # template = template->next
+      (lookup *(ebx+8) *(ebx+0xc))  # List-next List-next => eax
+      89/<- %ebx 0/r32/eax
+      # curr = curr->next
+      (lookup *(edi+8) *(edi+0xc))  # Stmt-var-next Stmt-var-next => eax
+      89/<- %edi 0/r32/eax
+      #
+      e9/jump loop/disp32
+    }
+    # if curr is not null, abort
+    81 7/subop/compare %edi 0/imm32
+    0f 85/jump-if-!= $check-mu-return-stmt:error-too-many-inouts/disp32
+$check-mu-return-stmt:end:
+    # . reclaim locals
+    81 0/subop/add %esp 0x6c/imm32
+    # . restore registers
+    5f/pop-to-edi
+    5e/pop-to-esi
+    5b/pop-to-ebx
+    5a/pop-to-edx
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+$check-mu-return-stmt:error1:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": return: '")
+    (lookup *edi *(edi+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *eax *(eax+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "' has the wrong type\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-return-stmt:error2:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": return: '")
+    (lookup *edi *(edi+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *eax *(eax+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "' is no longer available\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-return-stmt:error-literal-to-float:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": return: cannot copy literal '")
+    (lookup *edi *(edi+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *eax *(eax+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "' to float\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-return-stmt:error-too-few-inouts:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": return: too few inouts\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-return-stmt:error-too-many-inouts:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": return: too many inouts\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+check-all-unique-registers:  # outputs: (addr list var), fn: (addr function), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    56/push-esi
+    # var table/esi: (addr table (handle array byte) int 8)
+    81 5/subop/subtract %esp 0x60/imm32
+    68/push 0x60/imm32/size
+    68/push 0/imm32/read
+    68/push 0/imm32/write
+    89/<- %esi 4/r32/esp
+    # var curr/ecx: (addr list var) = outputs
+    8b/-> *(ebp+8) 1/r32/ecx
+    {
+      # if (curr == 0) break
+      81 7/subop/compare %ecx 0/imm32
+      0f 84/jump-if-= break/disp32
+      # var reg/eax: (addr array byte) = curr->value->register  # guaranteed to exist
+      (lookup *ecx *(ecx+4))  # List-value List-value => eax
+      (lookup *(eax+0x18) *(eax+0x1c))  # Var-register Var-register => eax
+      # if reg exists in table, abort
+      (maybe-get %esi %eax 0xc)  # => eax
+      3d/compare-eax-and 0/imm32
+      0f 85/jump-if-!= $check-all-unique-registers:abort/disp32
+      # insert reg in table
+      (lookup *ecx *(ecx+4))  # List-value List-value => eax
+      (lookup *(eax+0x18) *(eax+0x1c))  # Var-register Var-register => eax
+      (get-or-insert %esi %eax 0xc Heap)
+      # curr = curr->next
+      (lookup *(ecx+8) *(ecx+0xc))  # List-next List-next => eax
+      89/<- %ecx 0/r32/eax
+      e9/jump loop/disp32
+    }
+$check-all-unique-registers:end:
+    # . reclaim locals
+    81 0/subop/add %esp 0x6c/imm32
+    # . restore registers
+    5e/pop-to-esi
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+$check-all-unique-registers:abort:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": outputs must be in unique registers\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+# return false if s's register is not between start (inclusive) and end (exclusive)
+# return false if the positionally corresponding register in stmt->inouts (where s comes from) is also s's register
+# otherwise return true
+register-within-list-with-conflict?:  # s: (addr stmt-var), start: (addr list var), end: (addr list var), stmt: (addr stmt) -> result/eax: boolean
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    51/push-ecx
+    52/push-edx
+    53/push-ebx
+    56/push-esi
+    57/push-edi
+    # var target/ebx: (addr array byte) = s->value->register
+    8b/-> *(ebp+8) 0/r32/eax
+    (lookup *eax *(eax+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *(eax+0x18) *(eax+0x1c))  # Var-register Var-register => eax
+#?     (write-buffered Stderr "AA: ")
+#?     (write-buffered Stderr %eax)
+#?     (write-buffered Stderr Newline)
+#?     (flush Stderr)
+    # if (var->register == 0) return false
+    3d/compare-eax-and 0/imm32
+    0f 84/jump-if-= $register-within-list-with-conflict?:end/disp32  # eax turns into result
+    89/<- %ebx 0/r32/eax
+    # var curr/ecx: (addr list var) = start
+    8b/-> *(ebp+0xc) 1/r32/ecx
+    # edx = end
+    8b/-> *(ebp+0x10) 2/r32/edx
+    {
+      # if (curr == 0) break
+      81 7/subop/compare %edi 0/imm32
+      0f 84/jump-if-= break/disp32
+      # if (curr == end) break
+      39/compare %ecx 2/r32/edx
+      0f 84/jump-if-= break/disp32
+      # var curr-reg/eax: (addr array byte) = curr->value->register
+      (lookup *ecx *(ecx+4))  # List-value List-value => eax
+      (lookup *(eax+0x18) *(eax+0x1c))  # Var-register Var-register => eax
+      # if (curr-reg == 0) continue
+      3d/compare-eax-and 0/imm32
+      74/jump-if-= $register-within-list-with-conflict?:continue/disp8
+      # if (curr-reg == target) check for conflict
+      (string-equal? %eax %ebx)  # => eax
+      3d/compare-eax-and 0/imm32/false
+      {
+        74/jump-if-= break/disp8
+#?         (write-buffered Stderr "conflict?\n")
+#?         (flush Stderr)
+        # var return-inouts/eax: (addr stmt-var) = stmt->inouts
+        8b/-> *(ebp+0x14) 0/r32/eax
+        (lookup *(eax+0xc) *(eax+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+        (register-conflict? %ebx %eax *(ebp+0xc))  # => eax
+        eb/jump $register-within-list-with-conflict?:end/disp8
+      }
+$register-within-list-with-conflict?:continue:
+      # curr = curr->next
+      (lookup *(ecx+8) *(ecx+0xc))  # List-next List-next => eax
+      89/<- %ecx 0/r32/eax
+      e9/jump loop/disp32
+    }
+    # return false
+    b8/copy-to-eax 0/imm32/false
+$register-within-list-with-conflict?:end:
+    # . restore registers
+    5f/pop-to-edi
+    5e/pop-to-esi
+    5b/pop-to-ebx
+    5a/pop-to-edx
+    59/pop-to-ecx
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+# At the first occurrence of register 'reg' in fn-outputs,
+# check if the corresponding element of return-inouts has a different register.
+# This hacky helper is intended to be called in one specific place. Don't
+# reuse it as is.
+register-conflict?:  # reg: (addr array byte), return-inouts: (addr stmt-var), fn-outputs: (addr list var) => result/eax: boolean
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    51/push-ecx
+    52/push-edx
+    53/push-ebx
+    56/push-esi
+    57/push-edi
+#?     (write-buffered Stderr "BB: ")
+#?     (write-buffered Stderr *(ebp+8))
+#?     (write-buffered Stderr Newline)
+#?     (flush Stderr)
+    # var curr-output/edi: (addr list var) = fn-outputs
+    8b/-> *(ebp+0x10) 7/r32/edi
+    # var curr-inout/esi: (addr stmt-var) = return-inouts
+    8b/-> *(ebp+0xc) 6/r32/esi
+    {
+      # if (curr-output == 0) abort
+      81 7/subop/compare %edi 0/imm32
+      0f 84/jump-if-= break/disp32
+      # if (curr-output->value->register != reg) continue
+      (lookup *edi *(edi+4))  # List-value List-value => eax
+      (lookup *(eax+0x18) *(eax+0x1c))  # Var-register Var-register => eax
+      (string-equal? %eax *(ebp+8))  # => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 84/jump-if= $register-conflict?:continue/disp32
+#?       (write-buffered Stderr "rescan\n")
+#?       (flush Stderr)
+      # var curr-reg/eax: (addr array byte) = curr-inout->value->register
+      (lookup *esi *(esi+4))  # List-value List-value => eax
+      (lookup *(eax+0x18) *(eax+0x1c))  # Var-register Var-register => eax
+      # if (curr-reg == 0) return true
+      3d/compare-eax-and 0/imm32
+      {
+        75/jump-if-!= break/disp8
+#?         (write-buffered Stderr "no register\n")
+#?         (flush Stderr)
+        b8/copy-to-eax 1/imm32/true
+        e9/jump $register-conflict?:end/disp32
+      }
+      # return (curr-reg != reg)
+      (string-equal? %eax *(ebp+8))  # => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 94/set-if-= %al
+#?       (write-buffered Stderr "final: ")
+#?       (write-int32-hex-buffered Stderr %eax)
+#?       (write-buffered Stderr Newline)
+#?       (flush Stderr)
+      eb/jump $register-conflict?:end/disp8
+$register-conflict?:continue:
+      # curr-output = curr-output->next
+      (lookup *(edi+8) *(edi+0xc))  # List-next List-next => eax
+      89/<- %edi 0/r32/eax
+      # curr-inout = curr-inout->next
+      (lookup *(esi+8) *(esi+0xc))  # Stmt-var-next Stmt-var-next => eax
+      89/<- %esi 0/r32/eax
+      e9/jump loop/disp32
+    }
+    # should never get here
+    (write-buffered Stderr "register-conflict? misused\n")
+    (flush Stderr)
+    e8/call syscall_exit/disp32
+$register-conflict?:end:
+    # . restore registers
+    5f/pop-to-edi
+    5e/pop-to-esi
+    5b/pop-to-ebx
+    5a/pop-to-edx
+    59/pop-to-ecx
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+check-final-stmt-is-return:  # block: (addr block), fn: (addr function), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    # var curr/ecx: (addr list stmt) = block->stmts
+    8b/-> *(ebp+8) 0/r32/eax
+    (lookup *(eax+4) *(eax+8))  # Block-stmts Block-stmts => eax
+    3d/compare-eax-and 0/imm32
+    74/jump-if-= $check-final-stmt-is-return:error/disp8
+    89/<- %ecx 0/r32/eax
+    {
+      # if curr->next == 0, break
+      (lookup *(ecx+8) *(ecx+0xc))  # List-next List-next => eax
+      3d/compare-eax-and 0/imm32
+      74/jump-if-= break/disp8
+      # curr = curr->next
+      89/<- %ecx 0/r32/eax
+      e9/jump loop/disp32
+    }
+$check-final-stmt-is-return:check-tag:
+    # if curr->value->tag != Stmt1, abort
+    (lookup *ecx *(ecx+4))  # List-value List-value => eax
+    81 7/subop/compare *eax 1/imm32/stmt1  # Stmt-tag
+    75/jump-if-!= $check-final-stmt-is-return:error/disp8
+$check-final-stmt-is-return:check-operation:
+    # if curr->operation != "return", abort
+    (lookup *(eax+4) *(eax+8))  # Stmt1-operation Stmt1-operation => eax
+    (string-equal? %eax "return")
+    3d/compare-eax-and 0/imm32/false
+    74/jump-if-= $check-final-stmt-is-return:error/disp8
+$check-final-stmt-is-return:end:
+    # . restore registers
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+$check-final-stmt-is-return:error:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": final statement should be a 'return'\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+check-no-breaks:  # block: (addr block), fn: (addr function), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    # var curr/ecx: (addr list stmt) = block->stmts
+    8b/-> *(ebp+8) 0/r32/eax
+    (lookup *(eax+4) *(eax+8))  # Block-stmts Block-stmts => eax
+    3d/compare-eax-and 0/imm32
+    0f 84/jump-if-= $check-no-breaks:end/disp32
+    89/<- %ecx 0/r32/eax
+    {
+      # if curr->next == 0, break
+      (lookup *(ecx+8) *(ecx+0xc))  # List-next List-next => eax
+      3d/compare-eax-and 0/imm32
+      74/jump-if-= break/disp8
+      # if curr->value->tag != Stmt1, continue
+      (lookup *ecx *(ecx+4))  # List-value List-value => eax
+      81 7/subop/compare *eax 1/imm32/stmt1  # Stmt-tag
+      75/jump-if-!= $check-no-breaks:continue/disp8
+      # if curr->value->operation starts with "break", abort
+      (lookup *(eax+4) *(eax+8))  # Stmt1-operation Stmt1-operation => eax
+      (string-starts-with? %eax "break")  # => eax
+      3d/compare-eax-and 0/imm32/false
+      75/jump-if-!= $check-no-breaks:error/disp8
+$check-no-breaks:continue:
+      # curr = curr->next
+      (lookup *(ecx+8) *(ecx+0xc))  # List-next List-next => eax
+      89/<- %ecx 0/r32/eax
+      e9/jump loop/disp32
+    }
+$check-no-breaks:end:
+    # . restore registers
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+$check-no-breaks:error:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) " has outputs, so you cannot 'break' out of the outermost block. Use 'return'.\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+check-mu-get-stmt:  # stmt: (addr stmt), fn: (addr function), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    52/push-edx
+    53/push-ebx
+    56/push-esi
+    57/push-edi
+    # esi = stmt
+    8b/-> *(ebp+8) 6/r32/esi
+    # - check for 0 inouts
+    # var base/ecx: (addr var) = stmt->inouts->value
+    (lookup *(esi+0xc) *(esi+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 84/jump-if-= $check-mu-get-stmt:error-too-few-inouts/disp32
+    (lookup *eax *(eax+4))  # Stmt-var-value Stmt-var-value => eax
+    89/<- %ecx 0/r32/eax
+$check-mu-get-stmt:check-base:
+    # - check base type
+    # if it's an 'addr', check that it's in a register
+    # var base-type/ebx: (addr type-tree) = lookup(base->type)
+    (lookup *(ecx+8) *(ecx+0xc))  # Var-type Var-type => eax
+    89/<- %ebx 0/r32/eax
+    {
+      81 7/subop/compare *ebx 0/imm32/false  # Type-tree-is-atom
+      0f 85/jump-if-!= break/disp32
+$check-mu-get-stmt:base-is-compound:
+      # if (type->left != addr) break
+      (lookup *(ebx+4) *(ebx+8))  # Type-tree-left Type-tree-left => eax
+      (is-simple-mu-type? %eax 2)  # addr => eax
+      3d/compare-eax-and 0/imm32/false
+      74/jump-if-= break/disp8
+$check-mu-get-stmt:base-is-addr:
+      # now check for register
+      81 7/subop/compare *(ecx+0x18) 0/imm32  # Var-register
+      0f 84/jump-if-= $check-mu-get-stmt:error-base-type-addr-but-not-register/disp32
+$check-mu-get-stmt:base-is-addr-in-register:
+      # type->left is now an addr; skip it
+      (lookup *(ebx+0xc) *(ebx+0x10))  # Type-tree-right Type-tree-right => eax
+      81 7/subop/compare *(eax+0xc) 0/imm32  # Type-tree-right
+      0f 85/jump-if-!= $check-mu-get-stmt:error-bad-base/disp32
+$check-mu-get-stmt:base-is-addr-to-atom-in-register:
+      (lookup *(eax+4) *(eax+8))  # Type-tree-left Type-tree-left => eax
+      89/<- %ebx 0/r32/eax
+    }
+$check-mu-get-stmt:check-base-typeinfo:
+    # ensure type is a container
+    81 7/subop/compare *ebx 0/imm32/false  # Type-tree-is-atom
+    {
+      75/jump-if-!= break/disp8
+      (lookup *(ebx+4) *(ebx+8))  # Type-tree-left Type-tree-left => eax
+      89/<- %ebx 0/r32/eax
+    }
+    # var base-type-id/ebx: type-id = base-type->value
+    8b/-> *(ebx+4) 3/r32/ebx  # Type-tree-value
+    (is-container? %ebx)  # => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 84/jump-if-= $check-mu-get-stmt:error-bad-base/disp32
+    # var base-typeinfo/edx: (addr typeinfo) = find-typeinfo(base-type-id)
+    # . var container/ecx: (handle typeinfo)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %ecx 4/r32/esp
+    # .
+    (find-typeinfo %ebx %ecx)
+    (lookup *ecx *(ecx+4))  # => eax
+    # . reclaim container
+    81 0/subop/add %esp 8/imm32
+    # .
+    89/<- %edx 0/r32/eax
+    # var offset/ecx: (addr stmt-var) = stmt->inouts->next
+    (lookup *(esi+0xc) *(esi+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+    (lookup *(eax+8) *(eax+0xc))  # Stmt-var-next Stmt-var-next => eax
+    89/<- %ecx 0/r32/eax
+    # - check for 1 inout
+    3d/compare-eax-and 0/imm32/false
+    0f 84/jump-if-= $check-mu-get-stmt:error-too-few-inouts/disp32
+    # var offset/ecx: (addr var) = lookup(offset->value)
+    (lookup *ecx *(ecx+4))  # Stmt-var-value Stmt-var-value => eax
+    89/<- %ecx 0/r32/eax
+    # - check for valid field
+    81 7/subop/compare *(ecx+0x14) -1/imm32/uninitialized  # Var-offset
+    0f 84/jump-if-= $check-mu-get-stmt:error-bad-field/disp32
+    # - check for too many inouts
+    (lookup *(esi+0xc) *(esi+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+    (lookup *(eax+8) *(eax+0xc))  # Stmt-var-next Stmt-var-next => eax
+    (lookup *(eax+8) *(eax+0xc))  # Stmt-var-next Stmt-var-next => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 85/jump-if-!= $check-mu-get-stmt:error-too-many-inouts/disp32
+    # var output/edi: (addr var) = stmt->outputs->value
+    (lookup *(esi+0x14) *(esi+0x18))  # Stmt1-outputs Stmt1-outputs => eax
+    # - check for 0 outputs
+    3d/compare-eax-and 0/imm32/false
+    0f 84/jump-if-= $check-mu-get-stmt:error-too-few-outputs/disp32
+    (lookup *eax *(eax+4))  # Stmt-var-value Stmt-var-value => eax
+    89/<- %edi 0/r32/eax
+$check-mu-get-stmt:check-output-type:
+    # - check output type
+    # must be in register
+    (lookup *(edi+0x18) *(edi+0x1c))  # Var-register Var-register => eax
+    3d/compare-eax-and 0/imm32
+    0f 84/jump-if-= $check-mu-get-stmt:error-output-not-in-register/disp32
+    # must have a non-atomic type
+    (lookup *(edi+8) *(edi+0xc))  # Var-type Var-type => eax
+    81 7/subop/compare *eax 0/imm32/false  # Type-tree-is-atom
+    0f 85/jump-if-!= $check-mu-get-stmt:error-output-type-not-address/disp32
+    # type must start with (addr ...)
+    (lookup *(eax+4) *(eax+8))  # Type-tree-left Type-tree-left => eax
+    (is-simple-mu-type? %eax 2)  # => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 84/jump-if-= $check-mu-get-stmt:error-output-type-not-address/disp32
+$check-mu-get-stmt:check-output-type-match:
+    # payload of addr type must match 'type' definition
+    (lookup *(edi+8) *(edi+0xc))  # Var-type Var-type => eax
+    (lookup *(eax+0xc) *(eax+0x10))  # Type-tree-right Type-tree-right => eax
+    # if (payload->right == null) payload = payload->left
+    81 7/subop/compare *(eax+0xc) 0/imm32/null  # Type-tree-right
+    {
+      75/jump-if-!= break/disp8
+      (lookup *(eax+4) *(eax+8))  # Type-tree-left Type-tree-left => eax
+    }
+    89/<- %edi 0/r32/eax
+    # . var output-name/ecx: (addr array byte)
+    (lookup *ecx *(ecx+4))  # Var-name Var-name => eax
+    89/<- %ecx 0/r32/eax
+    # . var base-typeinfo-entry/eax: (addr handle typeinfo-entry)
+    (lookup *(edx+4) *(edx+8))  # Typeinfo-fields Typeinfo-fields => eax
+    (get %eax %ecx 0x10)  # => eax
+    # .
+    (lookup *eax *(eax+4))  # => eax
+    (lookup *eax *(eax+4))  # Typeinfo-entry-input-var Typeinfo-entry-input-var => eax
+    (lookup *(eax+8) *(eax+0xc))  # Var-type Var-type => eax
+    # .
+    (type-equal? %edi %eax)  # => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 84/jump-if-= $check-mu-get-stmt:error-bad-output-type/disp32
+    # - check for too many outputs
+    (lookup *(esi+0x14) *(esi+0x18))  # Stmt1-outputs Stmt1-outputs => eax
+    (lookup *(eax+8) *(eax+0xc))  # Stmt-var-next Stmt-var-next => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 85/jump-if-!= $check-mu-get-stmt:error-too-many-outputs/disp32
+$check-mu-get-stmt:end:
+    # . restore registers
+    5f/pop-to-edi
+    5e/pop-to-esi
+    5b/pop-to-ebx
+    5a/pop-to-edx
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+$check-mu-get-stmt:error-too-few-inouts:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt get: too few inouts (2 required)\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-get-stmt:error-too-many-inouts:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt get: too many inouts (2 required)\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-get-stmt:error-too-few-outputs:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt get: must have an output\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-get-stmt:error-too-many-outputs:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt get: too many outputs (1 required)\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-get-stmt:error-bad-base:
+    # error("fn " fn ": stmt get: var '" base->name "' must have a 'type' definition\n")
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt get: var '")
+    (lookup *(esi+0xc) *(esi+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+    (lookup *eax *(eax+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *eax *(eax+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "' must have a 'type' definition\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-get-stmt:error-base-type-addr-but-not-register:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt get: var '")
+    (lookup *(esi+0xc) *(esi+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+    (lookup *eax *(eax+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *eax *(eax+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "' is an 'addr' type, and so must live in a register\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-get-stmt:error-bad-field:
+    # error("fn " fn ": stmt get: type " type " has no member called '" curr->name "'\n")
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt get: type '")
+    # . write(Type-id->data[tmp])
+    bf/copy-to-edi Type-id/imm32
+    8b/-> *(edi+ebx<<2+0xc) 6/r32/esi
+    {
+      81 7/subop/compare %esi 0/imm32
+      74/jump-if-= break/disp8
+      (write-buffered *(ebp+0x10) %esi)
+    }
+    # .
+    (write-buffered *(ebp+0x10) "' has no member called '")
+    (lookup *ecx *(ecx+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "'\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-get-stmt:error-output-not-in-register:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt get: output '")
+    (lookup *edi *(edi+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "' is not in a register\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-get-stmt:error-output-type-not-address:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt get: output must be an addr\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-get-stmt:error-bad-output-type:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt get: wrong output type for member '")
+    (write-buffered *(ebp+0x10) %ecx)
+    (write-buffered *(ebp+0x10) "' of type '")
+    bf/copy-to-edi Type-id/imm32
+    8b/-> *(edi+ebx<<2+0xc) 6/r32/esi
+    {
+      81 7/subop/compare %esi 0/imm32
+      74/jump-if-= break/disp8
+      (write-buffered *(ebp+0x10) %esi)
+    }
+    (write-buffered *(ebp+0x10) "'\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+check-mu-index-stmt:  # stmt: (addr stmt), fn: (addr function), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    52/push-edx
+    53/push-ebx
+    56/push-esi
+    57/push-edi
+    # esi = stmt
+    8b/-> *(ebp+8) 6/r32/esi
+    # - check for 0 inouts
+    # var base/ecx: (addr var) = stmt->inouts->value
+    (lookup *(esi+0xc) *(esi+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+$check-mu-index-stmt:check-no-inouts:
+    3d/compare-eax-and 0/imm32
+    0f 84/jump-if-= $check-mu-index-stmt:error-too-few-inouts/disp32
+    (lookup *eax *(eax+4))  # Stmt-var-value Stmt-var-value => eax
+    89/<- %ecx 0/r32/eax
+    # - check base type is either (addr array ...) in register or (array ...) on stack
+    # var base-type/ebx: (addr type-tree) = lookup(base->type)
+    (lookup *(ecx+8) *(ecx+0xc))  # Var-type Var-type => eax
+    89/<- %ebx 0/r32/eax
+    # if base-type is an atom, abort with a precise error
+    81 7/subop/compare *ebx 0/imm32/false  # Type-tree-is-atom
+    {
+      74/jump-if-= break/disp8
+      (is-simple-mu-type? %ebx 3)  # array => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 85/jump-if-!= $check-mu-index-stmt:error-base-array-atom-type/disp32
+      0f 84/jump-if-= $check-mu-index-stmt:error-base-non-array-type/disp32
+    }
+$check-mu-index-stmt:base-is-compound:
+    # if type->left not addr or array, abort
+    {
+      (lookup *(ebx+4) *(ebx+8))  # Type-tree-left Type-tree-left => eax
+      (is-simple-mu-type? %eax 2)  # addr => eax
+      3d/compare-eax-and 0/imm32/false
+      75/jump-if-!= break/disp8
+      (lookup *(ebx+4) *(ebx+8))  # Type-tree-left Type-tree-left => eax
+      (is-simple-mu-type? %eax 3)  # array => eax
+      3d/compare-eax-and 0/imm32/false
+      75/jump-if-!= break/disp8
+      e9/jump $check-mu-index-stmt:error-base-non-array-type/disp32
+    }
+    # if (type->left == addr) ensure type->right->left == array and type->register exists
+    {
+      (lookup *(ebx+4) *(ebx+8))  # Type-tree-left Type-tree-left => eax
+      (is-simple-mu-type? %eax 2)  # addr => eax
+      3d/compare-eax-and 0/imm32/false
+      74/jump-if-= break/disp8
+$check-mu-index-stmt:base-is-addr:
+      (lookup *(ebx+0xc) *(ebx+0x10))  # Type-tree-right Type-tree-right => eax
+      (lookup *(eax+4) *(eax+8))  # Type-tree-left Type-tree-left => eax
+      (is-simple-mu-type? %eax 3)  # array => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 84/jump-if-= $check-mu-index-stmt:error-base-non-array-type/disp32
+$check-mu-index-stmt:check-base-addr-is-register:
+      81 7/subop/compare *(ecx+0x18) 0/imm32  # Var-register
+      0f 84/jump-if-= $check-mu-index-stmt:error-base-address-array-type-on-stack/disp32
+    }
+    # if (type->left == array) ensure type->register doesn't exist
+    {
+      (lookup *(ebx+4) *(ebx+8))  # Type-tree-left Type-tree-left => eax
+      (is-simple-mu-type? %eax 3)  # array => eax
+      3d/compare-eax-and 0/imm32/false
+      74/jump-if-= break/disp8
+$check-mu-index-stmt:base-is-array:
+      81 7/subop/compare *(ecx+0x18) 0/imm32  # Var-register
+      0f 85/jump-if-!= $check-mu-index-stmt:error-base-array-type-in-register/disp32
+    }
+    # if (base-type->left == addr) base-type = base-type->right
+    {
+      (lookup *(ebx+4) *(ebx+8))  # Type-tree-left Type-tree-left => eax
+      (is-simple-mu-type? %eax 2)  # addr => eax
+      3d/compare-eax-and 0/imm32/false
+      74/jump-if-= break/disp8
+      (lookup *(ebx+0xc) *(ebx+0x10))  # Type-tree-right Type-tree-right => eax
+      89/<- %ebx 0/r32/eax
+    }
+    # - check for 1 inout
+    # var index/ecx: (addr stmt-var) = stmt->inouts->next->value
+    (lookup *(esi+0xc) *(esi+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+    (lookup *(eax+8) *(eax+0xc))  # Stmt-var-next Stmt-var-next => eax
+$check-mu-index-stmt:check-single-inout:
+    3d/compare-eax-and 0/imm32
+    0f 84/jump-if-= $check-mu-index-stmt:error-too-few-inouts/disp32
+    (lookup *eax *(eax+4))  # Stmt-var-value Stmt-var-value => eax
+    89/<- %ecx 0/r32/eax
+    # - check index is either a literal or register
+    # var index-type/edx: (addr type-tree)
+    (lookup *(ecx+8) *(ecx+0xc))  # Var-type Var-type => eax
+    89/<- %edx 0/r32/eax
+    # if index type is an atom, it must be a literal or int
+    81 7/subop/compare *edx 0/imm32/false  # Type-tree-is-atom
+    {
+      74/jump-if-= break/disp8
+$check-mu-index-stmt:index-type-is-atom:
+      (is-simple-mu-type? %edx 0)  # literal => eax
+      3d/compare-eax-and 0/imm32/false
+      75/jump-if-!= $check-mu-index-stmt:index-type-done/disp8
+      (is-simple-mu-type? %edx 1)  # int => eax
+      3d/compare-eax-and 0/imm32/false
+      75/jump-if-!= $check-mu-index-stmt:index-type-done/disp8
+      (is-simple-mu-type? %edx 7)  # offset => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 85/jump-if-!= $check-mu-index-stmt:error-index-offset-atom-type/disp32
+      e9/jump $check-mu-index-stmt:error-invalid-index-type/disp32
+    }
+    # if index type is a non-atom: it must be an offset
+    {
+      75/jump-if-!= break/disp8
+$check-mu-index-stmt:index-type-is-non-atom:
+      (lookup *(edx+4) *(edx+8))  # Type-tree-left Type-tree-left => eax
+      (is-simple-mu-type? %eax 7)  # offset => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 84/jump-if-= $check-mu-index-stmt:error-invalid-index-type/disp32
+    }
+$check-mu-index-stmt:index-type-done:
+    # check index is either a literal or in a register
+    {
+      (is-simple-mu-type? %edx 0)  # literal => eax
+      3d/compare-eax-and 0/imm32/false
+      75/jump-if-!= break/disp8
+$check-mu-index-stmt:check-index-in-register:
+      81 7/subop/compare *(ecx+0x18) 0/imm32  # Var-register
+      0f 84/jump-if-= $check-mu-index-stmt:error-index-on-stack/disp32
+    }
+    # - if index is an 'int', check that element type of base has size 1, 2, 4 or 8 bytes.
+    {
+      (is-simple-mu-type? %edx 1)  # int => eax
+      3d/compare-eax-and 0/imm32/false
+      74/jump-if-= break/disp8
+$check-mu-index-stmt:check-index-can-be-int:
+      (lookup *(esi+0xc) *(esi+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+      (lookup *eax *(eax+4))  # Stmt-var-value Stmt-var-value => eax
+      (array-element-size %eax)  # => eax
+      3d/compare-eax-and 1/imm32
+      74/jump-if-= break/disp8
+      3d/compare-eax-and 2/imm32
+      74/jump-if-= break/disp8
+      3d/compare-eax-and 4/imm32
+      74/jump-if-= break/disp8
+      3d/compare-eax-and 8/imm32
+      74/jump-if-= break/disp8
+      e9/jump $check-mu-index-stmt:error-index-needs-offset/disp32
+    }
+    # - check for too many inouts
+    (lookup *(esi+0xc) *(esi+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+    (lookup *(eax+8) *(eax+0xc))  # Stmt-var-next Stmt-var-next => eax
+    (lookup *(eax+8) *(eax+0xc))  # Stmt-var-next Stmt-var-next => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 85/jump-if-!= $check-mu-index-stmt:error-too-many-inouts/disp32
+    # - check for 0 outputs
+    # var output/edi: (addr var) = stmt->outputs->value
+    (lookup *(esi+0x14) *(esi+0x18))  # Stmt1-outputs Stmt1-outputs => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 84/jump-if-= $check-mu-index-stmt:error-too-few-outputs/disp32
+    (lookup *eax *(eax+4))  # Stmt-var-value Stmt-var-value => eax
+    89/<- %edi 0/r32/eax
+    # - check output type
+    # must have a non-atomic type
+    (lookup *(edi+8) *(edi+0xc))  # Var-type Var-type => eax
+    89/<- %edx 0/r32/eax
+    81 7/subop/compare *edx 0/imm32/false  # Type-tree-is-atom
+    0f 85/jump-if-!= $check-mu-index-stmt:error-output-type-not-address/disp32
+    # type must start with (addr ...)
+    (lookup *(edx+4) *(edx+8))  # Type-tree-left Type-tree-left => eax
+    (is-simple-mu-type? %eax 2)  # addr => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 84/jump-if-= $check-mu-index-stmt:error-output-type-not-address/disp32
+    # if tail(base-type) != tail(output-type) abort
+    (type-tail %ebx)  # => eax
+    89/<- %ebx 0/r32/eax
+    (type-tail %edx)  # => eax
+    (type-equal? %ebx %eax)  # => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 84/jump-if-= $check-mu-index-stmt:error-bad-output-type/disp32
+    # - check for too many outputs
+    (lookup *(esi+0x14) *(esi+0x18))  # Stmt1-outputs Stmt1-outputs => eax
+    (lookup *(eax+8) *(eax+0xc))  # Stmt-var-next Stmt-var-next => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 85/jump-if-!= $check-mu-index-stmt:error-too-many-outputs/disp32
+$check-mu-index-stmt:end:
+    # . restore registers
+    5f/pop-to-edi
+    5e/pop-to-esi
+    5b/pop-to-ebx
+    5a/pop-to-edx
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+$check-mu-index-stmt:error-base-non-array-type:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt index: var '")
+    (lookup *ecx *(ecx+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "' is not an array\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-index-stmt:error-base-array-atom-type:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt index: array '")
+    (lookup *ecx *(ecx+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "' must specify the type of its elements\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-index-stmt:error-base-address-array-type-on-stack:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt index: var '")
+    (lookup *ecx *(ecx+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "' is an addr to an array, and so must live in a register\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-index-stmt:error-base-array-type-in-register:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt index: var '")
+    (lookup *ecx *(ecx+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "' is an array, and so must live on the stack\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-index-stmt:error-too-few-inouts:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt index: too few inouts (2 required)\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-index-stmt:error-invalid-index-type:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt index: second argument '")
+    (lookup *ecx *(ecx+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "' must be an int or offset\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-index-stmt:error-index-offset-atom-type:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt index: offset '")
+    (lookup *ecx *(ecx+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "' must specify the type of array elements\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-index-stmt:error-index-on-stack:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt index: second argument '")
+    (lookup *ecx *(ecx+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "' must be in a register\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-index-stmt:error-index-needs-offset:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt index: cannot take an int for array '")
+    (lookup *(esi+0xc) *(esi+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+    (lookup *eax *(eax+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *eax *(eax+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "'; create an offset instead. See mu.md for details.\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-index-stmt:error-too-many-inouts:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt index: too many inouts (2 required)\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-index-stmt:error-too-few-outputs:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt index: must have an output\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-index-stmt:error-too-many-outputs:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt index: too many outputs (1 required)\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-index-stmt:error-output-not-in-register:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt index: output '")
+    (lookup *edi *(edi+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "' is not in a register\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-index-stmt:error-output-type-not-address:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt index: output '")
+    (lookup *edi *(edi+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "' must be an addr\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-index-stmt:error-bad-output-type:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt index: output '")
+    (lookup *edi *(edi+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "' does not have the right type\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+check-mu-length-stmt:  # stmt: (addr stmt), fn: (addr function), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    52/push-edx
+    53/push-ebx
+    56/push-esi
+    57/push-edi
+    # esi = stmt
+    8b/-> *(ebp+8) 6/r32/esi
+    # - check for 0 inouts
+    # var base/ecx: (addr var) = stmt->inouts->value
+    (lookup *(esi+0xc) *(esi+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+$check-mu-length-stmt:check-no-inouts:
+    3d/compare-eax-and 0/imm32
+    0f 84/jump-if-= $check-mu-length-stmt:error-too-few-inouts/disp32
+    (lookup *eax *(eax+4))  # Stmt-var-value Stmt-var-value => eax
+    89/<- %ecx 0/r32/eax
+    # - check base type is either (addr array ...) in register or (array ...) on stack
+    # var base-type/ebx: (addr type-tree) = lookup(base->type)
+    (lookup *(ecx+8) *(ecx+0xc))  # Var-type Var-type => eax
+    89/<- %ebx 0/r32/eax
+    # if base-type is an atom, abort with a precise error
+    81 7/subop/compare *ebx 0/imm32/false  # Type-tree-is-atom
+    {
+      74/jump-if-= break/disp8
+      (is-simple-mu-type? %ebx 3)  # array => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 85/jump-if-!= $check-mu-length-stmt:error-base-array-atom-type/disp32
+      0f 84/jump-if-= $check-mu-length-stmt:error-base-non-array-type/disp32
+    }
+$check-mu-length-stmt:base-is-compound:
+    # if type->left not addr or array, abort
+    {
+      (lookup *(ebx+4) *(ebx+8))  # Type-tree-left Type-tree-left => eax
+      (is-simple-mu-type? %eax 2)  # addr => eax
+      3d/compare-eax-and 0/imm32/false
+      75/jump-if-!= break/disp8
+      (lookup *(ebx+4) *(ebx+8))  # Type-tree-left Type-tree-left => eax
+      (is-simple-mu-type? %eax 3)  # array => eax
+      3d/compare-eax-and 0/imm32/false
+      75/jump-if-!= break/disp8
+      e9/jump $check-mu-length-stmt:error-base-non-array-type/disp32
+    }
+    # if (type->left == addr) ensure type->right->left == array and type->register exists
+    {
+      (lookup *(ebx+4) *(ebx+8))  # Type-tree-left Type-tree-left => eax
+      (is-simple-mu-type? %eax 2)  # addr => eax
+      3d/compare-eax-and 0/imm32/false
+      74/jump-if-= break/disp8
+$check-mu-length-stmt:base-is-addr:
+      (lookup *(ebx+0xc) *(ebx+0x10))  # Type-tree-right Type-tree-right => eax
+      (lookup *(eax+4) *(eax+8))  # Type-tree-left Type-tree-left => eax
+      (is-simple-mu-type? %eax 3)  # array => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 84/jump-if-= $check-mu-length-stmt:error-base-non-array-type/disp32
+$check-mu-length-stmt:check-base-addr-is-register:
+      81 7/subop/compare *(ecx+0x18) 0/imm32  # Var-register
+      0f 84/jump-if-= $check-mu-length-stmt:error-base-address-array-type-on-stack/disp32
+    }
+    # if (type->left == array) ensure type->register doesn't exist
+    {
+      (lookup *(ebx+4) *(ebx+8))  # Type-tree-left Type-tree-left => eax
+      (is-simple-mu-type? %eax 3)  # array => eax
+      3d/compare-eax-and 0/imm32/false
+      74/jump-if-= break/disp8
+$check-mu-length-stmt:base-is-array:
+      81 7/subop/compare *(ecx+0x18) 0/imm32  # Var-register
+      0f 85/jump-if-!= $check-mu-length-stmt:error-base-array-type-in-register/disp32
+    }
+    # if (base-type->left == addr) base-type = base-type->right
+    {
+      (lookup *(ebx+4) *(ebx+8))  # Type-tree-left Type-tree-left => eax
+      (is-simple-mu-type? %eax 2)  # addr => eax
+      3d/compare-eax-and 0/imm32/false
+      74/jump-if-= break/disp8
+      (lookup *(ebx+0xc) *(ebx+0x10))  # Type-tree-right Type-tree-right => eax
+      89/<- %ebx 0/r32/eax
+    }
+    # - check for too many inouts
+    (lookup *(esi+0xc) *(esi+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+    (lookup *(eax+8) *(eax+0xc))  # Stmt-var-next Stmt-var-next => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 85/jump-if-!= $check-mu-length-stmt:error-too-many-inouts/disp32
+    # - check for 0 outputs
+    # var output/edi: (addr var) = stmt->outputs->value
+    (lookup *(esi+0x14) *(esi+0x18))  # Stmt1-outputs Stmt1-outputs => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 84/jump-if-= $check-mu-length-stmt:error-too-few-outputs/disp32
+    (lookup *eax *(eax+4))  # Stmt-var-value Stmt-var-value => eax
+    89/<- %edi 0/r32/eax
+    # - check output type
+    # must have a non-atomic type
+    (lookup *(edi+8) *(edi+0xc))  # Var-type Var-type => eax
+    (is-simple-mu-type? %eax 1)  # int => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 84/jump-if-= $check-mu-length-stmt:error-invalid-output-type/disp32
+    # - check for too many outputs
+    (lookup *(esi+0x14) *(esi+0x18))  # Stmt1-outputs Stmt1-outputs => eax
+    (lookup *(eax+8) *(eax+0xc))  # Stmt-var-next Stmt-var-next => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 85/jump-if-!= $check-mu-length-stmt:error-too-many-outputs/disp32
+$check-mu-length-stmt:end:
+    # . restore registers
+    5f/pop-to-edi
+    5e/pop-to-esi
+    5b/pop-to-ebx
+    5a/pop-to-edx
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+$check-mu-length-stmt:error-base-non-array-type:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt length: var '")
+    (lookup *ecx *(ecx+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "' is not an array\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-length-stmt:error-base-array-atom-type:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt length: array '")
+    (lookup *ecx *(ecx+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "' must specify the type of its elements\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-length-stmt:error-base-address-array-type-on-stack:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt length: var '")
+    (lookup *ecx *(ecx+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "' is an addr to an array, and so must live in a register\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-length-stmt:error-base-array-type-in-register:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt length: var '")
+    (lookup *ecx *(ecx+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "' is an array, and so must live on the stack\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-length-stmt:error-too-few-inouts:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt length: too few inouts (1 required)\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-length-stmt:error-invalid-index-type:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt length: second argument '")
+    (lookup *ecx *(ecx+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "' must be an int or offset\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-length-stmt:error-index-offset-atom-type:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt length: offset '")
+    (lookup *ecx *(ecx+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "' must specify the type of array elements\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-length-stmt:error-index-on-stack:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt length: second argument '")
+    (lookup *ecx *(ecx+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "' must be in a register\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-length-stmt:error-index-needs-offset:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt length: cannot take an int for array '")
+    (lookup *(esi+0xc) *(esi+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+    (lookup *eax *(eax+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *eax *(eax+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "'; create an offset instead. See mu.md for details.\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-length-stmt:error-too-many-inouts:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt length: too many inouts (1 required)\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-length-stmt:error-too-few-outputs:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt length: must have an output\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-length-stmt:error-too-many-outputs:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt length: too many outputs (1 required)\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-length-stmt:error-output-not-in-register:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt length: output '")
+    (lookup *edi *(edi+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "' is not in a register\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-length-stmt:error-invalid-output-type:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt length: output '")
+    (lookup *edi *(edi+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "' does not have the right type\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+check-mu-compute-offset-stmt:  # stmt: (addr stmt), fn: (addr function), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    52/push-edx
+    53/push-ebx
+    56/push-esi
+    57/push-edi
+    # esi = stmt
+    8b/-> *(ebp+8) 6/r32/esi
+    # - check for 0 inouts
+    # var base/ecx: (addr var) = stmt->inouts->value
+    (lookup *(esi+0xc) *(esi+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+$check-mu-compute-offset-stmt:check-no-inouts:
+    3d/compare-eax-and 0/imm32
+    0f 84/jump-if-= $check-mu-compute-offset-stmt:error-too-few-inouts/disp32
+    (lookup *eax *(eax+4))  # Stmt-var-value Stmt-var-value => eax
+    89/<- %ecx 0/r32/eax
+    # - check base type is either (addr array ...) in register or (array ...) on stack
+    # var base-type/ebx: (addr type-tree) = lookup(base->type)
+    (lookup *(ecx+8) *(ecx+0xc))  # Var-type Var-type => eax
+    89/<- %ebx 0/r32/eax
+    # if base-type is an atom, abort with a precise error
+    81 7/subop/compare *ebx 0/imm32/false  # Type-tree-is-atom
+    {
+      74/jump-if-= break/disp8
+      (is-simple-mu-type? %ebx 3)  # array => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 85/jump-if-!= $check-mu-compute-offset-stmt:error-base-array-atom-type/disp32
+      0f 84/jump-if-= $check-mu-compute-offset-stmt:error-base-non-array-type/disp32
+    }
+$check-mu-compute-offset-stmt:base-is-compound:
+    # if type->left not addr or array, abort
+    {
+      (lookup *(ebx+4) *(ebx+8))  # Type-tree-left Type-tree-left => eax
+      (is-simple-mu-type? %eax 2)  # addr => eax
+      3d/compare-eax-and 0/imm32/false
+      75/jump-if-!= break/disp8
+      (lookup *(ebx+4) *(ebx+8))  # Type-tree-left Type-tree-left => eax
+      (is-simple-mu-type? %eax 3)  # array => eax
+      3d/compare-eax-and 0/imm32/false
+      75/jump-if-!= break/disp8
+      e9/jump $check-mu-compute-offset-stmt:error-base-non-array-type/disp32
+    }
+    # if (type->left == addr) ensure type->right->left == array and type->register exists
+    {
+      (lookup *(ebx+4) *(ebx+8))  # Type-tree-left Type-tree-left => eax
+      (is-simple-mu-type? %eax 2)  # addr => eax
+      3d/compare-eax-and 0/imm32/false
+      74/jump-if-= break/disp8
+$check-mu-compute-offset-stmt:base-is-addr:
+      (lookup *(ebx+0xc) *(ebx+0x10))  # Type-tree-right Type-tree-right => eax
+      (lookup *(eax+4) *(eax+8))  # Type-tree-left Type-tree-left => eax
+      (is-simple-mu-type? %eax 3)  # array => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 84/jump-if-= $check-mu-compute-offset-stmt:error-base-non-array-type/disp32
+    }
+    # if (base-type->left == addr) base-type = base-type->right
+    {
+      (lookup *(ebx+4) *(ebx+8))  # Type-tree-left Type-tree-left => eax
+      (is-simple-mu-type? %eax 2)  # addr => eax
+      3d/compare-eax-and 0/imm32/false
+      74/jump-if-= break/disp8
+      (lookup *(ebx+0xc) *(ebx+0x10))  # Type-tree-right Type-tree-right => eax
+      89/<- %ebx 0/r32/eax
+    }
+    # - check for 1 inout
+    # var index/ecx: (addr stmt-var) = stmt->inouts->next->value
+    (lookup *(esi+0xc) *(esi+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+    (lookup *(eax+8) *(eax+0xc))  # Stmt-var-next Stmt-var-next => eax
+$check-mu-compute-offset-stmt:check-single-inout:
+    3d/compare-eax-and 0/imm32
+    0f 84/jump-if-= $check-mu-compute-offset-stmt:error-too-few-inouts/disp32
+    (lookup *eax *(eax+4))  # Stmt-var-value Stmt-var-value => eax
+    89/<- %ecx 0/r32/eax
+    # - check index is either a literal or register
+    # var index-type/edx: (addr type-tree)
+    (lookup *(ecx+8) *(ecx+0xc))  # Var-type Var-type => eax
+    89/<- %edx 0/r32/eax
+    # index type must be a literal or int
+    81 7/subop/compare *edx 0/imm32/false  # Type-tree-is-atom
+    0f 84/jump-if-= $check-mu-compute-offset-stmt:error-invalid-index-type/disp32
+    {
+$check-mu-compute-offset-stmt:index-type-is-atom:
+      (is-simple-mu-type? %edx 0)  # literal => eax
+      3d/compare-eax-and 0/imm32/false
+      75/jump-if-!= break/disp8
+      (is-simple-mu-type? %edx 1)  # int => eax
+      3d/compare-eax-and 0/imm32/false
+      75/jump-if-!= break/disp8
+      e9/jump $check-mu-compute-offset-stmt:error-invalid-index-type/disp32
+    }
+    # - check for too many inouts
+    (lookup *(esi+0xc) *(esi+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+    (lookup *(eax+8) *(eax+0xc))  # Stmt-var-next Stmt-var-next => eax
+    (lookup *(eax+8) *(eax+0xc))  # Stmt-var-next Stmt-var-next => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 85/jump-if-!= $check-mu-compute-offset-stmt:error-too-many-inouts/disp32
+    # - check for 0 outputs
+    # var output/edi: (addr var) = stmt->outputs->value
+    (lookup *(esi+0x14) *(esi+0x18))  # Stmt1-outputs Stmt1-outputs => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 84/jump-if-= $check-mu-compute-offset-stmt:error-too-few-outputs/disp32
+    (lookup *eax *(eax+4))  # Stmt-var-value Stmt-var-value => eax
+    89/<- %edi 0/r32/eax
+    # - check output type
+    # must have a non-atomic type
+    (lookup *(edi+8) *(edi+0xc))  # Var-type Var-type => eax
+    89/<- %edx 0/r32/eax
+    81 7/subop/compare *edx 0/imm32/false  # Type-tree-is-atom
+    0f 85/jump-if-!= $check-mu-compute-offset-stmt:error-output-type-not-offset/disp32
+    # type must start with (offset ...)
+    (lookup *(edx+4) *(edx+8))  # Type-tree-left Type-tree-left => eax
+    (is-simple-mu-type? %eax 7)  # offset => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 84/jump-if-= $check-mu-compute-offset-stmt:error-output-type-not-offset/disp32
+    # if tail(base-type) != tail(output-type) abort
+    (type-tail %ebx)  # => eax
+    89/<- %ebx 0/r32/eax
+    (type-tail %edx)  # => eax
+    (type-equal? %ebx %eax)  # => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 84/jump-if-= $check-mu-compute-offset-stmt:error-bad-output-type/disp32
+    # - check for too many outputs
+    (lookup *(esi+0x14) *(esi+0x18))  # Stmt1-outputs Stmt1-outputs => eax
+    (lookup *(eax+8) *(eax+0xc))  # Stmt-var-next Stmt-var-next => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 85/jump-if-!= $check-mu-compute-offset-stmt:error-too-many-outputs/disp32
+$check-mu-compute-offset-stmt:end:
+    # . restore registers
+    5f/pop-to-edi
+    5e/pop-to-esi
+    5b/pop-to-ebx
+    5a/pop-to-edx
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+$check-mu-compute-offset-stmt:error-base-non-array-type:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt compute-offset: var '")
+    (lookup *ecx *(ecx+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "' is not an array\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-compute-offset-stmt:error-base-array-atom-type:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt compute-offset: array '")
+    (lookup *ecx *(ecx+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "' must specify the type of its elements\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-compute-offset-stmt:error-too-few-inouts:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt compute-offset: too few inouts (2 required)\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-compute-offset-stmt:error-invalid-index-type:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt compute-offset: second argument '")
+    (lookup *ecx *(ecx+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "' must be an int\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-compute-offset-stmt:error-index-offset-atom-type:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt compute-offset: offset '")
+    (lookup *ecx *(ecx+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "' must specify the type of array elements\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-compute-offset-stmt:error-index-on-stack:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt compute-offset: second argument '")
+    (lookup *ecx *(ecx+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "' must be in a register\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-compute-offset-stmt:error-too-many-inouts:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt compute-offset: too many inouts (2 required)\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-compute-offset-stmt:error-too-few-outputs:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt compute-offset: must have an output\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-compute-offset-stmt:error-too-many-outputs:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt compute-offset: too many outputs (1 required)\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-compute-offset-stmt:error-output-not-in-register:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt compute-offset: output '")
+    (lookup *edi *(edi+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "' is not in a register\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-compute-offset-stmt:error-output-type-not-offset:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt compute-offset: output '")
+    (lookup *edi *(edi+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "' must be an offset\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-compute-offset-stmt:error-bad-output-type:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt compute-offset: output '")
+    (lookup *edi *(edi+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "' does not have the right type\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+check-mu-copy-object-stmt:  # stmt: (addr stmt), fn: (addr function), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    53/push-ebx
+    56/push-esi
+    57/push-edi
+    # esi = stmt
+    8b/-> *(ebp+8) 6/r32/esi
+$check-mu-copy-object-stmt:check-for-output:
+    # if stmt->outputs abort
+    (lookup *(esi+0x14) *(esi+0x18))  # Stmt1-outputs Stmt1-outputs => eax
+    3d/compare-eax-and 0/imm32
+    0f 85/jump-if-!= $check-mu-copy-object-stmt:error-too-many-outputs/disp32
+$check-mu-copy-object-stmt:get-left:
+    # var dest/edi: (addr stmt-var) = stmt->inouts
+    (lookup *(esi+0xc) *(esi+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+    89/<- %edi 0/r32/eax
+    # zero inouts
+    3d/compare-eax-and 0/imm32
+    0f 84/jump-if-= $check-mu-copy-object-stmt:error-incorrect-inouts/disp32
+$check-mu-copy-object-stmt:get-src:
+    # var src/esi: (addr stmt-var) = dest->next
+    (lookup *(edi+8) *(edi+0xc))  # Stmt-var-next Stmt-var-next => eax
+    89/<- %esi 0/r32/eax
+    # 1 inout
+    3d/compare-eax-and 0/imm32
+    0f 84/jump-if-= $check-mu-copy-object-stmt:error-incorrect-inouts/disp32
+    # > 2 inouts
+    (lookup *(esi+8) *(esi+0xc))  # Stmt-var-next Stmt-var-next => eax
+    3d/compare-eax-and 0/imm32
+    0f 85/jump-if-!= $check-mu-copy-object-stmt:error-incorrect-inouts/disp32
+$check-mu-copy-object-stmt:types:
+    # var src-type/ecx: (addr type-tree) = src->value->type
+    (lookup *esi *(esi+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *(eax+8) *(eax+0xc))  # Var-type Var-type => eax
+    89/<- %ecx 0/r32/eax
+    # if (src->is-deref?) src-type = src-type->payload
+    8b/-> *(esi+0x10) 0/r32/eax  # Stmt-var-is-deref
+    3d/compare-eax-and 0/imm32/false
+    {
+      74/jump-if-= break/disp8
+      (lookup *(ecx+0xc) *(ecx+0x10))  # Type-tree-right Type-tree-right => eax
+      # if src-type->right is null, src-type = src-type->left
+      81 7/subop/compare *(eax+0xc) 0/imm32  # Type-tree-right
+      {
+        75/jump-if-!= break/disp8
+        (lookup *(eax+4) *(eax+8))  # Type-tree-left Type-tree-left => eax
+      }
+      89/<- %ecx 0/r32/eax
+    }
+    # if src-type is not addr, abort
+    (is-mu-addr-type? %ecx)  # => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 84/jump-if-= $check-mu-copy-object-stmt:error-invalid-types/disp32
+    # var dest-type/ebx: (addr type-tree) = dest->value->type
+    (lookup *edi *(edi+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *(eax+8) *(eax+0xc))  # Var-type Var-type => eax
+    89/<- %ebx 0/r32/eax
+    # if (dest->is-deref?) dest-type = dest-type->payload
+    8b/-> *(edi+0x10) 0/r32/eax  # Stmt-var-is-deref
+    3d/compare-eax-and 0/imm32/false
+    {
+      74/jump-if-= break/disp8
+      (lookup *(ebx+0xc) *(ebx+0x10))  # Type-tree-right Type-tree-right => eax
+      # if dest-type->right is null, dest-type = dest-type->left
+      81 7/subop/compare *(eax+0xc) 0/imm32  # Type-tree-right
+      {
+        75/jump-if-!= break/disp8
+        (lookup *(eax+4) *(eax+8))  # Type-tree-left Type-tree-left => eax
+      }
+      89/<- %ebx 0/r32/eax
+    }
+    # if (dest-type != src-type) abort
+    (type-equal? %ecx %ebx)  # => eax
+    3d/compare-eax-and 0/imm32
+    0f 84/jump-if-= $check-mu-copy-object-stmt:error-invalid-types/disp32
+$check-mu-copy-object-stmt:end:
+    # . restore registers
+    5f/pop-to-edi
+    5e/pop-to-esi
+    5b/pop-to-ebx
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+$check-mu-copy-object-stmt:error-incorrect-inouts:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt 'copy-object' must have two inouts\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-copy-object-stmt:error-too-many-outputs:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt 'copy-object' must not have any outputs\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-copy-object-stmt:error-invalid-types:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt copy-object: two inouts with identical addr types expected\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+check-mu-clear-object-stmt:  # stmt: (addr stmt), fn: (addr function), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    53/push-ebx
+    56/push-esi
+    57/push-edi
+    # esi = stmt
+    8b/-> *(ebp+8) 6/r32/esi
+$check-mu-clear-object-stmt:check-for-output:
+    # if stmt->outputs abort
+    (lookup *(esi+0x14) *(esi+0x18))  # Stmt1-outputs Stmt1-outputs => eax
+    3d/compare-eax-and 0/imm32
+    0f 85/jump-if-!= $check-mu-clear-object-stmt:error-too-many-outputs/disp32
+$check-mu-clear-object-stmt:get-left:
+    # var dest/edi: (addr stmt-var) = stmt->inouts
+    (lookup *(esi+0xc) *(esi+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+    89/<- %edi 0/r32/eax
+    # zero inouts
+    3d/compare-eax-and 0/imm32
+    0f 84/jump-if-= $check-mu-clear-object-stmt:error-incorrect-inouts/disp32
+$check-mu-clear-object-stmt:get-src:
+    # > 1 inout
+    (lookup *(edi+8) *(edi+0xc))  # Stmt-var-next Stmt-var-next => eax
+    3d/compare-eax-and 0/imm32
+    0f 85/jump-if-!= $check-mu-clear-object-stmt:error-incorrect-inouts/disp32
+$check-mu-clear-object-stmt:types:
+    # var src-type/ecx: (addr type-tree) = src->value->type
+    (lookup *edi *(edi+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *(eax+8) *(eax+0xc))  # Var-type Var-type => eax
+    89/<- %ecx 0/r32/eax
+    # if (src->is-deref?) src-type = src-type->payload
+    8b/-> *(edi+0x10) 0/r32/eax  # Stmt-var-is-deref
+    3d/compare-eax-and 0/imm32/false
+    {
+      74/jump-if-= break/disp8
+      (lookup *(ecx+0xc) *(ecx+0x10))  # Type-tree-right Type-tree-right => eax
+      # if src-type->right is null, src-type = src-type->left
+      81 7/subop/compare *(eax+0xc) 0/imm32  # Type-tree-right
+      {
+        75/jump-if-!= break/disp8
+        (lookup *(eax+4) *(eax+8))  # Type-tree-left Type-tree-left => eax
+      }
+      89/<- %ecx 0/r32/eax
+    }
+    # if src-type is not addr, abort
+    (is-mu-addr-type? %ecx)  # => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 84/jump-if-= $check-mu-clear-object-stmt:error-invalid-type/disp32
+$check-mu-clear-object-stmt:end:
+    # . restore registers
+    5f/pop-to-edi
+    5e/pop-to-esi
+    5b/pop-to-ebx
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+$check-mu-clear-object-stmt:error-incorrect-inouts:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt 'clear-object' must have a single inout\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-clear-object-stmt:error-too-many-outputs:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt 'clear-object' must not have any outputs\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-clear-object-stmt:error-invalid-type:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt clear-object: inout must have an addr type\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+check-mu-allocate-stmt:  # stmt: (addr stmt), fn: (addr function), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    53/push-ebx
+    56/push-esi
+    57/push-edi
+    # esi = stmt
+    8b/-> *(ebp+8) 6/r32/esi
+$check-mu-allocate-stmt:check-for-output:
+    # if stmt->outputs abort
+    (lookup *(esi+0x14) *(esi+0x18))  # Stmt1-outputs Stmt1-outputs => eax
+    3d/compare-eax-and 0/imm32
+    0f 85/jump-if-!= $check-mu-allocate-stmt:error-too-many-outputs/disp32
+$check-mu-allocate-stmt:get-target:
+    # var target/edi: (addr stmt-var) = stmt->inouts
+    (lookup *(esi+0xc) *(esi+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+    89/<- %edi 0/r32/eax
+    # zero inouts
+    3d/compare-eax-and 0/imm32
+    0f 84/jump-if-= $check-mu-allocate-stmt:error-incorrect-inouts/disp32
+    # > 1 inouts
+    (lookup *(edi+8) *(edi+0xc))  # Stmt-var-next Stmt-var-next => eax
+    3d/compare-eax-and 0/imm32
+    0f 85/jump-if-!= $check-mu-allocate-stmt:error-incorrect-inouts/disp32
+$check-mu-allocate-stmt:check-type:
+    # var target-type/ebx: (addr type-tree) = target->value->type
+    (lookup *edi *(edi+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *(eax+8) *(eax+0xc))  # Var-type Var-type => eax
+    89/<- %ebx 0/r32/eax
+    # if (target->is-deref?) target-type = target-type->payload
+    8b/-> *(edi+0x10) 0/r32/eax  # Stmt-var-is-deref
+    3d/compare-eax-and 0/imm32/false
+    {
+      74/jump-if-= break/disp8
+      (lookup *(ebx+0xc) *(ebx+0x10))  # Type-tree-right Type-tree-right => eax
+      # if target-type->right is null, target-type = target-type->left
+      81 7/subop/compare *(eax+0xc) 0/imm32  # Type-tree-right
+      {
+        75/jump-if-!= break/disp8
+        (lookup *(eax+4) *(eax+8))  # Type-tree-left Type-tree-left => eax
+      }
+      89/<- %ebx 0/r32/eax
+    }
+    # if target-type is not addr, abort
+    (is-mu-addr-type? %ebx)  # => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 84/jump-if-= $check-mu-allocate-stmt:error-invalid-type/disp32
+    # if target-type->right is an atom, abort
+    (lookup *(ebx+0xc) *(ebx+0x10))  # Type-tree-right Type-tree-right => eax
+    81 7/subop/compare *eax 0/imm32/false  # Type-tree-is-atom
+    0f 85/jump-if-!= $check-mu-allocate-stmt:error-invalid-type/disp32
+    # if target-type->right->left is not handle, abort
+    (lookup *(eax+4) *(eax+8))  # Type-tree-left Type-tree-left => eax
+    (is-simple-mu-type? %eax 4)  # handle => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 84/jump-if-= $check-mu-allocate-stmt:error-invalid-type/disp32
+$check-mu-allocate-stmt:end:
+    # . restore registers
+    5f/pop-to-edi
+    5e/pop-to-esi
+    5b/pop-to-ebx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+$check-mu-allocate-stmt:error-incorrect-inouts:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt 'allocate' must have a single inout\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-allocate-stmt:error-too-many-outputs:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt 'allocate' must not have any outputs\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-allocate-stmt:error-invalid-type:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt allocate: inout '")
+    (lookup *edi *(edi+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *eax *(eax+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "' must have type (addr handle ...)\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+check-mu-populate-stmt:  # stmt: (addr stmt), fn: (addr function), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    53/push-ebx
+    56/push-esi
+    57/push-edi
+    # esi = stmt
+    8b/-> *(ebp+8) 6/r32/esi
+$check-mu-populate-stmt:check-for-output:
+    # if stmt->outputs abort
+    (lookup *(esi+0x14) *(esi+0x18))  # Stmt1-outputs Stmt1-outputs => eax
+    3d/compare-eax-and 0/imm32
+    0f 85/jump-if-!= $check-mu-populate-stmt:error-too-many-outputs/disp32
+$check-mu-populate-stmt:get-target:
+    # var target/edi: (addr stmt-var) = stmt->inouts
+    (lookup *(esi+0xc) *(esi+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+    89/<- %edi 0/r32/eax
+    # zero inouts
+    3d/compare-eax-and 0/imm32
+    0f 84/jump-if-= $check-mu-populate-stmt:error-incorrect-inouts/disp32
+$check-mu-populate-stmt:get-length:
+    # var length/esi: (addr stmt-var) = dest->next
+    (lookup *(edi+8) *(edi+0xc))  # Stmt-var-next Stmt-var-next => eax
+    89/<- %esi 0/r32/eax
+    # 1 inout
+    3d/compare-eax-and 0/imm32
+    0f 84/jump-if-= $check-mu-populate-stmt:error-incorrect-inouts/disp32
+    # > 2 inouts
+    (lookup *(esi+8) *(esi+0xc))  # Stmt-var-next Stmt-var-next => eax
+    3d/compare-eax-and 0/imm32
+    0f 85/jump-if-!= $check-mu-populate-stmt:error-incorrect-inouts/disp32
+$check-mu-populate-stmt:check-target-type:
+    # var target-type/ebx: (addr type-tree) = target->value->type
+    (lookup *edi *(edi+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *(eax+8) *(eax+0xc))  # Var-type Var-type => eax
+    89/<- %ebx 0/r32/eax
+$check-mu-populate-stmt:check-target-type-deref:
+    # if (target->is-deref?) target-type = target-type->payload
+    8b/-> *(edi+0x10) 0/r32/eax  # Stmt-var-is-deref
+    3d/compare-eax-and 0/imm32/false
+    {
+      74/jump-if-= break/disp8
+      (lookup *(ebx+0xc) *(ebx+0x10))  # Type-tree-right Type-tree-right => eax
+      # if target-type->right is null, target-type = target-type->left
+      81 7/subop/compare *(eax+0xc) 0/imm32  # Type-tree-right
+      {
+        75/jump-if-!= break/disp8
+        (lookup *(eax+4) *(eax+8))  # Type-tree-left Type-tree-left => eax
+      }
+      89/<- %ebx 0/r32/eax
+    }
+$check-mu-populate-stmt:check-target-type-addr:
+    # if target-type is not addr, abort
+    (is-mu-addr-type? %ebx)  # => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 84/jump-if-= $check-mu-populate-stmt:error-invalid-target-type/disp32
+    # if target-type->right is an atom, abort
+    (lookup *(ebx+0xc) *(ebx+0x10))  # Type-tree-right Type-tree-right => eax
+    89/<- %ebx 0/r32/eax
+    81 7/subop/compare *ebx 0/imm32/false  # Type-tree-is-atom
+    0f 85/jump-if-!= $check-mu-populate-stmt:error-invalid-target-type/disp32
+$check-mu-populate-stmt:check-target-type-handle:
+    # if target-type->right->left is not handle, abort
+    (lookup *(ebx+4) *(ebx+8))  # Type-tree-left Type-tree-left => eax
+    (is-simple-mu-type? %eax 4)  # handle => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 84/jump-if-= $check-mu-populate-stmt:error-invalid-target-type/disp32
+    # if target-type->right->right is an atom, abort
+    (lookup *(ebx+0xc) *(ebx+0x10))  # Type-tree-right Type-tree-right => eax
+    89/<- %ebx 0/r32/eax
+    81 7/subop/compare *ebx 0/imm32/false  # Type-tree-is-atom
+    0f 85/jump-if-!= $check-mu-populate-stmt:error-invalid-target-type/disp32
+$check-mu-populate-stmt:check-target-type-array:
+    # if target-type->right->right->left is not array, abort
+    (lookup *(ebx+4) *(ebx+8))  # Type-tree-left Type-tree-left => eax
+    (is-simple-mu-type? %eax 3)  # array => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 84/jump-if-= $check-mu-populate-stmt:error-invalid-target-type/disp32
+$check-mu-populate-stmt:check-length-type:
+    (lookup *esi *(esi+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *(eax+8) *(eax+0xc))  # Var-type Var-type => eax
+    89/<- %ebx 0/r32/eax
+    (is-simple-mu-type? %ebx 0)  # literal => eax
+    3d/compare-eax-and 0/imm32/false
+    75/jump-if-!= $check-mu-populate-stmt:end/disp8
+    (is-simple-mu-type? %ebx 1)  # int => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 84/jump-if-= $check-mu-populate-stmt:error-invalid-length-type/disp32
+$check-mu-populate-stmt:end:
+    # . restore registers
+    5f/pop-to-edi
+    5e/pop-to-esi
+    5b/pop-to-ebx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+$check-mu-populate-stmt:error-incorrect-inouts:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt 'populate' must have two inouts\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-populate-stmt:error-too-many-outputs:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt 'populate' must not have any outputs\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-populate-stmt:error-invalid-target-type:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt populate: first inout '")
+    (lookup *edi *(edi+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *eax *(eax+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "' must have type (addr handle array ...)\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-populate-stmt:error-invalid-length-type:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt populate: second inout '")
+    (lookup *esi *(esi+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *eax *(eax+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "' must be an int\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+check-mu-populate-stream-stmt:  # stmt: (addr stmt), fn: (addr function), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    53/push-ebx
+    56/push-esi
+    57/push-edi
+    # esi = stmt
+    8b/-> *(ebp+8) 6/r32/esi
+$check-mu-populate-stream-stmt:check-for-output:
+    # if stmt->outputs abort
+    (lookup *(esi+0x14) *(esi+0x18))  # Stmt1-outputs Stmt1-outputs => eax
+    3d/compare-eax-and 0/imm32
+    0f 85/jump-if-!= $check-mu-populate-stream-stmt:error-too-many-outputs/disp32
+$check-mu-populate-stream-stmt:get-target:
+    # var target/edi: (addr stmt-var) = stmt->inouts
+    (lookup *(esi+0xc) *(esi+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+    89/<- %edi 0/r32/eax
+    # zero inouts
+    3d/compare-eax-and 0/imm32
+    0f 84/jump-if-= $check-mu-populate-stream-stmt:error-incorrect-inouts/disp32
+$check-mu-populate-stream-stmt:get-length:
+    # var length/esi: (addr stmt-var) = dest->next
+    (lookup *(edi+8) *(edi+0xc))  # Stmt-var-next Stmt-var-next => eax
+    89/<- %esi 0/r32/eax
+    # 1 inout
+    3d/compare-eax-and 0/imm32
+    0f 84/jump-if-= $check-mu-populate-stream-stmt:error-incorrect-inouts/disp32
+    # > 2 inouts
+    (lookup *(esi+8) *(esi+0xc))  # Stmt-var-next Stmt-var-next => eax
+    3d/compare-eax-and 0/imm32
+    0f 85/jump-if-!= $check-mu-populate-stream-stmt:error-incorrect-inouts/disp32
+$check-mu-populate-stream-stmt:check-target-type:
+    # var target-type/ebx: (addr type-tree) = target->value->type
+    (lookup *edi *(edi+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *(eax+8) *(eax+0xc))  # Var-type Var-type => eax
+    89/<- %ebx 0/r32/eax
+$check-mu-populate-stream-stmt:check-target-type-deref:
+    # if (target->is-deref?) target-type = target-type->payload
+    8b/-> *(edi+0x10) 0/r32/eax  # Stmt-var-is-deref
+    3d/compare-eax-and 0/imm32/false
+    {
+      74/jump-if-= break/disp8
+      (lookup *(ebx+0xc) *(ebx+0x10))  # Type-tree-right Type-tree-right => eax
+      # if target-type->right is null, target-type = target-type->left
+      81 7/subop/compare *(eax+0xc) 0/imm32  # Type-tree-right
+      {
+        75/jump-if-!= break/disp8
+        (lookup *(eax+4) *(eax+8))  # Type-tree-left Type-tree-left => eax
+      }
+      89/<- %ebx 0/r32/eax
+    }
+$check-mu-populate-stream-stmt:check-target-type-addr:
+    # if target-type is not addr, abort
+    (is-mu-addr-type? %ebx)  # => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 84/jump-if-= $check-mu-populate-stream-stmt:error-invalid-target-type/disp32
+    # if target-type->right is an atom, abort
+    (lookup *(ebx+0xc) *(ebx+0x10))  # Type-tree-right Type-tree-right => eax
+    89/<- %ebx 0/r32/eax
+    81 7/subop/compare *ebx 0/imm32/false  # Type-tree-is-atom
+    0f 85/jump-if-!= $check-mu-populate-stream-stmt:error-invalid-target-type/disp32
+$check-mu-populate-stream-stmt:check-target-type-handle:
+    # if target-type->right->left is not handle, abort
+    (lookup *(ebx+4) *(ebx+8))  # Type-tree-left Type-tree-left => eax
+    (is-simple-mu-type? %eax 4)  # handle => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 84/jump-if-= $check-mu-populate-stream-stmt:error-invalid-target-type/disp32
+    # if target-type->right->right is an atom, abort
+    (lookup *(ebx+0xc) *(ebx+0x10))  # Type-tree-right Type-tree-right => eax
+    89/<- %ebx 0/r32/eax
+    81 7/subop/compare *ebx 0/imm32/false  # Type-tree-is-atom
+    0f 85/jump-if-!= $check-mu-populate-stream-stmt:error-invalid-target-type/disp32
+$check-mu-populate-stream-stmt:check-target-type-stream:
+    # if target-type->right->right->left is not stream, abort
+    (lookup *(ebx+4) *(ebx+8))  # Type-tree-left Type-tree-left => eax
+    (is-simple-mu-type? %eax 0xb)  # stream => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 84/jump-if-= $check-mu-populate-stream-stmt:error-invalid-target-type/disp32
+$check-mu-populate-stream-stmt:check-length-type:
+    (lookup *esi *(esi+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *(eax+8) *(eax+0xc))  # Var-type Var-type => eax
+    89/<- %ebx 0/r32/eax
+    (is-simple-mu-type? %ebx 0)  # literal => eax
+    3d/compare-eax-and 0/imm32/false
+    75/jump-if-!= $check-mu-populate-stream-stmt:end/disp8
+    (is-simple-mu-type? %ebx 1)  # int => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 84/jump-if-= $check-mu-populate-stream-stmt:error-invalid-length-type/disp32
+$check-mu-populate-stream-stmt:end:
+    # . restore registers
+    5f/pop-to-edi
+    5e/pop-to-esi
+    5b/pop-to-ebx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+$check-mu-populate-stream-stmt:error-incorrect-inouts:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt 'populate-stream' must have two inouts\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-populate-stream-stmt:error-too-many-outputs:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt 'populate-stream' must not have any outputs\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-populate-stream-stmt:error-invalid-target-type:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt populate-stream: first inout '")
+    (lookup *edi *(edi+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *eax *(eax+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "' must have type (addr handle stream ...)\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-populate-stream-stmt:error-invalid-length-type:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt populate-stream: second inout '")
+    (lookup *esi *(esi+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *eax *(eax+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "' must be an int\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+check-mu-read-from-stream-stmt:  # stmt: (addr stmt), fn: (addr function), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    52/push-edx
+    53/push-ebx
+    56/push-esi
+    57/push-edi
+    # esi = stmt
+    8b/-> *(ebp+8) 6/r32/esi
+    # - check for 0 inouts
+    # var base/ecx: (addr var) = stmt->inouts->value
+    (lookup *(esi+0xc) *(esi+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+$check-mu-read-from-stream-stmt:check-no-inouts:
+    3d/compare-eax-and 0/imm32
+    0f 84/jump-if-= $check-mu-read-from-stream-stmt:error-too-few-inouts/disp32
+    (lookup *eax *(eax+4))  # Stmt-var-value Stmt-var-value => eax
+    89/<- %ecx 0/r32/eax
+    # - check base type is (addr stream T)
+    # var base-type/ebx: (addr type-tree) = lookup(base->type)
+    (lookup *(ecx+8) *(ecx+0xc))  # Var-type Var-type => eax
+    89/<- %ebx 0/r32/eax
+$check-mu-read-from-stream-stmt:check-base-is-compound:
+    # if base-type is an atom, abort
+    81 7/subop/compare *ebx 0/imm32/false  # Type-tree-is-atom
+    0f 85/jump-if-!= $check-mu-read-from-stream-stmt:error-invalid-base-type/disp32
+$check-mu-read-from-stream-stmt:check-base-is-addr:
+    # if type->left not addr, abort
+    (lookup *(ebx+4) *(ebx+8))  # Type-tree-left Type-tree-left => eax
+    (is-simple-mu-type? %eax 2)  # addr => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 84/jump-if-= $check-mu-read-from-stream-stmt:error-invalid-base-type/disp32
+$check-mu-read-from-stream-stmt:check-base-is-addr-to-stream:
+    # base-type = base-type->right
+    (lookup *(ebx+0xc) *(ebx+0x10))  # Type-tree-right Type-tree-right => eax
+    89/<- %ebx 0/r32/eax
+    # ensure base-type->left == stream
+    (lookup *(eax+4) *(eax+8))  # Type-tree-left Type-tree-left => eax
+    (is-simple-mu-type? %eax 0xb)  # stream => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 84/jump-if-= $check-mu-read-from-stream-stmt:error-invalid-base-type/disp32
+    # - check target type is (addr T)
+    # var target/ecx: (addr stmt-var) = stmt->inouts->next->value
+    (lookup *(esi+0xc) *(esi+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+    (lookup *(eax+8) *(eax+0xc))  # Stmt-var-next Stmt-var-next => eax
+$check-mu-read-from-stream-stmt:check-single-inout:
+    3d/compare-eax-and 0/imm32
+    0f 84/jump-if-= $check-mu-read-from-stream-stmt:error-too-few-inouts/disp32
+    (lookup *eax *(eax+4))  # Stmt-var-value Stmt-var-value => eax
+    89/<- %ecx 0/r32/eax
+    # var target-type/edx: (addr type-tree)
+    (lookup *(ecx+8) *(ecx+0xc))  # Var-type Var-type => eax
+    89/<- %edx 0/r32/eax
+    # if target-type is an atom, it must be a literal or int
+$check-mu-read-from-stream-stmt:check-target-is-compound:
+    81 7/subop/compare *edx 0/imm32/false  # Type-tree-is-atom
+    0f 85/jump-if-!= $check-mu-read-from-stream-stmt:error-target-type-not-address/disp32
+$check-mu-read-from-stream-stmt:check-target-type:
+    # target type must start with (addr ...)
+    (lookup *(edx+4) *(edx+8))  # Type-tree-left Type-tree-left => eax
+    (is-simple-mu-type? %eax 2)  # addr => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 84/jump-if-= $check-mu-read-from-stream-stmt:error-target-type-not-address/disp32
+    # if tail(base-type) != tail(target-type) abort
+    (type-tail %ebx)  # => eax
+    89/<- %ebx 0/r32/eax
+    (type-tail %edx)  # => eax
+    (type-equal? %ebx %eax)  # => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 84/jump-if-= $check-mu-read-from-stream-stmt:error-invalid-target-type/disp32
+$check-mu-read-from-stream-stmt:check-too-many-inouts:
+    # - check for too many inouts
+    (lookup *(esi+0xc) *(esi+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+    (lookup *(eax+8) *(eax+0xc))  # Stmt-var-next Stmt-var-next => eax
+    (lookup *(eax+8) *(eax+0xc))  # Stmt-var-next Stmt-var-next => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 85/jump-if-!= $check-mu-read-from-stream-stmt:error-too-many-inouts/disp32
+$check-mu-read-from-stream-stmt:check-unexpected-output:
+    # - check for any output
+    (lookup *(esi+0x14) *(esi+0x18))  # Stmt1-outputs Stmt1-outputs => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 85/jump-if-!= $check-mu-read-from-stream-stmt:error-unexpected-output/disp32
+$check-mu-read-from-stream-stmt:end:
+    # . restore registers
+    5f/pop-to-edi
+    5e/pop-to-esi
+    5b/pop-to-ebx
+    5a/pop-to-edx
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+$check-mu-read-from-stream-stmt:error-invalid-base-type:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt read-from-stream: var '")
+    (lookup *ecx *(ecx+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "' must be an addr to a stream\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-read-from-stream-stmt:error-too-few-inouts:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt read-from-stream: too few inouts (2 required)\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-read-from-stream-stmt:error-target-type-not-address:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt read-from-stream: target '")
+    (lookup *ecx *(ecx+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "' must be an addr\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-read-from-stream-stmt:error-invalid-target-type:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt read-from-stream: second inout '")
+    (lookup *ecx *(ecx+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "' does not have the right type\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-read-from-stream-stmt:error-too-many-inouts:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt read-from-stream: too many inouts (2 required)\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-read-from-stream-stmt:error-unexpected-output:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt read-from-stream: unexpected output\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+check-mu-write-to-stream-stmt:  # stmt: (addr stmt), fn: (addr function), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    52/push-edx
+    53/push-ebx
+    56/push-esi
+    57/push-edi
+    # esi = stmt
+    8b/-> *(ebp+8) 6/r32/esi
+    # - check for 0 inouts
+    # var base/ecx: (addr var) = stmt->inouts->value
+    (lookup *(esi+0xc) *(esi+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+$check-mu-write-to-stream-stmt:check-no-inouts:
+    3d/compare-eax-and 0/imm32
+    0f 84/jump-if-= $check-mu-write-to-stream-stmt:error-too-few-inouts/disp32
+    (lookup *eax *(eax+4))  # Stmt-var-value Stmt-var-value => eax
+    89/<- %ecx 0/r32/eax
+    # - check base type is (addr stream T)
+    # var base-type/ebx: (addr type-tree) = lookup(base->type)
+    (lookup *(ecx+8) *(ecx+0xc))  # Var-type Var-type => eax
+    89/<- %ebx 0/r32/eax
+$check-mu-write-to-stream-stmt:check-base-is-compound:
+    # if base-type is an atom, abort
+    81 7/subop/compare *ebx 0/imm32/false  # Type-tree-is-atom
+    0f 85/jump-if-!= $check-mu-write-to-stream-stmt:error-invalid-base-type/disp32
+$check-mu-write-to-stream-stmt:check-base-is-addr:
+    # if type->left not addr, abort
+    (lookup *(ebx+4) *(ebx+8))  # Type-tree-left Type-tree-left => eax
+    (is-simple-mu-type? %eax 2)  # addr => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 84/jump-if-= $check-mu-write-to-stream-stmt:error-invalid-base-type/disp32
+$check-mu-write-to-stream-stmt:check-base-is-addr-to-stream:
+    # base-type = base-type->right
+    (lookup *(ebx+0xc) *(ebx+0x10))  # Type-tree-right Type-tree-right => eax
+    89/<- %ebx 0/r32/eax
+    # ensure base-type->left == stream
+    (lookup *(eax+4) *(eax+8))  # Type-tree-left Type-tree-left => eax
+    (is-simple-mu-type? %eax 0xb)  # stream => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 84/jump-if-= $check-mu-write-to-stream-stmt:error-invalid-base-type/disp32
+    # - check target type is (addr T)
+    # var target/ecx: (addr stmt-var) = stmt->inouts->next->value
+    (lookup *(esi+0xc) *(esi+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+    (lookup *(eax+8) *(eax+0xc))  # Stmt-var-next Stmt-var-next => eax
+$check-mu-write-to-stream-stmt:check-single-inout:
+    3d/compare-eax-and 0/imm32
+    0f 84/jump-if-= $check-mu-write-to-stream-stmt:error-too-few-inouts/disp32
+    (lookup *eax *(eax+4))  # Stmt-var-value Stmt-var-value => eax
+    89/<- %ecx 0/r32/eax
+    # var target-type/edx: (addr type-tree)
+    (lookup *(ecx+8) *(ecx+0xc))  # Var-type Var-type => eax
+    89/<- %edx 0/r32/eax
+    # if target-type is an atom, it must be a literal or int
+$check-mu-write-to-stream-stmt:check-target-is-compound:
+    81 7/subop/compare *edx 0/imm32/false  # Type-tree-is-atom
+    0f 85/jump-if-!= $check-mu-write-to-stream-stmt:error-target-type-not-address/disp32
+$check-mu-write-to-stream-stmt:check-target-type:
+    # target type must start with (addr ...)
+    (lookup *(edx+4) *(edx+8))  # Type-tree-left Type-tree-left => eax
+    (is-simple-mu-type? %eax 2)  # addr => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 84/jump-if-= $check-mu-write-to-stream-stmt:error-target-type-not-address/disp32
+    # if tail(base-type) != tail(target-type) abort
+    (type-tail %ebx)  # => eax
+    89/<- %ebx 0/r32/eax
+    (type-tail %edx)  # => eax
+    (type-equal? %ebx %eax)  # => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 84/jump-if-= $check-mu-write-to-stream-stmt:error-invalid-target-type/disp32
+$check-mu-write-to-stream-stmt:check-too-many-inouts:
+    # - check for too many inouts
+    (lookup *(esi+0xc) *(esi+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+    (lookup *(eax+8) *(eax+0xc))  # Stmt-var-next Stmt-var-next => eax
+    (lookup *(eax+8) *(eax+0xc))  # Stmt-var-next Stmt-var-next => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 85/jump-if-!= $check-mu-write-to-stream-stmt:error-too-many-inouts/disp32
+$check-mu-write-to-stream-stmt:check-unexpected-output:
+    # - check for any output
+    (lookup *(esi+0x14) *(esi+0x18))  # Stmt1-outputs Stmt1-outputs => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 85/jump-if-!= $check-mu-write-to-stream-stmt:error-unexpected-output/disp32
+$check-mu-write-to-stream-stmt:end:
+    # . restore registers
+    5f/pop-to-edi
+    5e/pop-to-esi
+    5b/pop-to-ebx
+    5a/pop-to-edx
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+$check-mu-write-to-stream-stmt:error-invalid-base-type:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt write-to-stream: var '")
+    (lookup *ecx *(ecx+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "' must be an addr to a stream\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-write-to-stream-stmt:error-too-few-inouts:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt write-to-stream: too few inouts (2 required)\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-write-to-stream-stmt:error-target-type-not-address:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt write-to-stream: target '")
+    (lookup *ecx *(ecx+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "' must be an addr\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-write-to-stream-stmt:error-invalid-target-type:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt write-to-stream: second inout '")
+    (lookup *ecx *(ecx+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "' does not have the right type\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-write-to-stream-stmt:error-too-many-inouts:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt write-to-stream: too many inouts (2 required)\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-write-to-stream-stmt:error-unexpected-output:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt write-to-stream: unexpected output\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+check-mu-convert-stmt:  # stmt: (addr stmt), fn: (addr function), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    52/push-edx
+    56/push-esi
+    57/push-edi
+$check-mu-convert-stmt:get-output:
+    # esi = stmt
+    8b/-> *(ebp+8) 6/r32/esi
+    # var output/edi: (addr stmt-var) = stmt->outputs
+    (lookup *(esi+0x14) *(esi+0x18))  # Stmt1-outputs Stmt1-outputs => eax
+    89/<- %edi 0/r32/eax
+    # zero outputs
+    3d/compare-eax-and 0/imm32
+    0f 84/jump-if-= $check-mu-convert-stmt:error-no-output/disp32
+    # > 1 output
+    (lookup *(edi+8) *(edi+0xc))  # Stmt-var-next Stmt-var-next => eax
+    3d/compare-eax-and 0/imm32
+    0f 85/jump-if-!= $check-mu-convert-stmt:error-too-many-outputs/disp32
+$check-mu-convert-stmt:get-inout:
+    # var inout/esi: (addr stmt-var) = stmt->inouts
+    (lookup *(esi+0xc) *(esi+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+    89/<- %esi 0/r32/eax
+    # zero inouts
+    3d/compare-eax-and 0/imm32
+    0f 84/jump-if-= $check-mu-convert-stmt:error-no-inout/disp32
+    # > 1 inout
+    (lookup *(esi+8) *(esi+0xc))  # Stmt-var-next Stmt-var-next => eax
+    3d/compare-eax-and 0/imm32
+    0f 85/jump-if-!= $check-mu-convert-stmt:error-too-many-inouts/disp32
+$check-mu-convert-stmt:types:
+    # var inout-type/ecx: (addr type-tree) = inout->value->type
+    (lookup *esi *(esi+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *(eax+8) *(eax+0xc))  # Var-type Var-type => eax
+    89/<- %ecx 0/r32/eax
+    # if (inout->is-deref?) inout-type = inout-type->payload
+    8b/-> *(esi+0x10) 0/r32/eax  # Stmt-var-is-deref
+    3d/compare-eax-and 0/imm32/false
+    {
+      74/jump-if-= break/disp8
+      (lookup *(ecx+0xc) *(ecx+0x10))  # Type-tree-right Type-tree-right => eax
+      # if inout-type->right is null, t = inout-type->left
+      81 7/subop/compare *(eax+0xc) 0/imm32  # Type-tree-right
+      {
+        75/jump-if-!= break/disp8
+        (lookup *(eax+4) *(eax+8))  # Type-tree-left Type-tree-left => eax
+      }
+      89/<- %ecx 0/r32/eax
+    }
+    # if input is not int or float, abort
+    {
+      (is-simple-mu-type? %ecx 1)  # int => eax
+      3d/compare-eax-and 0/imm32/false
+      75/jump-if-!= break/disp8
+      (is-simple-mu-type? %ecx 0xf)  # float => eax
+      3d/compare-eax-and 0/imm32/false
+      75/jump-if-!= break/disp8
+      e9/jump $check-mu-convert-stmt:error-invalid-inout-type/disp32
+    }
+    # if output not in register, abort
+    (lookup *edi *(edi+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *(eax+0x18) *(eax+0x1c))  # Var-register Var-register => eax
+    3d/compare-eax-and 0/imm32
+    0f 84/jump-if-= $check-mu-convert-stmt:error-output-not-in-register/disp32
+    # var output-type/edx: (addr type-tree) = output->value->type
+    (lookup *edi *(edi+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *(eax+8) *(eax+0xc))  # Var-type Var-type => eax
+    89/<- %edx 0/r32/eax
+    # if output is not int or float, abort
+    {
+      (is-simple-mu-type? %edx 1)  # int => eax
+      3d/compare-eax-and 0/imm32/false
+      75/jump-if-!= break/disp8
+      (is-simple-mu-type? %edx 0xf)  # float => eax
+      3d/compare-eax-and 0/imm32/false
+      75/jump-if-!= break/disp8
+      e9/jump $check-mu-convert-stmt:error-invalid-output-type/disp32
+    }
+    # if both are ints, abort
+    {
+      (is-simple-mu-type? %edx 1)  # int => eax
+      3d/compare-eax-and 0/imm32/false
+      74/jump-if-= break/disp8
+      (is-simple-mu-type? %ecx 1)  # int => eax
+      3d/compare-eax-and 0/imm32/false
+      74/jump-if-= break/disp8
+      e9/jump $check-mu-convert-stmt:error-int-to-int/disp32
+    }
+    # if both are floats, abort
+    {
+      (is-simple-mu-type? %edx 0xf)  # float => eax
+      3d/compare-eax-and 0/imm32/false
+      74/jump-if-= break/disp8
+      (is-simple-mu-type? %ecx 0xf)  # float => eax
+      3d/compare-eax-and 0/imm32/false
+      74/jump-if-= break/disp8
+      e9/jump $check-mu-convert-stmt:error-float-to-float/disp32
+    }
+$check-mu-convert-stmt:end:
+    # . restore registers
+    5f/pop-to-edi
+    5e/pop-to-esi
+    5a/pop-to-edx
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+$check-mu-convert-stmt:error-no-inout:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt 'convert' expects an inout\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-convert-stmt:error-too-many-inouts:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt 'convert' must have just one inout\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-convert-stmt:error-no-output:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt 'convert' expects an output\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-convert-stmt:error-output-not-in-register:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt convert: output '")
+    (lookup *edi *(edi+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *eax *(eax+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "' not in a register\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-convert-stmt:error-too-many-outputs:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt 'convert' must have just one output\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-convert-stmt:error-invalid-inout-type:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt convert: inout '")
+    (lookup *esi *(esi+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *eax *(eax+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "' must be an int or float\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-convert-stmt:error-invalid-output-type:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt convert: output '")
+    (lookup *edi *(edi+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *eax *(eax+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "' must be an int or float\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-convert-stmt:error-int-to-int:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt convert: no need to convert int to int\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$check-mu-convert-stmt:error-float-to-float:
+    (write-buffered *(ebp+0x10) "fn ")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) ": stmt convert: no need to convert float to float\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+check-mu-call:  # stmt: (addr stmt), callee: (addr function), fn: (addr function), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # var type-parameters: (addr table (handle array byte) (addr type-tree) 8)
+    68/push 0/imm32
+    # var type-parameters-storage: (table (handle array byte) (addr type-tree) 8)
+    81 5/subop/subtract %esp 0x60/imm32
+    68/push 0x60/imm32/size
+    68/push 0/imm32/read
+    68/push 0/imm32/write
+    # save a pointer to type-parameters-storage at type-parameters
+    89/<- *(ebp-4) 4/r32/esp
+    (clear-stream *(ebp-4))
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    52/push-edx
+    53/push-ebx
+    56/push-esi
+    57/push-edi
+    # esi = stmt
+    8b/-> *(ebp+8) 6/r32/esi
+    # edi = callee
+    8b/-> *(ebp+0xc) 7/r32/edi
+    # var inouts/ecx: (addr stmt-var) = lookup(stmt->inouts)
+    (lookup *(esi+0xc) *(esi+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+    89/<- %ecx 0/r32/eax
+    # var expected/edx: (addr list var) = lookup(f->inouts)
+    (lookup *(edi+8) *(edi+0xc))  # Function-inouts Function-inouts => eax
+    89/<- %edx 0/r32/eax
+    {
+$check-mu-call:check-for-inouts:
+      # if (inouts == 0) break
+      81 7/subop/compare %ecx 0/imm32
+      0f 84/jump-if-= break/disp32
+      # if (expected == 0) error
+      81 7/subop/compare %edx 0/imm32
+      0f 84/jump-if-= break/disp32
+$check-mu-call:check-null-addr:
+      # if (inouts->value->name == "0") continue
+      (lookup *ecx *(ecx+4))  # Stmt-var-value Stmt-var-value => eax
+      (lookup *eax *(eax+4))  # Var-name Var-name => eax
+      (string-equal? %eax "0")  # => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 85/jump-if-!= $check-mu-call:continue-to-next-inout/disp32
+$check-mu-call:check-inout-type:
+      # var t/ebx: (addr type-tree) = inouts->value->type
+      (lookup *ecx *(ecx+4))  # Stmt-var-value Stmt-var-value => eax
+      (lookup *(eax+8) *(eax+0xc))  # Var-type Var-type => eax
+      89/<- %ebx 0/r32/eax
+      # if (inouts->is-deref?) t = t->right
+      81 7/subop/compare *(ecx+0x10) 0/imm32/false  # Stmt-var-is-deref
+      {
+        74/jump-if-= break/disp8
+        (lookup *(ebx+0xc) *(ebx+0x10))  # Type-tree-right Type-tree-right => eax
+        89/<- %ebx 0/r32/eax
+        # if t->right is null, t = t->left
+        81 7/subop/compare *(ebx+0xc) 0/imm32  # Type-tree-right
+        75/jump-if-!= break/disp8
+        (lookup *(ebx+4) *(ebx+8))  # Type-tree-left Type-tree-left => eax
+        89/<- %ebx 0/r32/eax
+      }
+      # var v2/eax: (addr v) = lookup(expected->value)
+      (lookup *edx *(edx+4))  # List-value List-value => eax
+      # var t2/eax: (addr type-tree) = lookup(v2->type)
+      (lookup *(eax+8) *(eax+0xc))  # Var-type Var-type => eax
+      # if (t != t2) error
+      (type-match? %eax %ebx *(ebp-4))  # => eax
+      3d/compare-eax-and 0/imm32/false
+      {
+        0f 85/jump-if-!= break/disp32
+        (write-buffered *(ebp+0x14) "fn ")
+        8b/-> *(ebp+0x10) 0/r32/eax
+        (lookup *eax *(eax+4))  # Function-name Function-name => eax
+        (write-buffered *(ebp+0x14) %eax)
+        (write-buffered *(ebp+0x14) ": call ")
+        (lookup *edi *(edi+4))  # Function-name Function-name => eax
+        (write-buffered *(ebp+0x14) %eax)
+        (write-buffered *(ebp+0x14) ": type for inout '")
+        (lookup *ecx *(ecx+4))  # Stmt-var-value Stmt-var-value => eax
+        (lookup *eax *(eax+4))  # Var-name Var-name => eax
+        (write-buffered *(ebp+0x14) %eax)
+        (write-buffered *(ebp+0x14) "' is not right\n")
+        (flush *(ebp+0x14))
+        (stop *(ebp+0x18) 1)
+      }
+$check-mu-call:continue-to-next-inout:
+      # inouts = lookup(inouts->next)
+      (lookup *(ecx+8) *(ecx+0xc))  # Stmt-var-next Stmt-var-next => eax
+      89/<- %ecx 0/r32/eax
+      # expected = lookup(expected->next)
+      (lookup *(edx+8) *(edx+0xc))  # List-next List-next => eax
+      89/<- %edx 0/r32/eax
+      #
+      e9/jump loop/disp32
+    }
+$check-mu-call:check-inout-count:
+    # if (inouts == expected) proceed
+    39/compare %ecx 2/r32/edx
+    {
+      0f 84/jump-if-= break/disp32
+      # exactly one of the two is null
+      # if (inouts == 0) error("too many inouts")
+      {
+        81 7/subop/compare %ecx 0/imm32
+        0f 84/jump-if-= break/disp32
+        (write-buffered *(ebp+0x14) "fn ")
+        8b/-> *(ebp+0x10) 0/r32/eax
+        (lookup *eax *(eax+4))  # Function-name Function-name => eax
+        (write-buffered *(ebp+0x14) %eax)
+        (write-buffered *(ebp+0x14) ": call ")
+        (lookup *edi *(edi+4))  # Function-name Function-name => eax
+        (write-buffered *(ebp+0x14) %eax)
+        (write-buffered *(ebp+0x14) ": too many inouts\n")
+        (flush *(ebp+0x14))
+        (stop *(ebp+0x18) 1)
+      }
+      # if (expected == 0) error("too few inouts")
+      {
+        81 7/subop/compare %edx 0/imm32
+        0f 84/jump-if-= break/disp32
+        (write-buffered *(ebp+0x14) "fn ")
+        8b/-> *(ebp+0x10) 0/r32/eax
+        (lookup *eax *(eax+4))  # Function-name Function-name => eax
+        (write-buffered *(ebp+0x14) %eax)
+        (write-buffered *(ebp+0x14) ": call ")
+        (lookup *edi *(edi+4))  # Function-name Function-name => eax
+        (write-buffered *(ebp+0x14) %eax)
+        (write-buffered *(ebp+0x14) ": too few inouts\n")
+        (flush *(ebp+0x14))
+        (stop *(ebp+0x18) 1)
+      }
+    }
+$check-mu-call:check-outputs:
+    # var outputs/ecx: (addr stmt-var) = lookup(stmt->outputs)
+    (lookup *(esi+0x14) *(esi+0x18))  # Stmt1-outputs Stmt1-outputs => eax
+    89/<- %ecx 0/r32/eax
+    # var expected/edx: (addr list var) = lookup(f->outputs)
+    (lookup *(edi+0x10) *(edi+0x14))  # Function-outputs Function-outputs => eax
+    89/<- %edx 0/r32/eax
+    {
+$check-mu-call:check-for-outputs:
+      # if (outputs == 0) break
+      81 7/subop/compare %ecx 0/imm32
+      0f 84/jump-if-= break/disp32
+      # if (expected == 0) error
+      81 7/subop/compare %edx 0/imm32
+      0f 84/jump-if-= break/disp32
+$check-mu-call:check-output-type:
+      # var v/eax: (addr v) = lookup(outputs->value)
+      (lookup *ecx *(ecx+4))  # Stmt-var-value Stmt-var-value => eax
+      # var t/ebx: (addr type-tree) = lookup(v->type)
+      (lookup *(eax+8) *(eax+0xc))  # Var-type Var-type => eax
+      89/<- %ebx 0/r32/eax
+      # if (outputs->is-deref?) t = t->right  # TODO: check that t->left is an addr
+      81 7/subop/compare *(ecx+0x10) 0/imm32/false  # Stmt-var-is-deref
+      {
+        74/jump-if-= break/disp8
+        (lookup *(ebx+0xc) *(ebx+0x10))  # Type-tree-right Type-tree-right => eax
+        89/<- %ebx 0/r32/eax
+      }
+      # var v2/eax: (addr v) = lookup(expected->value)
+      (lookup *edx *(edx+4))  # List-value List-value => eax
+      # var t2/eax: (addr type-tree) = lookup(v2->type)
+      (lookup *(eax+8) *(eax+0xc))  # Var-type Var-type => eax
+      # if (t != t2) error
+      (type-match? %eax %ebx *(ebp-4))  # => eax
+      3d/compare-eax-and 0/imm32/false
+      {
+        0f 85/jump-if-!= break/disp32
+        (write-buffered *(ebp+0x14) "fn ")
+        8b/-> *(ebp+0x10) 0/r32/eax
+        (lookup *eax *(eax+4))  # Function-name Function-name => eax
+        (write-buffered *(ebp+0x14) %eax)
+        (write-buffered *(ebp+0x14) ": call ")
+        (lookup *edi *(edi+4))  # Function-name Function-name => eax
+        (write-buffered *(ebp+0x14) %eax)
+        (write-buffered *(ebp+0x14) ": type for output '")
+        (lookup *ecx *(ecx+4))  # Stmt-var-value Stmt-var-value => eax
+        (lookup *eax *(eax+4))  # Var-name Var-name => eax
+        (write-buffered *(ebp+0x14) %eax)
+        (write-buffered *(ebp+0x14) "' is not right\n")
+        (flush *(ebp+0x14))
+        (stop *(ebp+0x18) 1)
+      }
+$check-mu-call:check-output-register:
+      # var v/eax: (addr v) = lookup(outputs->value)
+      (lookup *ecx *(ecx+4))  # Stmt-var-value Stmt-var-value => eax
+      # var r/ebx: (addr array byte) = lookup(v->register)
+      (lookup *(eax+18) *(eax+0x1c))  # Var-register Var-register => eax
+      89/<- %ebx 0/r32/eax
+      # if (r == 0) error
+      3d/compare-eax-and 0/imm32
+      {
+        0f 85/jump-if-!= break/disp32
+        (write-buffered *(ebp+0x14) "fn ")
+        8b/-> *(ebp+0x10) 0/r32/eax
+        (lookup *eax *(eax+4))  # Function-name Function-name => eax
+        (write-buffered *(ebp+0x14) %eax)
+        (write-buffered *(ebp+0x14) ": call ")
+        (lookup *edi *(edi+4))  # Function-name Function-name => eax
+        (write-buffered *(ebp+0x14) %eax)
+        (write-buffered *(ebp+0x14) ": output '")
+        (lookup *ecx *(ecx+4))  # Stmt-var-value Stmt-var-value => eax
+        (lookup *eax *(eax+4))  # Var-name Var-name => eax
+        (write-buffered *(ebp+0x14) %eax)
+        (write-buffered *(ebp+0x14) "' is not in a register\n")
+        (flush *(ebp+0x14))
+        (stop *(ebp+0x18) 1)
+      }
+      # var v2/eax: (addr v) = lookup(expected->value)
+      (lookup *edx *(edx+4))  # Stmt-var-value Stmt-var-value => eax
+      # var r2/eax: (addr array byte) = lookup(v2->register)
+      (lookup *(eax+18) *(eax+0x1c))  # Var-register Var-register => eax
+      # if (r != r2) error
+      (string-equal? %eax %ebx)  # => eax
+      3d/compare-eax-and 0/imm32/false
+      {
+        0f 85/jump-if-!= break/disp32
+        (write-buffered *(ebp+0x14) "fn ")
+        8b/-> *(ebp+0x10) 0/r32/eax
+        (lookup *eax *(eax+4))  # Function-name Function-name => eax
+        (write-buffered *(ebp+0x14) %eax)
+        (write-buffered *(ebp+0x14) ": call ")
+        (lookup *edi *(edi+4))  # Function-name Function-name => eax
+        (write-buffered *(ebp+0x14) %eax)
+        (write-buffered *(ebp+0x14) ": register for output '")
+        (lookup *ecx *(ecx+4))  # Stmt-var-value Stmt-var-value => eax
+        (lookup *eax *(eax+4))  # Var-name Var-name => eax
+        (write-buffered *(ebp+0x14) %eax)
+        (write-buffered *(ebp+0x14) "' is not right\n")
+        (flush *(ebp+0x14))
+        (stop *(ebp+0x18) 1)
+      }
+$check-mu-call:continue-to-next-output:
+      # outputs = lookup(outputs->next)
+      (lookup *(ecx+8) *(ecx+0xc))  # Stmt-var-next Stmt-var-next => eax
+      89/<- %ecx 0/r32/eax
+      # expected = lookup(expected->next)
+      (lookup *(edx+8) *(edx+0xc))  # List-next List-next => eax
+      89/<- %edx 0/r32/eax
+      #
+      e9/jump loop/disp32
+    }
+$check-mu-call:check-output-count:
+    # if (outputs == expected) proceed
+    39/compare %ecx 2/r32/edx
+    {
+      0f 84/jump-if-= break/disp32
+      # exactly one of the two is null
+      # if (outputs == 0) error("too many outputs")
+      {
+        81 7/subop/compare %ecx 0/imm32
+        0f 84/jump-if-= break/disp32
+        (write-buffered *(ebp+0x14) "fn ")
+        8b/-> *(ebp+0x10) 0/r32/eax
+        (lookup *eax *(eax+4))  # Function-name Function-name => eax
+        (write-buffered *(ebp+0x14) %eax)
+        (write-buffered *(ebp+0x14) ": call ")
+        (lookup *edi *(edi+4))  # Function-name Function-name => eax
+        (write-buffered *(ebp+0x14) %eax)
+        (write-buffered *(ebp+0x14) ": too many outputs\n")
+        (flush *(ebp+0x14))
+        (stop *(ebp+0x18) 1)
+      }
+      # if (expected == 0) error("too few outputs")
+      {
+        81 7/subop/compare %edx 0/imm32
+        0f 84/jump-if-= break/disp32
+        (write-buffered *(ebp+0x14) "fn ")
+        8b/-> *(ebp+0x10) 0/r32/eax
+        (lookup *eax *(eax+4))  # Function-name Function-name => eax
+        (write-buffered *(ebp+0x14) %eax)
+        (write-buffered *(ebp+0x14) ": call ")
+        (lookup *edi *(edi+4))  # Function-name Function-name => eax
+        (write-buffered *(ebp+0x14) %eax)
+        (write-buffered *(ebp+0x14) ": too few outputs\n")
+        (flush *(ebp+0x14))
+        (stop *(ebp+0x18) 1)
+      }
+    }
+$check-mu-call:end:
+    # . restore registers
+    5f/pop-to-edi
+    5e/pop-to-esi
+    5b/pop-to-ebx
+    5a/pop-to-edx
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . reclaim locals exclusively on the stack
+    81 0/subop/add %esp 0x70/imm32
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+# like type-equal? but takes literals type parameters into account
+type-match?:  # def: (addr type-tree), call: (addr type-tree), type-parameters: (addr table (handle array byte) (addr type-tree)) -> result/eax: boolean
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # if (call is literal and def is numberlike) return true
+    {
+$type-match?:check-literal-int:
+      (is-simple-mu-type? *(ebp+0xc) 0)  # literal => eax
+      3d/compare-eax-and 0/imm32/false
+      74/jump-if-= break/disp8
+      (is-mu-numberlike-output? *(ebp+8))  # => eax
+      3d/compare-eax-and 0/imm32/false
+      74/jump-if-= break/disp8
+      b8/copy-to-eax 1/imm32/true
+      e9/jump $type-match?:end/disp32
+    }
+    # if (call is literal-string and def is string) return true
+    {
+$type-match?:check-literal-string:
+      (is-simple-mu-type? *(ebp+0xc) 0x10)  # literal-string => eax
+      3d/compare-eax-and 0/imm32/false
+      74/jump-if-= break/disp8
+      (is-mu-string-type? *(ebp+8))  # => eax
+      3d/compare-eax-and 0/imm32/false
+      74/jump-if-= break/disp8
+      b8/copy-to-eax 1/imm32/true
+      e9/jump $type-match?:end/disp32
+    }
+$type-match?:baseline:
+    # otherwise fall back
+    (type-component-match? *(ebp+8) *(ebp+0xc) *(ebp+0x10))  # => eax
+$type-match?:end:
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+type-component-match?:  # def: (addr type-tree), call: (addr type-tree), type-parameters: (addr table (handle array byte) (addr type-tree)) -> result/eax: boolean
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    51/push-ecx
+    52/push-edx
+    53/push-ebx
+    # ecx = def
+    8b/-> *(ebp+8) 1/r32/ecx
+    # edx = call
+    8b/-> *(ebp+0xc) 2/r32/edx
+$type-component-match?:compare-addr:
+    # if (def == call) return true
+    8b/-> %ecx 0/r32/eax  # Var-type
+    39/compare %edx 0/r32/eax  # Var-type
+    b8/copy-to-eax 1/imm32/true
+    0f 84/jump-if-= $type-component-match?:end/disp32
+    # if (def == 0) return false
+    b8/copy-to-eax 0/imm32/false
+    81 7/subop/compare %ecx 0/imm32  # Type-tree-is-atom
+    0f 84/jump-if-= $type-component-match?:end/disp32
+    # if (call == 0) return false
+    81 7/subop/compare %edx 0/imm32  # Type-tree-is-atom
+    0f 84/jump-if-= $type-component-match?:end/disp32
+    # if def is a type parameter, just check in type-parameters
+    {
+$type-component-match?:check-type-parameter:
+      81 7/subop/compare *ecx 0/imm32/false  # Type-tree-is-atom
+      74/jump-if-= break/disp8
+      81 7/subop/compare *(ecx+4) 0xa/imm32/type-parameter  # Type-tree-value
+      75/jump-if-!= break/disp8
+$type-component-match?:type-parameter:
+      (type-parameter-match? *(ecx+8) *(ecx+0xc)  %edx  *(ebp+0x10))  # => eax
+      e9/jump $type-component-match?:end/disp32
+    }
+    # if def is a list containing just a type parameter, just check in type-parameters
+    {
+$type-component-match?:check-list-type-parameter:
+      # if def is a list..
+      81 7/subop/compare *ecx 0/imm32/false  # Type-tree-is-atom
+      75/jump-if-!= break/disp8
+      #   ..that's a singleton
+      81 7/subop/compare *(ecx+0xc) 0/imm32  # Type-tree-left
+      75/jump-if-!= break/disp8
+      #   ..and whose head is a type parameter
+      (lookup *(ecx+4) *(ecx+8))  # Type-tree-left Type-tree-left => eax
+      81 7/subop/compare *eax 0/imm32/false  # Type-tree-is-atom
+      74/jump-if-= break/disp8
+      81 7/subop/compare *(eax+4) 0xa/imm32/type-parameter  # Type-tree-value
+      75/jump-if-!= break/disp8
+$type-component-match?:list-type-parameter:
+      (type-parameter-match? *(eax+8) *(eax+0xc)  %edx  *(ebp+0x10))  # => eax
+      e9/jump $type-component-match?:end/disp32
+    }
+$type-component-match?:compare-atom-state:
+    # if (def->is-atom? != call->is-atom?) return false
+    8b/-> *ecx 3/r32/ebx  # Type-tree-is-atom
+    39/compare *edx 3/r32/ebx  # Type-tree-is-atom
+    b8/copy-to-eax 0/imm32/false
+    0f 85/jump-if-!= $type-component-match?:end/disp32
+    # if def->is-atom? return (def->value == call->value)
+    {
+$type-component-match?:check-atom:
+      81 7/subop/compare %ebx 0/imm32/false
+      74/jump-if-= break/disp8
+$type-component-match?:is-atom:
+      8b/-> *(ecx+4) 0/r32/eax  # Type-tree-value
+      39/compare *(edx+4) 0/r32/eax  # Type-tree-value
+      0f 94/set-if-= %al
+      81 4/subop/and %eax 0xff/imm32
+      e9/jump $type-component-match?:end/disp32
+    }
+$type-component-match?:check-left:
+    # if (!type-component-match?(def->left, call->left)) return false
+    (lookup *(ecx+4) *(ecx+8))  # Type-tree-left Type-tree-left => eax
+    89/<- %ebx 0/r32/eax
+    (lookup *(edx+4) *(edx+8))  # Type-tree-left Type-tree-left => eax
+    (type-component-match? %ebx %eax *(ebp+0x10))  # => eax
+    3d/compare-eax-and 0/imm32/false
+    74/jump-if-= $type-component-match?:end/disp8
+$type-component-match?:check-right:
+    # return type-component-match?(def->right, call->right)
+    (lookup *(ecx+0xc) *(ecx+0x10))  # Type-tree-right Type-tree-right => eax
+    89/<- %ebx 0/r32/eax
+    (lookup *(edx+0xc) *(edx+0x10))  # Type-tree-right Type-tree-right => eax
+    (type-component-match? %ebx %eax *(ebp+0x10))  # => eax
+$type-component-match?:end:
+    # . restore registers
+    5b/pop-to-ebx
+    5a/pop-to-edx
+    59/pop-to-ecx
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+type-parameter-match?:  # type-parameter-name: (handle array byte), type: (addr type-tree), type-parameters: (addr table (handle array byte) (addr type-tree)) -> result/eax: boolean
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    51/push-ecx
+    #
+    (get-or-insert-handle *(ebp+0x14)  *(ebp+8) *(ebp+0xc)  0xc)  # => eax
+    # if parameter wasn't saved, save it
+    {
+      81 7/subop/compare *eax 0/imm32
+      75/jump-if-!= break/disp8
+      8b/-> *(ebp+0x10) 1/r32/ecx
+      89/<- *eax 1/r32/ecx
+    }
+    #
+    (type-equal? *(ebp+0x10) *eax)  # => eax
+$type-parameter-match?:end:
+    # . restore registers
+    59/pop-to-ecx
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+size-of:  # v: (addr var) -> result/eax: int
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    51/push-ecx
+    # var t/ecx: (addr type-tree) = lookup(v->type)
+    8b/-> *(ebp+8) 1/r32/ecx
+#?     (write-buffered Stderr "size-of ")
+#?     (write-int32-hex-buffered Stderr %ecx)
+#?     (write-buffered Stderr Newline)
+#?     (write-buffered Stderr "type allocid: ")
+#?     (write-int32-hex-buffered Stderr *(ecx+8))
+#?     (write-buffered Stderr Newline)
+#?     (flush Stderr)
+    (lookup *(ecx+8) *(ecx+0xc))  # Var-type Var-type => eax
+    89/<- %ecx 0/r32/eax
+    # if is-mu-array?(t) return size-of-array(t)
+    {
+      (is-mu-array? %ecx)  # => eax
+      3d/compare-eax-and 0/imm32/false
+      74/jump-if-= break/disp8
+      (size-of-array %ecx)  # => eax
+      eb/jump $size-of:end/disp8
+    }
+    # if is-mu-stream?(t) return size-of-stream(t)
+    {
+      (is-mu-stream? %ecx)  # => eax
+      3d/compare-eax-and 0/imm32/false
+      74/jump-if-= break/disp8
+      (size-of-stream %ecx)  # => eax
+      eb/jump $size-of:end/disp8
+    }
+    # if (!t->is-atom?) t = lookup(t->left)
+    {
+      81 7/subop/compare *ecx 0/imm32/false  # Type-tree-is-atom
+      75/jump-if-!= break/disp8
+      (lookup *(ecx+4) *(ecx+8))  # Type-tree-left Type-tree-left => eax
+      89/<- %ecx 0/r32/eax
+    }
+    # TODO: assert t->is-atom?
+    (size-of-type-id *(ecx+4))  # Type-tree-value => eax
+$size-of:end:
+    # . restore registers
+    59/pop-to-ecx
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+size-of-deref:  # v: (addr var) -> result/eax: int
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    51/push-ecx
+    # var t/ecx: (addr type-tree) = lookup(v->type)
+    8b/-> *(ebp+8) 1/r32/ecx
+    (lookup *(ecx+8) *(ecx+0xc))  # Var-type Var-type => eax
+    89/<- %ecx 0/r32/eax
+    # TODO: assert(t is an addr)
+    # t = lookup(t->right)
+    (lookup *(ecx+0xc) *(ecx+0x10))  # Type-tree-right Type-tree-right => eax
+    89/<- %ecx 0/r32/eax
+    # if is-mu-array?(t) return size-of-array(t)
+    {
+      (is-mu-array? %ecx)  # => eax
+      3d/compare-eax-and 0/imm32/false
+      74/jump-if-= break/disp8
+      (size-of-array %ecx)  # => eax
+      eb/jump $size-of-deref:end/disp8
+    }
+    # if is-mu-stream?(t) return size-of-stream(t)
+    {
+      (is-mu-stream? %ecx)  # => eax
+      3d/compare-eax-and 0/imm32/false
+      74/jump-if-= break/disp8
+      (size-of-stream %ecx)  # => eax
+      eb/jump $size-of-deref:end/disp8
+    }
+    # if (!t->is-atom?) t = lookup(t->left)
+    {
+      81 7/subop/compare *ecx 0/imm32/false  # Type-tree-is-atom
+      75/jump-if-!= break/disp8
+      (lookup *(ecx+4) *(ecx+8))  # Type-tree-left Type-tree-left => eax
+      89/<- %ecx 0/r32/eax
+    }
+    # TODO: assert t->is-atom?
+    (size-of-type-id *(ecx+4))  # Type-tree-value => eax
+$size-of-deref:end:
+    # . restore registers
+    59/pop-to-ecx
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+is-mu-array?:  # t: (addr type-tree) -> result/eax: boolean
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    51/push-ecx
+    # ecx = t
+    8b/-> *(ebp+8) 1/r32/ecx
+    # if t->is-atom?, return false
+    81 7/subop/compare *ecx 0/imm32/false  # Type-tree-is-atom
+    75/jump-if-!= $is-mu-array?:return-false/disp8
+    # if !t->left->is-atom?, return false
+    (lookup *(ecx+4) *(ecx+8))  # Type-tree-left Type-tree-left => eax
+    81 7/subop/compare *eax 0/imm32/false  # Type-tree-is-atom
+    74/jump-if-= $is-mu-array?:return-false/disp8
+    # return t->left->value == array
+    81 7/subop/compare *(eax+4) 3/imm32/array-type-id  # Type-tree-value
+    0f 94/set-if-= %al
+    81 4/subop/and %eax 0xff/imm32
+    eb/jump $is-mu-array?:end/disp8
+$is-mu-array?:return-false:
+    b8/copy-to-eax 0/imm32/false
+$is-mu-array?:end:
+    # . restore registers
+    59/pop-to-ecx
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+# size of a statically allocated array where the size is part of the type expression
+size-of-array:  # a: (addr type-tree) -> result/eax: int
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    51/push-ecx
+    52/push-edx
+    #
+    8b/-> *(ebp+8) 1/r32/ecx
+    # TODO: assert that a->left is 'array'
+    (lookup *(ecx+0xc) *(ecx+0x10))  # Type-tree-right Type-tree-right => eax
+    89/<- %ecx 0/r32/eax
+    # var elem-type/edx: type-id = a->right->left->value
+    (lookup *(ecx+4) *(ecx+8))  # Type-tree-left Type-tree-left => eax
+    8b/-> *(eax+4) 2/r32/edx  # Type-tree-value
+    # TODO: assert that a->right->right->left->value == size
+    # var array-size/ecx: int = a->right->right->left->value-size
+    (lookup *(ecx+0xc) *(ecx+0x10))  # Type-tree-right Type-tree-right => eax
+    (lookup *(eax+4) *(eax+8))  # Type-tree-left Type-tree-left => eax
+    8b/-> *(eax+8) 1/r32/ecx  # Type-tree-value-size
+    # return 4 + array-size * size-of(elem-type)
+    (size-of-type-id-as-array-element %edx)  # => eax
+    f7 4/subop/multiply-into-edx-eax %ecx
+    05/add-to-eax 4/imm32  # for array size
+    # TODO: check edx for overflow
+$size-of-array:end:
+    # . restore registers
+    5a/pop-to-edx
+    59/pop-to-ecx
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+is-mu-stream?:  # t: (addr type-tree) -> result/eax: boolean
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    51/push-ecx
+    # ecx = t
+    8b/-> *(ebp+8) 1/r32/ecx
+    # if t->is-atom?, return false
+    81 7/subop/compare *ecx 0/imm32/false  # Type-tree-is-atom
+    75/jump-if-!= $is-mu-stream?:return-false/disp8
+    # if !t->left->is-atom?, return false
+    (lookup *(ecx+4) *(ecx+8))  # Type-tree-left Type-tree-left => eax
+    81 7/subop/compare *eax 0/imm32/false  # Type-tree-is-atom
+    74/jump-if-= $is-mu-stream?:return-false/disp8
+    # return t->left->value == stream
+    81 7/subop/compare *(eax+4) 0xb/imm32/stream-type-id  # Type-tree-value
+    0f 94/set-if-= %al
+    81 4/subop/and %eax 0xff/imm32
+    eb/jump $is-mu-stream?:end/disp8
+$is-mu-stream?:return-false:
+    b8/copy-to-eax 0/imm32/false
+$is-mu-stream?:end:
+    # . restore registers
+    59/pop-to-ecx
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+# size of a statically allocated stream where the size is part of the type expression
+size-of-stream:  # a: (addr type-tree) -> result/eax: int
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    #
+    (size-of-array *(ebp+8))  # assumes we ignore the actual type name 'array' in the type
+    05/add-to-eax 8/imm32  # for read/write pointers
+$size-of-stream:end:
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+size-of-type-id:  # t: type-id -> result/eax: int
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    51/push-ecx
+    # var out/ecx: (handle typeinfo)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %ecx 4/r32/esp
+    # eax = t
+    8b/-> *(ebp+8) 0/r32/eax
+    # if t is a literal, return 0
+    3d/compare-eax-and 0/imm32
+    0f 84/jump-if-= $size-of-type-id:end/disp32  # eax changes type from type-id to int
+    # if t is a byte, return 4 (because we don't really support non-multiples of 4)
+    3d/compare-eax-and 8/imm32/byte
+    {
+      75/jump-if-!= break/disp8
+      b8/copy-to-eax 4/imm32
+      eb/jump $size-of-type-id:end/disp8
+    }
+    # if t is a handle, return 8
+    3d/compare-eax-and 4/imm32/handle
+    {
+      75/jump-if-!= break/disp8
+      b8/copy-to-eax 8/imm32
+      eb/jump $size-of-type-id:end/disp8  # eax changes type from type-id to int
+    }
+    # if t is a slice, return 8
+    3d/compare-eax-and 0xc/imm32/slice
+    {
+      75/jump-if-!= break/disp8
+      b8/copy-to-eax 8/imm32
+      eb/jump $size-of-type-id:end/disp8  # eax changes type from type-id to int
+    }
+    # if t is a user-defined type, return its size
+    # TODO: support non-atom type
+    (find-typeinfo %eax %ecx)
+    {
+      81 7/subop/compare *ecx 0/imm32
+      74/jump-if-= break/disp8
+$size-of-type-id:user-defined:
+      (lookup *ecx *(ecx+4))  # => eax
+      8b/-> *(eax+0xc) 0/r32/eax  # Typeinfo-total-size-in-bytes
+      eb/jump $size-of-type-id:end/disp8
+    }
+    # otherwise return the word size
+    b8/copy-to-eax 4/imm32
+$size-of-type-id:end:
+    # . reclaim locals
+    81 0/subop/add %esp 8/imm32
+    # . restore registers
+    59/pop-to-ecx
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+# Minor violation of our type system since it returns an addr. But we could
+# replace it with a handle some time.
+# Returns null if t is an atom.
+type-tail:  # t: (addr type-tree) -> out/eax: (addr type-tree)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    51/push-ecx
+    # eax = 0
+    b8/copy-to-eax 0/imm32
+    # ecx = t
+    8b/-> *(ebp+8) 1/r32/ecx
+$type-tail:check-atom:
+    # if t->is-atom? return 0
+    81 7/subop/compare *ecx 0/imm32/false  # Type-tree-is-atom
+    0f 85/jump-if-!= $type-tail:end/disp32
+    # var tail = t->right
+    (lookup *(ecx+0xc) *(ecx+0x10))  # Type-tree-right Type-tree-right => eax
+    89/<- %ecx 0/r32/eax
+$type-tail:check-singleton:
+    # if (tail->right == 0) return tail->left
+    {
+      81 7/subop/compare *(ecx+0xc) 0/imm32  # Type-tree-right
+      75/jump-if-!= break/disp8
+      (lookup *(ecx+4) *(ecx+8))  # Type-tree-left Type-tree-left => eax
+      e9/jump $type-tail:end/disp32
+    }
+    # if tail->right->left is an array-capacity, return tail->left
+    {
+$type-tail:check-array-capacity:
+      (lookup *(ecx+0xc) *(ecx+0x10))  # Type-tree-right Type-tree-right => eax
+      81 7/subop/compare *eax 0/imm32/false  # Type-tree-is-atom
+      75/jump-if-!= break/disp8
+$type-tail:check-array-capacity-1:
+      (lookup *(eax+4) *(eax+8))  # Type-tree-left Type-tree-left => eax
+      3d/compare-eax-and 0/imm32
+      74/jump-if-= break/disp8
+$type-tail:check-array-capacity-2:
+      (is-simple-mu-type? %eax 9)  # array-capacity => eax
+      3d/compare-eax-and 0/imm32/false
+      74/jump-if-= break/disp8
+$type-tail:array-capacity:
+      (lookup *(ecx+4) *(ecx+8))  # Type-tree-left Type-tree-left => eax
+      eb/jump $type-tail:end/disp8
+    }
+$type-tail:check-compound-left:
+    # if !tail->left->is-atom? return tail->left
+    (lookup *(ecx+4) *(ecx+8))  # Type-tree-left Type-tree-left => eax
+    81 7/subop/compare *eax 0/imm32/false  # Type-tree-is-atom
+    74/jump-if-= $type-tail:end/disp8
+$type-tail:return-tail:
+    # return tail
+    89/<- %eax 1/r32/ecx
+$type-tail:end:
+    # . restore registers
+    59/pop-to-ecx
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+type-equal?:  # a: (addr type-tree), b: (addr type-tree) -> result/eax: boolean
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    51/push-ecx
+    52/push-edx
+    53/push-ebx
+    # ecx = a
+    8b/-> *(ebp+8) 1/r32/ecx
+    # edx = b
+    8b/-> *(ebp+0xc) 2/r32/edx
+$type-equal?:compare-addr:
+    # if (a == b) return true
+    8b/-> %ecx 0/r32/eax  # Var-type
+    39/compare %edx 0/r32/eax  # Var-type
+    b8/copy-to-eax 1/imm32/true
+    0f 84/jump-if-= $type-equal?:end/disp32
+$type-equal?:compare-null-a:
+    # if (a == 0) return false
+    b8/copy-to-eax 0/imm32/false
+    81 7/subop/compare %ecx 0/imm32
+    0f 84/jump-if-= $type-equal?:end/disp32
+$type-equal?:compare-null-b:
+    # if (b == 0) return false
+    81 7/subop/compare %edx 0/imm32
+    0f 84/jump-if-= $type-equal?:end/disp32
+$type-equal?:compare-atom-state:
+    # if (a->is-atom? != b->is-atom?) return false
+    8b/-> *ecx 3/r32/ebx  # Type-tree-is-atom
+    39/compare *edx 3/r32/ebx  # Type-tree-is-atom
+    b8/copy-to-eax 0/imm32/false
+    0f 85/jump-if-!= $type-equal?:end/disp32
+    # if a->is-atom? return (a->value == b->value)
+    {
+$type-equal?:check-atom:
+      81 7/subop/compare %ebx 0/imm32/false
+      74/jump-if-= break/disp8
+$type-equal?:is-atom:
+      8b/-> *(ecx+4) 0/r32/eax  # Type-tree-value
+      39/compare *(edx+4) 0/r32/eax  # Type-tree-value
+      0f 94/set-if-= %al
+      81 4/subop/and %eax 0xff/imm32
+      e9/jump $type-equal?:end/disp32
+    }
+$type-equal?:check-left:
+    # if (!type-equal?(a->left, b->left)) return false
+    (lookup *(ecx+4) *(ecx+8))  # Type-tree-left Type-tree-left => eax
+    89/<- %ebx 0/r32/eax
+    (lookup *(edx+4) *(edx+8))  # Type-tree-left Type-tree-left => eax
+    (type-equal? %eax %ebx)  # => eax
+    3d/compare-eax-and 0/imm32/false
+    74/jump-if-= $type-equal?:end/disp8
+$type-equal?:check-right:
+    # return type-equal?(a->right, b->right)
+    (lookup *(ecx+0xc) *(ecx+0x10))  # Type-tree-right Type-tree-right => eax
+    89/<- %ebx 0/r32/eax
+    (lookup *(edx+0xc) *(edx+0x10))  # Type-tree-right Type-tree-right => eax
+    (type-equal? %eax %ebx)  # => eax
+$type-equal?:end:
+    # . restore registers
+    5b/pop-to-ebx
+    5a/pop-to-edx
+    59/pop-to-ecx
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+#######################################################
+# Code-generation
+#######################################################
+
+== data
+
+# Global state added to each var record when performing code-generation.
+Curr-local-stack-offset:  # (addr int)
+    0/imm32
+
+== code
+
+# We may not need to pass err/ed everywhere here. I think they're relics of Mu
+# getting type checks later in life.
+# But we do need them for runtime checks, particularly array index bounds checks.
+# So perhaps it's not worth taking them out. They're a safety net.
+
+emit-subx:  # out: (addr buffered-file), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    # var curr/eax: (addr function) = *Program->functions
+    (lookup *_Program-functions *_Program-functions->payload)  # => eax
+    {
+      # if (curr == null) break
+      3d/compare-eax-and 0/imm32
+      0f 84/jump-if-= break/disp32
+      (emit-subx-function *(ebp+8) %eax *(ebp+0xc) *(ebp+0x10))
+      # curr = lookup(curr->next)
+      (lookup *(eax+0x20) *(eax+0x24))  # Function-next Function-next => eax
+      e9/jump loop/disp32
+    }
+$emit-subx:end:
+    # . restore registers
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+emit-subx-function:  # out: (addr buffered-file), f: (addr function), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # some preprocessing
+    (populate-mu-type-offsets-in-inouts *(ebp+0xc))
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    52/push-edx
+    # initialize some global state
+    c7 0/subop/copy *Curr-block-depth 1/imm32  # Important: keep this in sync with the parse phase
+    c7 0/subop/copy *Curr-local-stack-offset 0/imm32
+    # ecx = f
+    8b/-> *(ebp+0xc) 1/r32/ecx
+    # var vars/edx: (stack (addr var) 256)
+    81 5/subop/subtract %esp 0xc00/imm32
+    68/push 0xc00/imm32/size
+    68/push 0/imm32/top
+    89/<- %edx 4/r32/esp
+    # var name/eax: (addr array byte) = lookup(f->name)
+    (lookup *ecx *(ecx+4))  # Function-name Function-name => eax
+    #
+    (write-buffered *(ebp+8) %eax)
+    (write-buffered *(ebp+8) ":\n")
+    (emit-subx-prologue *(ebp+8))
+    # var body/eax: (addr block) = lookup(f->body)
+    (lookup *(ecx+0x18) *(ecx+0x1c))  # Function-body Function-body => eax
+    #
+    (emit-subx-block *(ebp+8) %eax %edx *(ebp+0xc) *(ebp+0x10) *(ebp+0x14))
+    (emit-subx-epilogue *(ebp+8))
+    # TODO: validate that *Curr-block-depth and *Curr-local-stack-offset have
+    # been cleaned up
+$emit-subx-function:end:
+    # . reclaim locals
+    81 0/subop/add %esp 0xc08/imm32
+    # . restore registers
+    5a/pop-to-edx
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+populate-mu-type-offsets-in-inouts:  # f: (addr function)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    52/push-edx
+    53/push-ebx
+    57/push-edi
+    # var next-offset/edx: int = 8
+    ba/copy-to-edx 8/imm32
+    # var curr/ecx: (addr list var) = lookup(f->inouts)
+    8b/-> *(ebp+8) 1/r32/ecx
+    (lookup *(ecx+8) *(ecx+0xc))  # Function-inouts Function-inouts => eax
+    89/<- %ecx 0/r32/eax
+    {
+$populate-mu-type-offsets-in-inouts:loop:
+      81 7/subop/compare %ecx 0/imm32
+      74/jump-if-= break/disp8
+      # var v/ebx: (addr var) = lookup(curr->value)
+      (lookup *ecx *(ecx+4))  # List-value List-value => eax
+      89/<- %ebx 0/r32/eax
+#?       (lookup *ebx *(ebx+4))
+#?       (write-buffered Stderr "setting offset of fn inout ")
+#?       (write-buffered Stderr %eax)
+#?       (write-buffered Stderr "@")
+#?       (write-int32-hex-buffered Stderr %ebx)
+#?       (write-buffered Stderr " to ")
+#?       (write-int32-hex-buffered Stderr %edx)
+#?       (write-buffered Stderr Newline)
+#?       (flush Stderr)
+      # v->offset = next-offset
+      89/<- *(ebx+0x14) 2/r32/edx  # Var-offset
+      # next-offset += size-of(v)
+      (size-of %ebx)  # => eax
+      01/add-to %edx 0/r32/eax
+      # curr = lookup(curr->next)
+      (lookup *(ecx+8) *(ecx+0xc))  # List-next List-next => eax
+      89/<- %ecx 0/r32/eax
+      #
+      eb/jump loop/disp8
+    }
+$populate-mu-type-offsets-in-inouts:end:
+    # . restore registers
+    5f/pop-to-edi
+    5b/pop-to-ebx
+    5a/pop-to-edx
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+emit-subx-stmt-list:  # out: (addr buffered-file), stmts: (addr list stmt), vars: (addr stack live-var), fn: (addr function), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    53/push-ebx
+    56/push-esi
+    # esi = stmts
+    8b/-> *(ebp+0xc) 6/r32/esi
+    #
+    {
+$emit-subx-stmt-list:loop:
+      81 7/subop/compare %esi 0/imm32
+      0f 84/jump-if-= break/disp32
+      # var curr-stmt/ecx: (addr stmt) = lookup(stmts->value)
+      (lookup *esi *(esi+4))  # List-value List-value => eax
+      89/<- %ecx 0/r32/eax
+      {
+$emit-subx-stmt-list:check-for-block:
+        81 7/subop/compare *ecx 0/imm32/block  # Stmt-tag
+        75/jump-if-!= break/disp8
+$emit-subx-stmt-list:block:
+        (emit-subx-block *(ebp+8) %ecx *(ebp+0x10) *(ebp+0x14) *(ebp+0x18) *(ebp+0x1c))
+      }
+      {
+$emit-subx-stmt-list:check-for-stmt:
+        81 7/subop/compare *ecx 1/imm32/stmt1  # Stmt-tag
+        0f 85/jump-if-!= break/disp32
+$emit-subx-stmt-list:stmt1:
+        {
+          (is-mu-branch? %ecx)  # => eax
+          3d/compare-eax-and 0/imm32/false
+          0f 84/jump-if-= break/disp32
+$emit-subx-stmt-list:branch-stmt:
+          # unconditional return {{{
+          {
+$emit-subx-stmt-list:return:
+            # if (!string-equal?(curr-stmt->operation, "return")) break
+            (lookup *(ecx+4) *(ecx+8))  # Stmt1-operation Stmt1-operation => eax
+            (string-equal? %eax "return")  # => eax
+            3d/compare-eax-and 0/imm32/false
+            0f 84/jump-if-= break/disp32
+            #
+            (emit-outputs *(ebp+8) %ecx *(ebp+0x14))
+            (emit-cleanup-code-for-non-outputs *(ebp+8) *(ebp+0x10) *(ebp+0x14))
+            # emit jump to end of function
+            # getting at the name of the label is challenging
+            (emit-indent *(ebp+8) *Curr-block-depth)
+            (write-buffered *(ebp+8) "e9/jump ")
+            # var b/eax: (addr array byte) = fn->body->var->name
+            8b/-> *(ebp+0x14) 0/r32/eax
+            (lookup *(eax+0x18) *(eax+0x1c))  # Function-body Function-body => eax
+            (lookup *(eax+0xc) *(eax+0x10))  # Block-var Block-var => eax
+            (lookup *eax *(eax+4))  # Var-name Var-name => eax
+            (write-buffered *(ebp+8) %eax)
+            (write-buffered *(ebp+8) ":break/disp32\n")
+            e9/jump $emit-subx-stmt-list:clean-up/disp32
+          }
+          # }}}
+          # unconditional loops {{{
+          {
+            # if (!string-equal?(var->operation, "loop")) break
+            (lookup *(ecx+4) *(ecx+8))  # Stmt1-operation Stmt1-operation => eax
+            (string-equal? %eax "loop")  # => eax
+            3d/compare-eax-and 0/imm32/false
+            0f 84/jump-if-= break/disp32
+$emit-subx-stmt-list:unconditional-loop:
+            81 7/subop/compare *(ecx+0xc) 0/imm32  # Stmt1-inouts
+            # simple unconditional loops without a target
+            {
+              0f 85/jump-if-!= break/disp32
+$emit-subx-stmt-list:zero-arg-unconditional-loop:
+              (emit-cleanup-code-until-depth *(ebp+8) *(ebp+0x10) *Curr-block-depth)
+              (emit-indent *(ebp+8) *Curr-block-depth)
+              (write-buffered *(ebp+8) "e9/jump loop/disp32")
+              (write-buffered *(ebp+8) Newline)
+              e9/jump $emit-subx-stmt-list:clean-up/disp32  # skip remaining statements; they're dead code
+            }
+            # unconditional loops with a target
+            {
+              0f 84/jump-if-= break/disp32
+              (emit-subx-cleanup-and-unconditional-nonlocal-branch *(ebp+8) %ecx *(ebp+0x10))
+              e9/jump $emit-subx-stmt-list:clean-up/disp32
+            }
+          }
+          # }}}
+          # unconditional breaks {{{
+          {
+            # if (!string-equal?(curr-stmt->operation, "break")) break
+            (lookup *(ecx+4) *(ecx+8))  # Stmt1-operation Stmt1-operation => eax
+            (string-equal? %eax "break")  # => eax
+            3d/compare-eax-and 0/imm32/false
+            0f 84/jump-if-= break/disp32
+$emit-subx-stmt-list:unconditional-break:
+            81 7/subop/compare *(ecx+0xc) 0/imm32  # Stmt1-inouts
+            # simple unconditional breaks without a target
+            0f 84/jump-if-= $emit-subx-stmt-list:emit-cleanup/disp32  # easy: just skip remaining statements
+            # unconditional breaks with a target
+            (emit-subx-cleanup-and-unconditional-nonlocal-branch *(ebp+8) %ecx *(ebp+0x10))
+            e9/jump $emit-subx-stmt-list:clean-up/disp32
+          }
+          # }}}
+          # simple conditional branches without a target {{{
+          81 7/subop/compare *(ecx+0xc) 0/imm32  # Stmt1-inouts
+          {
+            0f 85/jump-if-!= break/disp32
+$emit-subx-stmt-list:zero-arg-conditional-branch:
+            # var old-block-depth/ebx: int = Curr-block-depth - 1
+            8b/-> *Curr-block-depth 3/r32/ebx
+            # cleanup prologue
+            (emit-indent *(ebp+8) *Curr-block-depth)
+            (write-buffered *(ebp+8) "{\n")
+            ff 0/subop/increment *Curr-block-depth
+            #
+            (emit-reverse-break *(ebp+8) %ecx)
+            # clean up until old block depth
+            (emit-cleanup-code-until-depth *(ebp+8) *(ebp+0x10) %ebx)
+            # var target-block-depth/ebx: int = Curr-block-depth - 1
+            4b/decrement-ebx
+            # emit jump to target block
+            (lookup *(ecx+4) *(ecx+8))  # Stmt1-operation Stmt1-operation => eax
+            (string-starts-with? %eax "break")  # => eax
+            3d/compare-eax-and 0/imm32/false
+            {
+              74/jump-if-= break/disp8
+              (emit-unconditional-jump-to-depth *(ebp+8) *(ebp+0x10) %ebx "break")
+            }
+            3d/compare-eax-and 0/imm32/false  # just in case the function call modified flags
+            {
+              75/jump-if-!= break/disp8
+              (emit-unconditional-jump-to-depth *(ebp+8) *(ebp+0x10) %ebx "loop")
+            }
+            # cleanup epilogue
+            ff 1/subop/decrement *Curr-block-depth
+            (emit-indent *(ebp+8) *Curr-block-depth)
+            (write-buffered *(ebp+8) "}\n")
+            # continue
+            e9/jump $emit-subx-stmt-list:continue/disp32
+          }
+          # }}}
+          # conditional branches with an explicit target {{{
+          {
+            0f 84/jump-if-= break/disp32
+$emit-subx-stmt-list:conditional-branch-with-target:
+            # cleanup prologue
+            (emit-indent *(ebp+8) *Curr-block-depth)
+            (write-buffered *(ebp+8) "{\n")
+            ff 0/subop/increment *Curr-block-depth
+            #
+            (emit-reverse-break *(ebp+8) %ecx)
+            (emit-subx-cleanup-and-unconditional-nonlocal-branch *(ebp+8) %ecx *(ebp+0x10))
+            # cleanup epilogue
+            ff 1/subop/decrement *Curr-block-depth
+            (emit-indent *(ebp+8) *Curr-block-depth)
+            (write-buffered *(ebp+8) "}\n")
+            # continue
+            e9/jump $emit-subx-stmt-list:continue/disp32
+          }
+          # }}}
+        }
+$emit-subx-stmt-list:1-to-1:
+        (emit-subx-stmt *(ebp+8) %ecx Primitives *(ebp+0x14) *(ebp+0x18) *(ebp+0x1c))
+        e9/jump $emit-subx-stmt-list:continue/disp32
+      }
+      {
+$emit-subx-stmt-list:check-for-var-def:
+        81 7/subop/compare *ecx 2/imm32/var-def  # Stmt-tag
+        75/jump-if-!= break/disp8
+$emit-subx-stmt-list:var-def:
+        (emit-subx-var-def *(ebp+8) %ecx)
+        (push *(ebp+0x10) *(ecx+4))  # Vardef-var
+        (push *(ebp+0x10) *(ecx+8))  # Vardef-var
+        (push *(ebp+0x10) 0)  # Live-var-register-spilled = 0 for vars on the stack
+        #
+        eb/jump $emit-subx-stmt-list:continue/disp8
+      }
+      {
+$emit-subx-stmt-list:check-for-reg-var-def:
+        81 7/subop/compare *ecx 3/imm32/reg-var-def  # Stmt-tag
+        0f 85/jump-if-!= break/disp32
+$emit-subx-stmt-list:reg-var-def:
+        # TODO: ensure that there's exactly one output
+        (push-output-and-maybe-emit-spill *(ebp+8) %ecx *(ebp+0x10) %esi *(ebp+0x14) *(ebp+0x18) *(ebp+0x1c))
+        # emit the instruction as usual
+        (emit-subx-stmt *(ebp+8) %ecx Primitives *(ebp+0x14) *(ebp+0x18) *(ebp+0x1c))
+        #
+        eb/jump $emit-subx-stmt-list:continue/disp8
+      }
+$emit-subx-stmt-list:continue:
+      # TODO: raise an error on unrecognized Stmt-tag
+      (lookup *(esi+8) *(esi+0xc))  # List-next List-next => eax
+      89/<- %esi 0/r32/eax
+      e9/jump loop/disp32
+    }
+$emit-subx-stmt-list:emit-cleanup:
+    (emit-cleanup-code-until-depth *(ebp+8) *(ebp+0x10) *Curr-block-depth)
+$emit-subx-stmt-list:clean-up:
+    (clean-up-stack-offset-state *(ebp+0x10) *Curr-block-depth)
+    (clean-up-blocks *(ebp+0x10) *Curr-block-depth *(ebp+0x14))
+$emit-subx-stmt-list:end:
+    # . restore registers
+    5e/pop-to-esi
+    5b/pop-to-ebx
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+# 'later-stmts' includes 'stmt', but will behave the same even without it; reg-var-def stmts are guaranteed not to write to function outputs.
+push-output-and-maybe-emit-spill:  # out: (addr buffered-file), stmt: (addr reg-var-def), vars: (addr stack (handle var)), later-stmts: (addr list stmt), fn: (addr function), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    52/push-edx
+    # ecx = stmt
+    8b/-> *(ebp+0xc) 1/r32/ecx
+    # var sv/eax: (addr stmt-var) = lookup(curr-stmt->outputs)
+    (lookup *(ecx+0x14) *(ecx+0x18))  # Regvardef-outputs Regvardef-outputs => eax
+    # TODO: assert !sv->is-deref?
+    # var v/ecx: (addr var) = lookup(sv->value)
+    (lookup *eax *(eax+4))  # Stmt-var-value Stmt-var-value => eax
+    89/<- %ecx 0/r32/eax
+    # v->block-depth = *Curr-block-depth
+    8b/-> *Curr-block-depth 0/r32/eax
+    89/<- *(ecx+0x10) 0/r32/eax  # Var-block-depth
+#?     (write-buffered Stderr "var ")
+#?     (lookup *ecx *(ecx+4))
+#?     (write-buffered Stderr %eax)
+#?     (write-buffered Stderr " at depth ")
+#?     (write-int32-hex-buffered Stderr *(ecx+0x10))
+#?     (write-buffered Stderr Newline)
+#?     (flush Stderr)
+    # ensure that v is in a register
+    81 7/subop/compare *(ecx+0x18) 0/imm32  # Var-register
+    0f 84/jump-if-= $push-output-and-maybe-emit-spill:abort/disp32
+    # var emit-spill?/edx: boolean = not-yet-spilled-this-block? && will-not-write-some-register?(fn)
+    (not-yet-spilled-this-block? %ecx *(ebp+0x10))  # => eax
+    89/<- %edx 0/r32/eax
+    3d/compare-eax-and 0/imm32/false
+    0f 84/jump-if-= $push-output-and-maybe-emit-spill:push/disp32
+    (will-not-write-some-register? %ecx *(ebp+0x14) *(ebp+0x18))  # => eax
+    89/<- %edx 0/r32/eax
+    # check emit-spill?
+    3d/compare-eax-and 0/imm32/false
+    0f 84/jump-if-= $push-output-and-maybe-emit-spill:push/disp32
+    # TODO: assert(size-of(output) == 4)
+    # *Curr-local-stack-offset -= 4
+    81 5/subop/subtract *Curr-local-stack-offset 4/imm32
+    # emit spill
+    (lookup *(ecx+0x18) *(ecx+0x1c))  # Var-register Var-register => eax
+    (emit-push-register *(ebp+8) %eax)
+$push-output-and-maybe-emit-spill:push:
+    8b/-> *(ebp+0xc) 1/r32/ecx
+    (lookup *(ecx+0x14) *(ecx+0x18))  # Regvardef-outputs Regvardef-outputs => eax
+    # push(vars, {sv->value, emit-spill?})
+    (push *(ebp+0x10) *eax)  # Stmt-var-value
+    (push *(ebp+0x10) *(eax+4))  # Stmt-var-value
+    (push *(ebp+0x10) %edx)
+$push-output-and-maybe-emit-spill:end:
+    # . restore registers
+    5a/pop-to-edx
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+$push-output-and-maybe-emit-spill:abort:
+    # error("var '" var->name "' initialized from an instruction must live in a register\n")
+    (write-buffered *(ebp+0x1c) "var '")
+    (write-buffered *(ebp+0x1c) *eax)  # Var-name
+    (write-buffered *(ebp+0x1c) "' initialized from an instruction must live in a register\n")
+    (flush *(ebp+0x1c))
+    (stop *(ebp+0x20) 1)
+    # never gets here
+
+emit-subx-cleanup-and-unconditional-nonlocal-branch:  # out: (addr buffered-file), stmt: (addr stmt1), vars: (addr stack live-var)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    # ecx = stmt
+    8b/-> *(ebp+0xc) 1/r32/ecx
+    # var target/eax: (addr array byte) = curr-stmt->inouts->value->name
+    (lookup *(ecx+0xc) *(ecx+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+    (lookup *eax *(eax+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *eax *(eax+4))  # Var-name Var-name => eax
+    # clean up until target block
+    (emit-cleanup-code-until-target *(ebp+8) *(ebp+0x10) %eax)
+    # emit jump to target block
+    (emit-indent *(ebp+8) *Curr-block-depth)
+    (write-buffered *(ebp+8) "e9/jump ")
+    (write-buffered *(ebp+8) %eax)
+    (lookup *(ecx+4) *(ecx+8))  # Stmt1-operation Stmt1-operation => eax
+    (string-starts-with? %eax "break")
+    3d/compare-eax-and 0/imm32/false
+    {
+      74/jump-if-= break/disp8
+      (write-buffered *(ebp+8) ":break/disp32\n")
+      eb/jump $emit-subx-cleanup-and-unconditional-nonlocal-branch:end/disp8
+    }
+    (write-buffered *(ebp+8) ":loop/disp32\n")
+$emit-subx-cleanup-and-unconditional-nonlocal-branch:end:
+    # . restore registers
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+emit-outputs:  # out: (addr buffered-file), return-stmt: (addr stmt1), fn: (addr function)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    56/push-esi
+    57/push-edi
+    # var curr-inout/esi: (addr stmt-var) = return-stmt->inouts
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *(eax+0xc) *(eax+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+    89/<- %esi 0/r32/eax
+    # var curr-output/edi: (addr list var) = fn->outputs
+    8b/-> *(ebp+0x10) 0/r32/eax
+    (lookup *(eax+0x10) *(eax+0x14))  # Function-outputs Function-outputs => eax
+    89/<- %edi 0/r32/eax
+    {
+$emit-outputs:loop:
+      81 7/subop/compare %esi 0/imm32
+      0f 84/jump-if-= break/disp32
+      # emit copy to output register
+      # var curr-output-register/ecx: (addr array byte) = curr-output->value->register
+      (lookup *edi *(edi+4))  # List-value List-value => eax
+      (lookup *(eax+0x18) *(eax+0x1c))  # Var-register Var-register => eax
+      89/<- %ecx 0/r32/eax
+      # if curr-output-register starts with "x", emit a floating-point copy
+      8a/copy-byte *(ecx+4) 0/r32/AL
+      81 4/subop/and %eax 0xff/imm32
+      3d/compare-eax-and 0x78/imm32/x
+      {
+        75/jump-if-!= break/disp8
+        (emit-float-output *(ebp+8) %esi %ecx)
+        eb/jump $emit-outputs:continue/disp8
+      }
+      # otherwise emit an int copy
+      (emit-int-output *(ebp+8) %esi %ecx)
+$emit-outputs:continue:
+      # curr-inout = curr-inout->next
+      (lookup *(esi+8) *(esi+0xc))  # Stmt-var-next Stmt-var-next => eax
+      89/<- %esi 0/r32/eax
+      # curr-output = curr-output->next
+      (lookup *(edi+8) *(edi+0xc))  # List-next List-next => eax
+      89/<- %edi 0/r32/eax
+      #
+      e9/jump loop/disp32
+    }
+$emit-outputs:end:
+    # . restore registers
+    5f/pop-to-edi
+    5e/pop-to-esi
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+emit-int-output:  # out: (addr buffered-file), return-var: (addr stmt-var), dest-reg: (addr array byte)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    # ecx = return-var->value
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Stmt-var-value Stmt-var-value => eax
+    89/<- %ecx 0/r32/eax
+    # if curr-var is a literal, emit copy of a literal to the output
+    (lookup *(ecx+8) *(ecx+0xc))  # Var-type Var-type => eax
+    (is-simple-mu-type? %eax 0)  # literal => eax
+    {
+      3d/compare-eax-and 0/imm32/false
+      0f 84/jump-if-= break/disp32
+      (emit-indent *(ebp+8) *Curr-block-depth)
+      (write-buffered *(ebp+8) "c7 0/subop/copy %")
+      (write-buffered *(ebp+8) *(ebp+0x10))
+      (write-buffered *(ebp+8) " ")
+      (lookup *ecx *(ecx+4))  # Var-name Var-name => eax
+      (write-buffered *(ebp+8) %eax)
+      (write-buffered *(ebp+8) "/imm32\n")
+      e9/jump $emit-int-output:end/disp32
+    }
+    # otherwise emit an integer copy
+    (emit-indent *(ebp+8) *Curr-block-depth)
+    (write-buffered *(ebp+8) "8b/->")
+    (emit-subx-var-as-rm32 *(ebp+8) *(ebp+0xc))
+    (write-buffered *(ebp+8) " ")
+    (get Mu-registers *(ebp+0x10) 0xc "Mu-registers")  # => eax
+    (write-int32-hex-buffered *(ebp+8) *eax)
+    (write-buffered *(ebp+8) "/r32\n")
+$emit-int-output:end:
+    # . restore registers
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+emit-float-output:  # out: (addr buffered-file), return-var: (addr stmt-var), dest-reg: (addr array byte)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    #
+    (emit-indent *(ebp+8) *Curr-block-depth)
+    (write-buffered *(ebp+8) "f3 0f 10/->")
+    (emit-subx-var-as-rm32 *(ebp+8) *(ebp+0xc))
+    (write-buffered *(ebp+8) " ")
+    (get Mu-registers *(ebp+0x10) 0xc "Mu-registers")  # => eax
+    (write-int32-hex-buffered *(ebp+8) *eax)
+    (write-buffered *(ebp+8) "/x32\n")
+$emit-float-output:end:
+    # . restore registers
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+is-mu-branch?:  # stmt: (addr stmt1) -> result/eax: boolean
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    51/push-ecx
+    # ecx = lookup(stmt->operation)
+    8b/-> *(ebp+8) 1/r32/ecx
+    (lookup *(ecx+4) *(ecx+8))  # Stmt1-operation Stmt1-operation => eax
+    89/<- %ecx 0/r32/eax
+    # if (stmt->operation starts with "loop") return true
+    (string-starts-with? %ecx "loop")  # => eax
+    3d/compare-eax-and 0/imm32/false
+    75/jump-if-not-equal $is-mu-branch?:end/disp8
+    # if (stmt->operation starts with "break") return true
+    (string-starts-with? %ecx "break")  # => eax
+    3d/compare-eax-and 0/imm32/false
+    75/jump-if-not-equal $is-mu-branch?:end/disp8
+    # otherwise return (stmt->operation starts with "return")
+    (string-starts-with? %ecx "return")  # => eax
+$is-mu-branch?:end:
+    # . restore registers
+    59/pop-to-ecx
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+emit-reverse-break:  # out: (addr buffered-file), stmt: (addr stmt1)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    # eax = stmt
+    8b/-> *(ebp+0xc) 0/r32/eax
+    #
+    (lookup *(eax+4) *(eax+8))  # Stmt1-operation Stmt1-operation => eax
+    (get Reverse-branch %eax 0x10 "reverse-branch: ")  # => eax: (addr handle array byte)
+    (emit-indent *(ebp+8) *Curr-block-depth)
+    (lookup *eax *(eax+4))  # => eax
+    (write-buffered *(ebp+8) %eax)
+    (write-buffered *(ebp+8) " break/disp32\n")
+$emit-reverse-break:end:
+    # . restore registers
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+== data
+
+# Table from Mu branch instructions to the reverse SubX opcodes for them.
+Reverse-branch:  # (table (handle array byte) (handle array byte))
+  # a table is a stream
+  0x1c0/imm32/write
+  0/imm32/read
+  0x1c0/imm32/size
+  # data
+  0x11/imm32/alloc-id   _string-break-if-=/imm32                0x11/imm32/alloc-id   _string_0f_85_jump_label/imm32
+  0x11/imm32/alloc-id   _string-loop-if-=/imm32                 0x11/imm32/alloc-id   _string_0f_85_jump_label/imm32
+  0x11/imm32/alloc-id   _string-break-if-!=/imm32               0x11/imm32/alloc-id   _string_0f_84_jump_label/imm32
+  0x11/imm32/alloc-id   _string-loop-if-!=/imm32                0x11/imm32/alloc-id   _string_0f_84_jump_label/imm32
+  0x11/imm32/alloc-id   _string-break-if-</imm32                0x11/imm32/alloc-id   _string_0f_8d_jump_label/imm32
+  0x11/imm32/alloc-id   _string-loop-if-</imm32                 0x11/imm32/alloc-id   _string_0f_8d_jump_label/imm32
+  0x11/imm32/alloc-id   _string-break-if->/imm32                0x11/imm32/alloc-id   _string_0f_8e_jump_label/imm32
+  0x11/imm32/alloc-id   _string-loop-if->/imm32                 0x11/imm32/alloc-id   _string_0f_8e_jump_label/imm32
+  0x11/imm32/alloc-id   _string-break-if-<=/imm32               0x11/imm32/alloc-id   _string_0f_8f_jump_label/imm32
+  0x11/imm32/alloc-id   _string-loop-if-<=/imm32                0x11/imm32/alloc-id   _string_0f_8f_jump_label/imm32
+  0x11/imm32/alloc-id   _string-break-if->=/imm32               0x11/imm32/alloc-id   _string_0f_8c_jump_label/imm32
+  0x11/imm32/alloc-id   _string-loop-if->=/imm32                0x11/imm32/alloc-id   _string_0f_8c_jump_label/imm32
+  0x11/imm32/alloc-id   _string-break-if-addr</imm32            0x11/imm32/alloc-id   _string_0f_83_jump_label/imm32
+  0x11/imm32/alloc-id   _string-loop-if-addr</imm32             0x11/imm32/alloc-id   _string_0f_83_jump_label/imm32
+  0x11/imm32/alloc-id   _string-break-if-addr>/imm32            0x11/imm32/alloc-id   _string_0f_86_jump_label/imm32
+  0x11/imm32/alloc-id   _string-loop-if-addr>/imm32             0x11/imm32/alloc-id   _string_0f_86_jump_label/imm32
+  0x11/imm32/alloc-id   _string-break-if-addr<=/imm32           0x11/imm32/alloc-id   _string_0f_87_jump_label/imm32
+  0x11/imm32/alloc-id   _string-loop-if-addr<=/imm32            0x11/imm32/alloc-id   _string_0f_87_jump_label/imm32
+  0x11/imm32/alloc-id   _string-break-if-addr>=/imm32           0x11/imm32/alloc-id   _string_0f_82_jump_label/imm32
+  0x11/imm32/alloc-id   _string-loop-if-addr>=/imm32            0x11/imm32/alloc-id   _string_0f_82_jump_label/imm32
+  0x11/imm32/alloc-id   _string-break-if-float</imm32           0x11/imm32/alloc-id   _string_0f_83_jump_label/imm32
+  0x11/imm32/alloc-id   _string-loop-if-float</imm32            0x11/imm32/alloc-id   _string_0f_83_jump_label/imm32
+  0x11/imm32/alloc-id   _string-break-if-float>/imm32           0x11/imm32/alloc-id   _string_0f_86_jump_label/imm32
+  0x11/imm32/alloc-id   _string-loop-if-float>/imm32            0x11/imm32/alloc-id   _string_0f_86_jump_label/imm32
+  0x11/imm32/alloc-id   _string-break-if-float<=/imm32          0x11/imm32/alloc-id   _string_0f_87_jump_label/imm32
+  0x11/imm32/alloc-id   _string-loop-if-float<=/imm32           0x11/imm32/alloc-id   _string_0f_87_jump_label/imm32
+  0x11/imm32/alloc-id   _string-break-if-float>=/imm32          0x11/imm32/alloc-id   _string_0f_82_jump_label/imm32
+  0x11/imm32/alloc-id   _string-loop-if-float>=/imm32           0x11/imm32/alloc-id   _string_0f_82_jump_label/imm32
+
+== code
+
+emit-unconditional-jump-to-depth:  # out: (addr buffered-file), vars: (addr stack live-var), depth: int, label-suffix: (addr array byte)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    52/push-edx
+    53/push-ebx
+    56/push-esi
+    # ecx = vars
+    8b/-> *(ebp+0xc) 1/r32/ecx
+    # var eax: int = vars->top
+    8b/-> *ecx 0/r32/eax
+    # var curr/esi: (addr handle var) = &vars->data[vars->top - 12]
+    8d/copy-address *(ecx+eax-4) 6/r32/esi  # vars + 8 + vars->top - 12/Live-var-size
+    # var min/ecx: (addr handle var) = vars->data
+    8d/copy-address *(ecx+8) 1/r32/ecx
+    # edx = depth
+    8b/-> *(ebp+0x10) 2/r32/edx
+    {
+$emit-unconditional-jump-to-depth:loop:
+      # if (curr < min) break
+      39/compare %esi 1/r32/ecx
+      0f 82/jump-if-addr< break/disp32
+      # var v/ebx: (addr var) = lookup(*curr)
+      (lookup *esi *(esi+4))  # => eax
+      89/<- %ebx 0/r32/eax
+      # if (v->block-depth < until-block-depth) break
+      39/compare *(ebx+0x10) 2/r32/edx  # Var-block-depth
+      0f 8c/jump-if-< break/disp32
+      {
+$emit-unconditional-jump-to-depth:check:
+        # if v->block-depth != until-block-depth, continue
+        39/compare *(ebx+0x10) 2/r32/edx  # Var-block-depth
+        0f 85/jump-if-!= break/disp32
+$emit-unconditional-jump-to-depth:depth-found:
+        # if v is not a literal, continue
+        (size-of %ebx)  # => eax
+        3d/compare-eax-and 0/imm32
+        0f 85/jump-if-!= break/disp32
+$emit-unconditional-jump-to-depth:label-found:
+        # emit unconditional jump, then return
+        (emit-indent *(ebp+8) *Curr-block-depth)
+        (write-buffered *(ebp+8) "e9/jump ")
+        (lookup *ebx *(ebx+4))  # Var-name Var-name => eax
+        (write-buffered *(ebp+8) %eax)
+        (write-buffered *(ebp+8) ":")
+        (write-buffered *(ebp+8) *(ebp+0x14))
+        (write-buffered *(ebp+8) "/disp32\n")
+        eb/jump $emit-unconditional-jump-to-depth:end/disp8
+      }
+      # curr -= 12
+      81 5/subop/subtract %esi 0xc/imm32
+      e9/jump loop/disp32
+    }
+    # TODO: error if no label at 'depth' was found
+$emit-unconditional-jump-to-depth:end:
+    # . restore registers
+    5e/pop-to-esi
+    5b/pop-to-ebx
+    5a/pop-to-edx
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+# emit clean-up code for 'vars' until some block depth
+# doesn't actually modify 'vars' so we need traverse manually inside the stack
+emit-cleanup-code-until-depth:  # out: (addr buffered-file), vars: (addr stack live-var), until-block-depth: int
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    52/push-edx
+    53/push-ebx
+    56/push-esi
+#?     (write-buffered Stderr "--- cleanup\n")
+#?     (flush Stderr)
+    # ecx = vars
+    8b/-> *(ebp+0xc) 1/r32/ecx
+    # var esi: int = vars->top
+    8b/-> *ecx 6/r32/esi
+    # var curr/esi: (addr handle var) = &vars->data[vars->top - 12]
+    8d/copy-address *(ecx+esi-4) 6/r32/esi  # vars + 8 + vars->top - 12/Live-var-size
+    # var min/ecx: (addr handle var) = vars->data
+    81 0/subop/add %ecx 8/imm32
+    # edx = until-block-depth
+    8b/-> *(ebp+0x10) 2/r32/edx
+    {
+$emit-cleanup-code-until-depth:loop:
+      # if (curr < min) break
+      39/compare %esi 1/r32/ecx
+      0f 82/jump-if-addr< break/disp32
+      # var v/ebx: (addr var) = lookup(*curr)
+      (lookup *esi *(esi+4))  # => eax
+      89/<- %ebx 0/r32/eax
+#?       (lookup *ebx *(ebx+4))  # Var-name
+#?       (write-buffered Stderr "var ")
+#?       (write-buffered Stderr %eax)
+#?       (write-buffered Stderr Newline)
+#?       (flush Stderr)
+      # if (v->block-depth < until-block-depth) break
+      39/compare *(ebx+0x10) 2/r32/edx  # Var-block-depth
+      0f 8c/jump-if-< break/disp32
+      # if v is in a register
+      81 7/subop/compare *(ebx+0x18) 0/imm32  # Var-register
+      {
+        0f 84/jump-if-= break/disp32
+        {
+$emit-cleanup-code-until-depth:check-for-previous-spill:
+          8b/-> *(esi+8) 0/r32/eax  # Live-var-register-spilled
+          3d/compare-eax-and 0/imm32/false
+          74/jump-if-= break/disp8
+$emit-cleanup-code-until-depth:reclaim-var-in-register:
+          (lookup *(ebx+0x18) *(ebx+0x1c))  # Var-register Var-register => eax
+          (emit-pop-register *(ebp+8) %eax)
+        }
+        eb/jump $emit-cleanup-code-until-depth:continue/disp8
+      }
+      # otherwise v is on the stack
+      {
+        75/jump-if-!= break/disp8
+$emit-cleanup-code-until-depth:var-on-stack:
+        (size-of %ebx)  # => eax
+        # don't emit code for labels
+        3d/compare-eax-and 0/imm32
+        74/jump-if-= break/disp8
+$emit-cleanup-code-until-depth:reclaim-var-on-stack:
+        (emit-indent *(ebp+8) *Curr-block-depth)
+        (write-buffered *(ebp+8) "81 0/subop/add %esp ")
+        (write-int32-hex-buffered *(ebp+8) %eax)
+        (write-buffered *(ebp+8) "/imm32\n")
+      }
+$emit-cleanup-code-until-depth:continue:
+      # curr -= 12
+      81 5/subop/subtract %esi 0xc/imm32
+      e9/jump loop/disp32
+    }
+$emit-cleanup-code-until-depth:end:
+    # . restore registers
+    5e/pop-to-esi
+    5b/pop-to-ebx
+    5a/pop-to-edx
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+# emit clean-up code for 'vars' that don't conflict with output registers
+# doesn't actually modify 'vars' so we need traverse manually inside the stack
+emit-cleanup-code-for-non-outputs:  # out: (addr buffered-file), vars: (addr stack live-var), fn: (addr function)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    52/push-edx
+    53/push-ebx
+    56/push-esi
+    57/push-edi
+    # ecx = vars
+    8b/-> *(ebp+0xc) 1/r32/ecx
+    # var esi: int = vars->top
+    8b/-> *ecx 6/r32/esi
+    # var curr/esi: (addr handle var) = &vars->data[vars->top - 12]
+    8d/copy-address *(ecx+esi-4) 6/r32/esi  # vars + 8 + vars->top - 12/Live-var-size
+    # var min/ecx: (addr handle var) = vars->data
+    81 0/subop/add %ecx 8/imm32
+    {
+$emit-cleanup-code-for-non-outputs:loop:
+      # if (curr < min) break
+      39/compare %esi 1/r32/ecx
+      0f 82/jump-if-addr< break/disp32
+      # var v/ebx: (addr var) = lookup(*curr)
+      (lookup *esi *(esi+4))  # => eax
+      89/<- %ebx 0/r32/eax
+      # if v is in a register
+      81 7/subop/compare *(ebx+0x18) 0/imm32  # Var-register
+      {
+        0f 84/jump-if-= break/disp32
+        {
+$emit-cleanup-code-for-non-outputs:check-for-previous-spill:
+          8b/-> *(esi+8) 0/r32/eax  # Live-var-register-spilled
+          3d/compare-eax-and 0/imm32/false
+          0f 84/jump-if-= break/disp32
+$emit-cleanup-code-for-non-outputs:reclaim-var-in-register:
+          # var reg/edi: (addr array name) = v->register
+          (lookup *(ebx+0x18) *(ebx+0x1c))  # Var-register Var-register => eax
+          89/<- %edi 0/r32/eax
+          # if reg is not in function outputs, emit a pop
+          (reg-in-function-outputs? *(ebp+0x10) %edi)  # => eax
+          3d/compare-eax-and 0/imm32/false
+          {
+            75/jump-if-!= break/disp8
+            (emit-pop-register *(ebp+8) %edi)
+            eb/jump $emit-cleanup-code-for-non-outputs:reclaim-var-in-register-done/disp8
+          }
+          # otherwise just drop it from the stack
+          (emit-indent *(ebp+8) *Curr-block-depth)
+          (write-buffered *(ebp+8) "81 0/subop/add %esp 4/imm32\n")
+        }
+$emit-cleanup-code-for-non-outputs:reclaim-var-in-register-done:
+        eb/jump $emit-cleanup-code-for-non-outputs:continue/disp8
+      }
+      # otherwise v is on the stack
+      {
+        75/jump-if-!= break/disp8
+$emit-cleanup-code-for-non-outputs:var-on-stack:
+        (size-of %ebx)  # => eax
+        # don't emit code for labels
+        3d/compare-eax-and 0/imm32
+        74/jump-if-= break/disp8
+$emit-cleanup-code-for-non-outputs:reclaim-var-on-stack:
+        (emit-indent *(ebp+8) *Curr-block-depth)
+        (write-buffered *(ebp+8) "81 0/subop/add %esp ")
+        (write-int32-hex-buffered *(ebp+8) %eax)
+        (write-buffered *(ebp+8) "/imm32\n")
+      }
+$emit-cleanup-code-for-non-outputs:continue:
+      # curr -= 12
+      81 5/subop/subtract %esi 0xc/imm32
+      e9/jump loop/disp32
+    }
+$emit-cleanup-code-for-non-outputs:end:
+    # . restore registers
+    5f/pop-to-edi
+    5e/pop-to-esi
+    5b/pop-to-ebx
+    5a/pop-to-edx
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+emit-push-register:  # out: (addr buffered-file), reg: (addr array byte)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # eax = reg
+    8b/-> *(ebp+0xc) 0/r32/eax
+    # var prefix/eax: byte = reg->data[0]
+    8a/copy-byte *(eax+4) 0/r32/AL
+    81 4/subop/and %eax 0xff/imm32
+    # if (prefix == 'x') push xmm register
+    {
+      3d/compare-eax-and 0x78/imm32/x
+      0f 85/jump-if-!= break/disp32
+      # TODO validate register
+      (emit-indent *(ebp+8) *Curr-block-depth)
+      (write-buffered *(ebp+8) "81 5/subop/subtract %esp 4/imm32\n")
+      (emit-indent *(ebp+8) *Curr-block-depth)
+      (write-buffered *(ebp+8) "f3 0f 11/<- *esp ")
+      # var prefix/eax: byte = reg->data[3]
+      8b/-> *(ebp+0xc) 0/r32/eax
+      8a/copy-byte *(eax+7) 0/r32/AL
+      81 4/subop/and %eax 0xff/imm32
+      (write-byte-buffered *(ebp+8) %eax)
+      (write-buffered *(ebp+8) "/x32\n")
+      e9/jump $emit-push-register:end/disp32
+    }
+    # otherwise push gp register
+    (emit-indent *(ebp+8) *Curr-block-depth)
+    (write-buffered *(ebp+8) "ff 6/subop/push %")
+    (write-buffered *(ebp+8) *(ebp+0xc))
+    (write-buffered *(ebp+8) Newline)
+$emit-push-register:end:
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+emit-pop-register:  # out: (addr buffered-file), reg: (addr array byte)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    # eax = reg
+    8b/-> *(ebp+0xc) 0/r32/eax
+    # var prefix/eax: byte = reg->data[0]
+    8a/copy-byte *(eax+4) 0/r32/AL
+    81 4/subop/and %eax 0xff/imm32
+    # if (prefix == 'x') pop to xmm register
+    {
+      3d/compare-eax-and 0x78/imm32/x
+      0f 85/jump-if-!= break/disp32
+      # TODO validate register
+      (emit-indent *(ebp+8) *Curr-block-depth)
+      (write-buffered *(ebp+8) "f3 0f 10/-> *esp ")
+      # var prefix/eax: byte = reg->data[3]
+      8b/-> *(ebp+0xc) 0/r32/eax
+      8a/copy-byte *(eax+7) 0/r32/AL
+      81 4/subop/and %eax 0xff/imm32
+      (write-byte-buffered *(ebp+8) %eax)
+      (write-buffered *(ebp+8) "/x32\n")
+      (emit-indent *(ebp+8) *Curr-block-depth)
+      (write-buffered *(ebp+8) "81 0/subop/add %esp 4/imm32\n")
+      e9/jump $emit-pop-register:end/disp32
+    }
+    # otherwise pop to gp register
+    (emit-indent *(ebp+8) *Curr-block-depth)
+    (write-buffered *(ebp+8) "8f 0/subop/pop %")
+    (write-buffered *(ebp+8) *(ebp+0xc))
+    (write-buffered *(ebp+8) Newline)
+$emit-pop-register:end:
+    # . restore registers
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+# emit clean-up code for 'vars' until a given label is encountered
+# doesn't actually modify 'vars' so we need traverse manually inside the stack
+emit-cleanup-code-until-target:  # out: (addr buffered-file), vars: (addr stack live-var), until-block-label: (addr array byte)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    52/push-edx
+    53/push-ebx
+    # ecx = vars
+    8b/-> *(ebp+0xc) 1/r32/ecx
+    # var eax: int = vars->top
+    8b/-> *ecx 0/r32/eax
+    # var curr/edx: (addr handle var) = &vars->data[vars->top - 12]
+    8d/copy-address *(ecx+eax-4) 2/r32/edx  # vars + 8 + vars->top - 12/Live-var-size
+    # var min/ecx: (addr handle var) = vars->data
+    81 0/subop/add %ecx 8/imm32
+    {
+$emit-cleanup-code-until-target:loop:
+      # if (curr < min) break
+      39/compare %edx 1/r32/ecx
+      0f 82/jump-if-addr< break/disp32
+      # var v/ebx: (handle var) = lookup(*curr)
+      (lookup *edx *(edx+4))  # => eax
+      89/<- %ebx 0/r32/eax
+      # if (v->name == until-block-label) break
+      (lookup *ebx *(ebx+4))  # Var-name Var-name => eax
+      (string-equal? %eax *(ebp+0x10))  # => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 85/jump-if-!= break/disp32
+      # if v is in a register
+      81 7/subop/compare *(ebx+0x18) 0/imm32  # Var-register
+      {
+        0f 84/jump-if-= break/disp32
+        {
+$emit-cleanup-code-until-target:check-for-previous-spill:
+          8b/-> *(edx+8) 0/r32/eax  # Live-var-register-spilled
+          3d/compare-eax-and 0/imm32/false
+          74/jump-if-= break/disp8
+$emit-cleanup-code-until-target:reclaim-var-in-register:
+          (lookup *(ebx+0x18) *(ebx+0x1c))  # Var-register Var-register => eax
+          (emit-pop-register *(ebp+8) %eax)
+        }
+        eb/jump $emit-cleanup-code-until-target:continue/disp8
+      }
+      # otherwise v is on the stack
+      {
+        75/jump-if-!= break/disp8
+$emit-cleanup-code-until-target:reclaim-var-on-stack:
+        (size-of %ebx)  # => eax
+        # don't emit code for labels
+        3d/compare-eax-and 0/imm32
+        74/jump-if-= break/disp8
+        #
+        (emit-indent *(ebp+8) *Curr-block-depth)
+        (write-buffered *(ebp+8) "81 0/subop/add %esp ")
+        (write-int32-hex-buffered *(ebp+8) %eax)
+        (write-buffered *(ebp+8) "/imm32\n")
+      }
+$emit-cleanup-code-until-target:continue:
+      # curr -= 12
+      81 5/subop/subtract %edx 0xc/imm32
+      e9/jump loop/disp32
+    }
+$emit-cleanup-code-until-target:end:
+    # . restore registers
+    5b/pop-to-ebx
+    5a/pop-to-edx
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+# update Curr-local-stack-offset assuming vars until some block depth are popped
+# doesn't actually modify 'vars', so we need traverse manually inside the stack
+clean-up-stack-offset-state:  # vars: (addr stack live-var), until-block-depth: int
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    52/push-edx
+    53/push-ebx
+    56/push-esi
+    # ecx = vars
+    8b/-> *(ebp+8) 1/r32/ecx
+    # var esi: int = vars->top
+    8b/-> *ecx 6/r32/esi
+    # var curr/esi: (addr handle var) = &vars->data[vars->top - 12]
+    8d/copy-address *(ecx+esi-4) 6/r32/esi  # vars + 8 + vars->top - 12/Live-var-size
+    # var min/ecx: (addr handle var) = vars->data
+    81 0/subop/add %ecx 8/imm32
+    # edx = until-block-depth
+    8b/-> *(ebp+0xc) 2/r32/edx
+    {
+$clean-up-stack-offset-state:loop:
+      # if (curr < min) break
+      39/compare %esi 1/r32/ecx
+      0f 82/jump-if-addr< break/disp32
+      # var v/ebx: (addr var) = lookup(*curr)
+      (lookup *esi *(esi+4))  # => eax
+      89/<- %ebx 0/r32/eax
+      # if (v->block-depth < until-block-depth) break
+      39/compare *(ebx+0x10) 2/r32/edx  # Var-block-depth
+      0f 8c/jump-if-< break/disp32
+      # if v is in a register
+      81 7/subop/compare *(ebx+0x18) 0/imm32  # Var-register
+      {
+        0f 84/jump-if-= break/disp32
+        {
+$clean-up-stack-offset-state:check-for-previous-spill:
+          8b/-> *(esi+8) 0/r32/eax  # Live-var-register-spilled
+          3d/compare-eax-and 0/imm32/false
+          74/jump-if-= break/disp8
+$clean-up-stack-offset-state:reclaim-var-in-register:
+          81 0/subop/add *Curr-local-stack-offset 4/imm32
+        }
+        eb/jump $clean-up-stack-offset-state:continue/disp8
+      }
+      # otherwise v is on the stack
+      {
+        75/jump-if-!= break/disp8
+$clean-up-stack-offset-state:var-on-stack:
+        (size-of %ebx)  # => eax
+        01/add-to *Curr-local-stack-offset 0/r32/eax
+      }
+$clean-up-stack-offset-state:continue:
+      # curr -= 12
+      81 5/subop/subtract %esi 0xc/imm32
+      e9/jump loop/disp32
+    }
+$clean-up-stack-offset-state:end:
+    # . restore registers
+    5e/pop-to-esi
+    5b/pop-to-ebx
+    5a/pop-to-edx
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+# Return true if there isn't a variable in 'vars' with the same block-depth
+# and register as 'v'.
+# 'v' is guaranteed not to be within 'vars'.
+not-yet-spilled-this-block?:  # v: (addr var), vars: (addr stack live-var) -> result/eax: boolean
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    51/push-ecx
+    52/push-edx
+    53/push-ebx
+    56/push-esi
+    57/push-edi
+    # ecx = vars
+    8b/-> *(ebp+0xc) 1/r32/ecx
+    # var eax: int = vars->top
+    8b/-> *ecx 0/r32/eax
+    # var curr/edx: (addr handle var) = &vars->data[vars->top - 12]
+    8d/copy-address *(ecx+eax-4) 2/r32/edx  # vars + 8 + vars->top - 12/Live-var-size
+    # var min/ecx: (addr handle var) = vars->data
+    8d/copy-address *(ecx+8) 1/r32/ecx
+    # var depth/ebx: int = v->block-depth
+    8b/-> *(ebp+8) 3/r32/ebx
+    8b/-> *(ebx+0x10) 3/r32/ebx  # Var-block-depth
+    # var needle/esi: (addr array byte) = v->register
+    8b/-> *(ebp+8) 6/r32/esi
+    (lookup *(esi+0x18) *(esi+0x1c))  # Var-register Var-register => eax
+    89/<- %esi 0/r32/eax
+    {
+$not-yet-spilled-this-block?:loop:
+      # if (curr < min) break
+      39/compare %edx 1/r32/ecx
+      0f 82/jump-if-addr< break/disp32
+      # var cand/edi: (addr var) = lookup(*curr)
+      (lookup *edx *(edx+4))  # => eax
+      89/<- %edi 0/r32/eax
+      # if (cand->block-depth < depth) break
+      39/compare *(edi+0x10) 3/r32/ebx  # Var-block-depth
+      0f 8c/jump-if-< break/disp32
+      # var cand-reg/edi: (array array byte) = cand->reg
+      (lookup *(edi+0x18) *(edi+0x1c))  # Var-register Var-register => eax
+      89/<- %edi 0/r32/eax
+      # if (cand-reg == null) continue
+      {
+$not-yet-spilled-this-block?:check-reg:
+        81 7/subop/compare %edi 0/imm32
+        0f 84/jump-if-= break/disp32
+        # if (cand-reg == needle) return true
+        (string-equal? %esi %edi)  # => eax
+        3d/compare-eax-and 0/imm32/false
+        74/jump-if-= break/disp8
+$not-yet-spilled-this-block?:return-false:
+        b8/copy-to-eax 0/imm32/false
+        eb/jump $not-yet-spilled-this-block?:end/disp8
+      }
+$not-yet-spilled-this-block?:continue:
+      # curr -= 12
+      81 5/subop/subtract %edx 0xc/imm32
+      e9/jump loop/disp32
+    }
+$not-yet-spilled-this-block?:return-true:
+    # return true
+    b8/copy-to-eax 1/imm32/true
+$not-yet-spilled-this-block?:end:
+    # . restore registers
+    5f/pop-to-edi
+    5e/pop-to-esi
+    5b/pop-to-ebx
+    5a/pop-to-edx
+    59/pop-to-ecx
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+# could the register of 'v' ever be written to by one of the vars in fn-outputs?
+will-not-write-some-register?:  # v: (addr var), stmts: (addr list stmt), fn: (addr function) -> result/eax: boolean
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # eax = v
+    8b/-> *(ebp+8) 0/r32/eax
+    # var reg/eax: (addr array byte) = lookup(v->register)
+    (lookup *(eax+0x18) *(eax+0x1c))  # Var-register Var-register => eax
+    # var target/eax: (addr var) = find-register(fn-outputs, reg)
+    (find-register *(ebp+0x10) %eax)  # => eax
+    # if (target == 0) return true
+    {
+      3d/compare-eax-and 0/imm32
+      75/jump-if-!= break/disp8
+      b8/copy-to-eax 1/imm32/true
+      eb/jump $will-not-write-some-register?:end/disp8
+    }
+    # return !assigns-in-stmts?(stmts, target)
+    (assigns-in-stmts? *(ebp+0xc) %eax)  # => eax
+    3d/compare-eax-and 0/imm32/false
+    # assume: true = 1, so no need to mask with 0x000000ff
+    0f 94/set-if-= %al
+$will-not-write-some-register?:end:
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+# return fn output with matching register
+# always returns false if 'reg' is null
+find-register:  # fn: (addr function), reg: (addr array byte) -> result/eax: (addr var)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    51/push-ecx
+    # var curr/ecx: (addr list var) = lookup(fn->outputs)
+    8b/-> *(ebp+8) 1/r32/ecx
+    (lookup *(ecx+0x10) *(ecx+0x14))  # Function-outputs Function-outputs => eax
+    89/<- %ecx 0/r32/eax
+    {
+$find-register:loop:
+      # if (curr == 0) break
+      81 7/subop/compare %ecx 0/imm32
+      74/jump-if-= break/disp8
+      # eax = curr->value->register
+      (lookup *ecx *(ecx+4))  # List-value List-value => eax
+      (lookup *(eax+0x18) *(eax+0x1c))  # Var-register Var-register => eax
+      # if (eax == reg) return curr->value
+$find-register:compare:
+      (string-equal? *(ebp+0xc) %eax)  # => eax
+      {
+        3d/compare-eax-and 0/imm32/false
+        74/jump-if-= break/disp8
+$find-register:found:
+        (lookup *ecx *(ecx+4))  # List-value List-value => eax
+        eb/jump $find-register:end/disp8
+      }
+      # curr = lookup(curr->next)
+      (lookup *(ecx+8) *(ecx+0xc))  # List-next List-next => eax
+      89/<- %ecx 0/r32/eax
+      #
+      eb/jump loop/disp8
+    }
+$find-register:end:
+    # . restore registers
+    59/pop-to-ecx
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+assigns-in-stmts?:  # stmts: (addr list stmt), v: (addr var) -> result/eax: boolean
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    51/push-ecx
+    # var curr/ecx: (addr list stmt) = stmts
+    8b/-> *(ebp+8) 1/r32/ecx
+    {
+      # if (curr == 0) break
+      81 7/subop/compare %ecx 0/imm32
+      74/jump-if-= break/disp8
+      # if assigns-in-stmt?(curr->value, v) return true
+      (lookup *ecx *(ecx+4))  # List-value List-value => eax
+      (assigns-in-stmt? %eax *(ebp+0xc))  # => eax
+      3d/compare-eax-and 0/imm32/false
+      75/jump-if-!= break/disp8
+      # curr = lookup(curr->next)
+      (lookup *(ecx+8) *(ecx+0xc))  # List-next List-next => eax
+      89/<- %ecx 0/r32/eax
+      #
+      eb/jump loop/disp8
+    }
+$assigns-in-stmts?:end:
+    # . restore registers
+    59/pop-to-ecx
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+assigns-in-stmt?:  # stmt: (addr stmt), v: (addr var) -> result/eax: boolean
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    51/push-ecx
+    # ecx = stmt
+    8b/-> *(ebp+8) 1/r32/ecx
+    # if stmt is a stmt1, return assigns-in-stmt-vars?(stmt->outputs, v)
+    {
+      81 7/subop/compare *ecx 1/imm32/stmt1  # Stmt-tag
+      75/jump-if-!= break/disp8
+      (lookup *(ecx+0x14) *(ecx+0x18))  # Stmt1-outputs Stmt1-outputs => eax
+      (assigns-in-stmt-vars? %eax *(ebp+0xc))  # => eax
+      eb/jump $assigns-in-stmt?:end/disp8
+    }
+    # if stmt is a block, return assigns-in-stmts?(stmt->stmts, v)
+    {
+      81 7/subop/compare *ecx 0/imm32/block  # Stmt-tag
+      75/jump-if-!= break/disp8
+      (lookup *(ecx+4) *(ecx+8))  # Block-stmts Block-stmts => eax
+      (assigns-in-stmts? %eax *(ebp+0xc))  # => eax
+      eb/jump $assigns-in-stmt?:end/disp8
+    }
+    # otherwise return false
+    b8/copy 0/imm32/false
+$assigns-in-stmt?:end:
+    # . restore registers
+    59/pop-to-ecx
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+assigns-in-stmt-vars?:  # stmt-var: (addr stmt-var), v: (addr var) -> result/eax: boolean
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    51/push-ecx
+    # var curr/ecx: (addr stmt-var) = stmt-var
+    8b/-> *(ebp+8) 1/r32/ecx
+    {
+      # if (curr == 0) break
+      81 7/subop/compare %ecx 0/imm32
+      74/jump-if-= break/disp8
+      # eax = lookup(curr->value)
+      (lookup *ecx *(ecx+4))  # Stmt-var-value Stmt-var-value => eax
+      # if (eax == v  &&  curr->is-deref? == false) return true
+      {
+        39/compare *(ebp+0xc) 0/r32/eax
+        75/jump-if-!= break/disp8
+        81 7/subop/compare *(ecx+0x10) 0/imm32/false  # Stmt-var-is-deref
+        75/jump-if-!= break/disp8
+        b8/copy-to-eax 1/imm32/true
+        eb/jump $assigns-in-stmt-vars?:end/disp8
+      }
+      # curr = lookup(curr->next)
+      (lookup *(ecx+8) *(ecx+0xc))  # Stmt-var-next Stmt-var-next => eax
+      89/<- %ecx 0/r32/eax
+      #
+      eb/jump loop/disp8
+    }
+$assigns-in-stmt-vars?:end:
+    # . restore registers
+    59/pop-to-ecx
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+# is there a var before 'v' with the same block-depth and register on the 'vars' stack?
+# v is guaranteed to be within vars
+# 'start' is provided as an optimization, a pointer within vars
+# *start == v
+same-register-spilled-before?:  # v: (addr var), vars: (addr stack (handle var)), start: (addr var) -> result/eax: boolean
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    51/push-ecx
+    52/push-edx
+    53/push-ebx
+    56/push-esi
+    57/push-edi
+    # ecx = v
+    8b/-> *(ebp+8) 1/r32/ecx
+    # var reg/edx: (addr array byte) = lookup(v->register)
+    (lookup *(ecx+0x18) *(ecx+0x1c))  # Var-register Var-register => eax
+    89/<- %edx 0/r32/eax
+    # var depth/ebx: int = v->block-depth
+    8b/-> *(ecx+0x10) 3/r32/ebx  # Var-block-depth
+    # var min/ecx: (addr handle var) = vars->data
+    8b/-> *(ebp+0xc) 1/r32/ecx
+    81 0/subop/add %ecx 8/imm32
+    # TODO: check that start >= min and start < &vars->data[top]
+    # TODO: check that *start == v
+    # var curr/esi: (addr handle var) = start
+    8b/-> *(ebp+0x10) 6/r32/esi
+    # curr -= 8
+    81 5/subop/subtract %esi 8/imm32
+    {
+$same-register-spilled-before?:loop:
+      # if (curr < min) break
+      39/compare %esi 1/r32/ecx
+      0f 82/jump-if-addr< break/disp32
+      # var x/eax: (addr var) = lookup(*curr)
+      (lookup *esi *(esi+4))  # => eax
+      # if (x->block-depth < depth) break
+      39/compare *(eax+0x10) 3/r32/ebx  # Var-block-depth
+      0f 8c/jump-if-< break/disp32
+      # if (x->register == 0) continue
+      81 7/subop/compare *(eax+0x18) 0/imm32  # Var-register
+      74/jump-if-= $same-register-spilled-before?:continue/disp8
+      # if (x->register == reg) return true
+      (lookup *(eax+0x18) *(eax+0x1c))  # Var-register Var-register => eax
+      (string-equal? %eax %edx)  # => eax
+      3d/compare-eax-and 0/imm32/false
+      b8/copy-to-eax 1/imm32/true
+      75/jump-if-!= $same-register-spilled-before?:end/disp8
+$same-register-spilled-before?:continue:
+      # curr -= 8
+      81 5/subop/subtract %esi 8/imm32
+      e9/jump loop/disp32
+    }
+$same-register-spilled-before?:false:
+    b8/copy-to-eax 0/imm32/false
+$same-register-spilled-before?:end:
+    # . restore registers
+    5f/pop-to-edi
+    5e/pop-to-esi
+    5b/pop-to-ebx
+    5a/pop-to-edx
+    59/pop-to-ecx
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+# clean up global state for 'vars' until some block depth (inclusive)
+clean-up-blocks:  # vars: (addr stack live-var), until-block-depth: int, fn: (addr function)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    56/push-esi
+    # esi = vars
+    8b/-> *(ebp+8) 6/r32/esi
+    # ecx = until-block-depth
+    8b/-> *(ebp+0xc) 1/r32/ecx
+    {
+$clean-up-blocks:reclaim-loop:
+      # if (vars->top <= 0) break
+      8b/-> *esi 0/r32/eax  # Stack-top
+      3d/compare-eax-and 0/imm32
+      0f 8e/jump-if-<= break/disp32
+      # var v/eax: (addr var) = lookup(vars[vars->top-12])
+      (lookup *(esi+eax-4) *(esi+eax))  # vars + 8 + vars->top - 12 => eax
+      # if (v->block-depth < until-block-depth) break
+      39/compare *(eax+0x10) 1/r32/ecx  # Var-block-depth
+      0f 8c/jump-if-< break/disp32
+      (pop %esi)  # => eax
+      (pop %esi)  # => eax
+      (pop %esi)  # => eax
+      e9/jump loop/disp32
+    }
+$clean-up-blocks:end:
+    # . restore registers
+    5e/pop-to-esi
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+reg-in-function-outputs?:  # fn: (addr function), target: (addr array byte) -> result/eax: boolean
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    51/push-ecx
+    # var curr/ecx: (addr list var) = lookup(fn->outputs)
+    8b/-> *(ebp+8) 0/r32/eax
+    (lookup *(eax+0x10) *(eax+0x14))  # Function-outputs Function-outputs => eax
+    89/<- %ecx 0/r32/eax
+    # while curr != null
+    {
+      81 7/subop/compare %ecx 0/imm32
+      74/jump-if-= break/disp8
+      # var v/eax: (addr var) = lookup(curr->value)
+      (lookup *ecx *(ecx+4))  # List-value List-value => eax
+      # var reg/eax: (addr array byte) = lookup(v->register)
+      (lookup *(eax+0x18) *(eax+0x1c))  # Var-register Var-register => eax
+      # if (reg == target) return true
+      (string-equal? %eax *(ebp+0xc))  # => eax
+      3d/compare-eax-and 0/imm32/false
+      75/jump-if-!= $reg-in-function-outputs?:end/disp8
+      # curr = curr->next
+      (lookup *(ecx+8) *(ecx+0xc))  # List-next List-next => eax
+      89/<- %ecx 0/r32/eax
+      #
+      eb/jump loop/disp8
+    }
+    # return false
+    b8/copy-to-eax 0/imm32
+$reg-in-function-outputs?:end:
+    # . restore registers
+    59/pop-to-ecx
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+emit-subx-var-def:  # out: (addr buffered-file), stmt: (addr stmt)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    52/push-edx
+    # eax = stmt
+    8b/-> *(ebp+0xc) 0/r32/eax
+    # var v/ecx: (addr var)
+    (lookup *(eax+4) *(eax+8))  # Vardef-var Vardef-var => eax
+    89/<- %ecx 0/r32/eax
+    # v->block-depth = *Curr-block-depth
+    8b/-> *Curr-block-depth 0/r32/eax
+    89/<- *(ecx+0x10) 0/r32/eax  # Var-block-depth
+    # var n/edx: int = size-of(stmt->var)
+    (size-of %ecx)  # => eax
+    89/<- %edx 0/r32/eax
+    # *Curr-local-stack-offset -= n
+    29/subtract-from *Curr-local-stack-offset 2/r32/edx
+    # v->offset = *Curr-local-stack-offset
+    8b/-> *Curr-local-stack-offset 0/r32/eax
+    89/<- *(ecx+0x14) 0/r32/eax  # Var-offset
+    # if v is an array, do something special to initialize it
+    {
+      (lookup *(ecx+8) *(ecx+0xc))  # Var-type Var-type => eax
+      (is-mu-array? %eax)  # => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 84/jump-if-= break/disp32
+      # var array-size-without-size/edx: int = n-4
+      81 5/subop/subtract %edx 4/imm32
+      #
+      (emit-array-data-initialization *(ebp+8) %edx)
+      e9/jump $emit-subx-var-def:end/disp32
+    }
+    # another special-case for initializing streams
+    # a stream is an array with 2 extra pointers
+    {
+      (lookup *(ecx+8) *(ecx+0xc))  # Var-type Var-type => eax
+      (is-mu-stream? %eax)  # => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 84/jump-if-= break/disp32
+      # var array-size-without-size/edx: int = n-12
+      81 5/subop/subtract %edx 0xc/imm32
+      (emit-array-data-initialization *(ebp+8) %edx)
+      # emit read and write pointers
+      (emit-indent *(ebp+8) *Curr-block-depth)
+      (write-buffered *(ebp+8) "68/push 0/imm32\n")
+      (emit-indent *(ebp+8) *Curr-block-depth)
+      (write-buffered *(ebp+8) "68/push 0/imm32\n")
+      #
+      eb/jump $emit-subx-var-def:end/disp8
+    }
+    # while n > 0
+    {
+      81 7/subop/compare %edx 0/imm32
+      7e/jump-if-<= break/disp8
+      (emit-indent *(ebp+8) *Curr-block-depth)
+      (write-buffered *(ebp+8) "68/push 0/imm32\n")
+      # n -= 4
+      81 5/subop/subtract %edx 4/imm32
+      #
+      eb/jump loop/disp8
+    }
+$emit-subx-var-def:end:
+    # . restore registers
+    5a/pop-to-edx
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+emit-array-data-initialization:  # out: (addr buffered-file), n: int
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    #
+    (emit-indent *(ebp+8) *Curr-block-depth)
+    (write-buffered *(ebp+8) "(push-n-zero-bytes ")
+    (write-int32-hex-buffered *(ebp+8) *(ebp+0xc))
+    (write-buffered *(ebp+8) ")\n")
+    (emit-indent *(ebp+8) *Curr-block-depth)
+    (write-buffered *(ebp+8) "68/push ")
+    (write-int32-hex-buffered *(ebp+8) *(ebp+0xc))
+    (write-buffered *(ebp+8) "/imm32\n")
+$emit-array-data-initialization:end:
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+emit-subx-stmt:  # out: (addr buffered-file), stmt: (addr stmt), primitives: (addr primitive), fn: (addr function), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    # - some special-case primitives that don't actually use the 'primitives' data structure
+    # var op/ecx: (addr array byte) = lookup(stmt->operation)
+    8b/-> *(ebp+0xc) 1/r32/ecx
+    (lookup *(ecx+4) *(ecx+8))  # Stmt1-operation Stmt1-operation => eax
+    89/<- %ecx 0/r32/eax
+    # copy byte (can be a primitive except we need to emit a second instruction)
+    {
+      # if (!string-equal?(stmt->operation, "copy-byte")) break
+      (string-equal? %ecx "copy-byte")  # => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 84/jump-if-= break/disp32
+      (translate-mu-copy-byte-stmt *(ebp+8) *(ebp+0xc) *(ebp+0x18) *(ebp+0x1c))
+      e9/jump $emit-subx-stmt:end/disp32
+    }
+    # copy-byte-to can be a primitive; writes to memory don't need to clear surrounding bytes
+    # array size
+    {
+      # if (!string-equal?(stmt->operation, "length")) break
+      (string-equal? %ecx "length")  # => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 84/jump-if-= break/disp32
+      (translate-mu-length-stmt *(ebp+8) *(ebp+0xc) *(ebp+0x18) *(ebp+0x1c))
+      e9/jump $emit-subx-stmt:end/disp32
+    }
+    # index into array
+    {
+      # if (!string-equal?(stmt->operation, "index")) break
+      (string-equal? %ecx "index")  # => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 84/jump-if-= break/disp32
+      (translate-mu-index-stmt *(ebp+8) *(ebp+0xc) *(ebp+0x14) *(ebp+0x18) *(ebp+0x1c))
+      e9/jump $emit-subx-stmt:end/disp32
+    }
+    # compute-offset for index into array
+    {
+      # if (!string-equal?(stmt->operation, "compute-offset")) break
+      (string-equal? %ecx "compute-offset")  # => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 84/jump-if-= break/disp32
+      (translate-mu-compute-index-stmt *(ebp+8) *(ebp+0xc) *(ebp+0x18) *(ebp+0x1c))
+      e9/jump $emit-subx-stmt:end/disp32
+    }
+    # get field from record
+    {
+      # if (!string-equal?(stmt->operation, "get")) break
+      (string-equal? %ecx "get")  # => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 84/jump-if-= break/disp32
+      (translate-mu-get-stmt *(ebp+8) *(ebp+0xc))
+      e9/jump $emit-subx-stmt:end/disp32
+    }
+    # allocate scalar
+    {
+      # if (!string-equal?(stmt->operation, "allocate")) break
+      (string-equal? %ecx "allocate")  # => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 84/jump-if-= break/disp32
+      (translate-mu-allocate-stmt *(ebp+8) *(ebp+0xc) *(ebp+0x18) *(ebp+0x1c))
+      e9/jump $emit-subx-stmt:end/disp32
+    }
+    # copy-object
+    {
+      # if (!string-equal?(stmt->operation, "copy-object")) break
+      (string-equal? %ecx "copy-object")  # => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 84/jump-if-= break/disp32
+      (translate-mu-copy-object-stmt *(ebp+8) *(ebp+0xc) *(ebp+0x18) *(ebp+0x1c))
+      e9/jump $emit-subx-stmt:end/disp32
+    }
+    # clear-object
+    {
+      # if (!string-equal?(stmt->operation, "clear-object")) break
+      (string-equal? %ecx "clear-object")  # => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 84/jump-if-= break/disp32
+      (translate-mu-clear-object-stmt *(ebp+8) *(ebp+0xc) *(ebp+0x18) *(ebp+0x1c))
+      e9/jump $emit-subx-stmt:end/disp32
+    }
+    # allocate array
+    {
+      # if (!string-equal?(stmt->operation, "populate")) break
+      (string-equal? %ecx "populate")  # => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 84/jump-if-= break/disp32
+      (translate-mu-populate-stmt *(ebp+8) *(ebp+0xc) *(ebp+0x18) *(ebp+0x1c))
+      e9/jump $emit-subx-stmt:end/disp32
+    }
+    # allocate stream
+    {
+      # if (!string-equal?(stmt->operation, "populate-stream")) break
+      (string-equal? %ecx "populate-stream")  # => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 84/jump-if-= break/disp32
+      (translate-mu-populate-stream-stmt *(ebp+8) *(ebp+0xc) *(ebp+0x18) *(ebp+0x1c))
+      e9/jump $emit-subx-stmt:end/disp32
+    }
+    # read from stream
+    {
+      # if (!string-equal?(stmt->operation, "read-from-stream")) break
+      (string-equal? %ecx "read-from-stream")  # => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 84/jump-if-= break/disp32
+      (translate-mu-read-from-stream-stmt *(ebp+8) *(ebp+0xc) *(ebp+0x18) *(ebp+0x1c))
+      e9/jump $emit-subx-stmt:end/disp32
+    }
+    # write to stream
+    {
+      # if (!string-equal?(stmt->operation, "write-to-stream")) break
+      (string-equal? %ecx "write-to-stream")  # => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 84/jump-if-= break/disp32
+      (translate-mu-write-to-stream-stmt *(ebp+8) *(ebp+0xc) *(ebp+0x18) *(ebp+0x1c))
+      e9/jump $emit-subx-stmt:end/disp32
+    }
+    # - optimizations
+    # if copy instruction has same register in source and destination, emit nothing
+    (is-redundant-copy? *(ebp+0xc))  # => eax
+    3d/compare-eax-and 0/imm32/false
+    75/jump-if-!= $emit-subx-stmt:end/disp8
+    # - if stmt matches a primitive, emit it
+    {
+$emit-subx-stmt:check-for-primitive:
+      # var curr/eax: (addr primitive)
+      (find-matching-primitive *(ebp+0x10) *(ebp+0xc))  # primitives, stmt => eax
+      3d/compare-eax-and 0/imm32
+      74/jump-if-= break/disp8
+$emit-subx-stmt:primitive:
+      (emit-subx-primitive *(ebp+8) *(ebp+0xc) %eax)  # out, stmt, curr
+      e9/jump $emit-subx-stmt:end/disp32
+    }
+    # - otherwise emit a call
+    # TODO: type-checking
+$emit-subx-stmt:call:
+    (emit-call *(ebp+8) *(ebp+0xc))
+$emit-subx-stmt:end:
+    # . restore registers
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+is-redundant-copy?:  # stmt: (addr stmt) -> result/eax: boolean
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    56/push-esi
+    57/push-edi
+    # esi = stmt
+    8b/-> *(ebp+8) 6/r32/esi
+    # if stmt->operation != "copy" return false
+    (lookup *(esi+4) *(esi+8))  # Stmt1-operation Stmt1-operation => eax
+    (string-equal? %eax "copy")  # => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 84/jump-if-= $is-redundant-copy?:end/disp32
+    # var output-reg/edi: (addr stmt-var) = stmt->outputs->value->register
+    (lookup *(esi+0x14) *(esi+0x18))  # Stmt1-outputs Stmt1-outputs => eax
+    (lookup *eax *(eax+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *(eax+0x18) *(eax+0x1c))  # Var-register Var-register => eax
+    # . if output-reg == null, return false
+    3d/compare-eax-and 0/imm32
+    74/jump-if-= $is-redundant-copy?:end/disp8
+    89/<- %edi 0/r32/eax
+    # return (inout->value->register == output->value->register)
+    (lookup *(esi+0xc) *(esi+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+    # . if inout->is-deref return false
+    81 7/subop/compare *(eax+0x10) 0/imm32/false  # Stmt-var-is-deref
+    {
+      74/jump-if-= break/disp8
+      b8/copy-to-eax 0/imm32/false
+      e9/jump $is-redundant-copy?:end/disp32
+    }
+    (lookup *eax *(eax+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *(eax+0x18) *(eax+0x1c))  # Var-register Var-register => eax
+    # . if inout-reg == null, return false
+    3d/compare-eax-and 0/imm32
+    74/jump-if-= $is-redundant-copy?:end/disp8
+    (string-equal? %eax %edi)  # => eax
+$is-redundant-copy?:end:
+    # . restore registers
+    5f/pop-to-edi
+    5e/pop-to-esi
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+translate-mu-length-stmt:  # out: (addr buffered-file), stmt: (addr stmt), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    52/push-edx
+    53/push-ebx
+    56/push-esi
+    # esi = stmt
+    8b/-> *(ebp+0xc) 6/r32/esi
+    # var base/ebx: (addr var) = stmt->inouts[0]->value
+    (lookup *(esi+0xc) *(esi+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+    (lookup *eax *(eax+4))  # Stmt-var-value Stmt-var-value => eax
+    89/<- %ebx 0/r32/eax
+    # var elemsize/ecx: int = array-element-size(base)
+    (array-element-size %ebx *(ebp+0x10) *(ebp+0x14))  # => eax
+    89/<- %ecx 0/r32/eax
+    # var outreg/edx: (addr array byte) = stmt->outputs[0]->value->register
+    (lookup *(esi+0x14) *(esi+0x18))  # Stmt1-outputs Stmt1-outputs => eax
+    (lookup *eax *(eax+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *(eax+0x18) *(eax+0x1c))  # Var-register Var-register => eax
+    89/<- %edx 0/r32/eax
+    # if elemsize == 1
+    {
+      81 7/subop/compare %ecx 1/imm32
+      75/jump-if-!= break/disp8
+$translate-mu-length-stmt:size-1:
+      (emit-save-size-to *(ebp+8) %ebx %edx)
+      e9/jump $translate-mu-length-stmt:end/disp32
+    }
+    # if elemsize is a power of 2 less than 256
+    {
+      (power-of-2? %ecx *(ebp+0x10) *(ebp+0x14))  # => eax
+      3d/compare-eax-and 0/imm32/false
+      74/jump-if-= break/disp8
+      81 7/subop/compare %ecx 0xff/imm32
+      7f/jump-if-> break/disp8
+$translate-mu-length-stmt:size-power-of-2:
+      (emit-save-size-to *(ebp+8) %ebx %edx)
+      (emit-divide-by-shift-right *(ebp+8) %edx %ecx)
+      e9/jump $translate-mu-length-stmt:end/disp32
+    }
+    # otherwise, the complex case
+    # . emit register spills
+    {
+$translate-mu-length-stmt:complex:
+      (string-equal? %edx "eax")  # => eax
+      3d/compare-eax-and 0/imm32/false
+      75/break-if-!= break/disp8
+      (emit-indent *(ebp+8) *Curr-block-depth)
+      (write-buffered *(ebp+8) "50/push-eax\n")
+    }
+    {
+      (string-equal? %edx "ecx")  # => eax
+      3d/compare-eax-and 0/imm32/false
+      75/break-if-!= break/disp8
+      (emit-indent *(ebp+8) *Curr-block-depth)
+      (write-buffered *(ebp+8) "51/push-ecx\n")
+    }
+    {
+      (string-equal? %edx "edx")  # => eax
+      3d/compare-eax-and 0/imm32/false
+      75/break-if-!= break/disp8
+      (emit-indent *(ebp+8) *Curr-block-depth)
+      (write-buffered *(ebp+8) "52/push-edx\n")
+    }
+    # .
+    (emit-save-size-to *(ebp+8) %ebx "eax")
+    (emit-indent *(ebp+8) *Curr-block-depth)
+    (write-buffered *(ebp+8) "31/xor %edx 2/r32/edx\n")
+    (emit-indent *(ebp+8) *Curr-block-depth)
+    (write-buffered *(ebp+8) "b9/copy-to-ecx ")
+    (write-int32-hex-buffered *(ebp+8) %ecx)
+    (write-buffered *(ebp+8) "/imm32\n")
+    (emit-indent *(ebp+8) *Curr-block-depth)
+    (write-buffered *(ebp+8) "f7 7/subop/idiv-eax-edx-by %ecx\n")
+    {
+      (string-equal? %edx "eax")  # => eax
+      3d/compare-eax-and 0/imm32/false
+      75/break-if-!= break/disp8
+      (emit-indent *(ebp+8) *Curr-block-depth)
+      (write-buffered *(ebp+8) "89/<- %")
+      (write-buffered *(ebp+8) %edx)
+      (write-buffered *(ebp+8) " 0/r32/eax\n")
+    }
+    # . emit register restores
+    {
+      (string-equal? %edx "edx")  # => eax
+      3d/compare-eax-and 0/imm32/false
+      75/break-if-!= break/disp8
+      (emit-indent *(ebp+8) *Curr-block-depth)
+      (write-buffered *(ebp+8) "5a/pop-to-edx\n")
+    }
+    {
+      (string-equal? %edx "ecx")  # => eax
+      3d/compare-eax-and 0/imm32/false
+      75/break-if-!= break/disp8
+      (emit-indent *(ebp+8) *Curr-block-depth)
+      (write-buffered *(ebp+8) "59/pop-to-ecx\n")
+    }
+    {
+      (string-equal? %edx "eax")  # => eax
+      3d/compare-eax-and 0/imm32/false
+      75/break-if-!= break/disp8
+      (emit-indent *(ebp+8) *Curr-block-depth)
+      (write-buffered *(ebp+8) "58/pop-to-eax\n")
+    }
+$translate-mu-length-stmt:end:
+    # . restore registers
+    5e/pop-to-esi
+    5b/pop-to-ebx
+    5a/pop-to-edx
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+array-element-size:  # arr: (addr var), err: (addr buffered-file), ed: (addr exit-descriptor) -> result/eax: int
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    #
+    (array-element-type-id *(ebp+8) *(ebp+0xc) *(ebp+0x10))  # => eax
+    (size-of-type-id-as-array-element %eax)  # => eax
+$array-element-size:end:
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+array-element-type-id:  # v: (addr var), err: (addr buffered-file), ed: (addr exit-descriptor) -> result/eax: type-id
+    # precondition: n is positive
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    #
+    8b/-> *(ebp+8) 0/r32/eax
+    # var t/eax: (addr type-tree)
+    (lookup *(eax+8) *(eax+0xc))  # Var-type Var-type => eax
+    # if t == 0 abort
+    3d/compare-eax-with 0/imm32
+    0f 84/jump-if-== $array-element-type-id:error0/disp32
+    # if t->is-atom? abort
+    81 7/subop/compare *eax 0/imm32/false  # Type-tree-is-atom
+    0f 85/jump-if-!= $array-element-type-id:error1/disp32
+    # if (t->left == addr) t = t->right
+    {
+      50/push-eax
+      (lookup *(eax+4) *(eax+8))  # Type-tree-left Type-tree-left => eax
+      (is-simple-mu-type? %eax 2)  # addr => eax
+      3d/compare-eax-with 0/imm32/false
+      58/pop-to-eax
+      74/jump-if-= break/disp8
+$array-element-type-id:skip-addr:
+      (lookup *(eax+0xc) *(eax+0x10))  # Type-tree-right Type-tree-right => eax
+    }
+    # if t == 0 abort
+    3d/compare-eax-with 0/imm32
+    0f 84/jump-if-= $array-element-type-id:error2/disp32
+    # if t->is-atom? abort
+    81 7/subop/compare *eax 0/imm32/false  # Type-tree-is-atom
+    0f 85/jump-if-!= $array-element-type-id:error2/disp32
+    # if t->left != array abort
+    {
+      50/push-eax
+      (lookup *(eax+4) *(eax+8))  # Type-tree-left Type-tree-left => eax
+      (is-simple-mu-type? %eax 3)  # array => eax
+      3d/compare-eax-with 0/imm32/false
+      58/pop-to-eax
+$array-element-type-id:no-array:
+      0f 84/jump-if-= $array-element-type-id:error2/disp32
+    }
+$array-element-type-id:skip-array:
+    # t = t->right
+    (lookup *(eax+0xc) *(eax+0x10))  # Type-tree-right Type-tree-right => eax
+    # if t == 0 abort
+    3d/compare-eax-with 0/imm32
+    0f 84/jump-if-= $array-element-type-id:error2/disp32
+    # if t->is-atom? abort
+    81 7/subop/compare *eax 0/imm32/false  # Type-tree-is-atom
+    0f 85/jump-if-!= $array-element-type-id:error2/disp32
+    # t = t->left
+    (lookup *(eax+4) *(eax+8))  # Type-tree-left Type-tree-left => eax
+    # if (!t->is-atom?) t = t->left     # TODO: assumes array element size can be determined from just first word of array element type
+    # if (t->is-atom == false) t = lookup(t->left)
+    {
+      81 7/subop/compare *eax 0/imm32/false  # Type-tree-is-atom
+      75/jump-if-!= break/disp8
+      (lookup *(eax+4) *(eax+8))  # Type-tree-left Type-tree-left => eax
+    }
+    # return t->value
+    8b/-> *(eax+4) 0/r32/eax  # Type-tree-value
+$array-element-type-id:end:
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+$array-element-type-id:error0:
+    (write-buffered *(ebp+0xc) "array-element-type-id: var '")
+    50/push-eax
+    8b/-> *(ebp+8) 0/r32/eax
+    (lookup *eax *(eax+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0xc) %eax)
+    58/pop-to-eax
+    (write-buffered *(ebp+0xc) "' has no type\n")
+    (flush *(ebp+0xc))
+    (stop *(ebp+0x10) 1)
+    # never gets here
+
+$array-element-type-id:error1:
+    (write-buffered *(ebp+0xc) "array-element-type-id: var '")
+    50/push-eax
+    8b/-> *(ebp+8) 0/r32/eax
+    (lookup *eax *(eax+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0xc) %eax)
+    58/pop-to-eax
+    (write-buffered *(ebp+0xc) "' has atomic type ")
+    (write-int32-hex-buffered *(ebp+0xc) *(eax+4))  # Type-tree-value
+    (write-buffered *(ebp+0xc) Newline)
+    (flush *(ebp+0xc))
+    (stop *(ebp+0x10) 1)
+    # never gets here
+
+$array-element-type-id:error2:
+    (write-buffered *(ebp+0xc) "array-element-type-id: var '")
+    50/push-eax
+    8b/-> *(ebp+8) 0/r32/eax
+    (lookup *eax *(eax+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0xc) %eax)
+    58/pop-to-eax
+    (write-buffered *(ebp+0xc) "' has non-array type\n")
+    (flush *(ebp+0xc))
+    (stop *(ebp+0x10) 1)
+    # never gets here
+
+size-of-type-id-as-array-element:  # t: type-id -> result/eax: int
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # eax = t
+    8b/-> *(ebp+8) 0/r32/eax
+    # if t is 'byte', size is 1
+    3d/compare-eax-and 8/imm32/byte
+    {
+      75/jump-if-!= break/disp8
+      b8/copy-to-eax 1/imm32
+      eb/jump $size-of-type-id-as-array-element:end/disp8
+    }
+    # otherwise proceed as usual
+    (size-of-type-id %eax)  # => eax
+$size-of-type-id-as-array-element:end:
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+emit-save-size-to:  # out: (addr buffered-file), base: (addr var), outreg: (addr array byte)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    53/push-ebx
+    # ebx = base
+    8b/-> *(ebp+0xc) 3/r32/ebx
+    (emit-indent *(ebp+8) *Curr-block-depth)
+    (write-buffered *(ebp+8) "8b/-> *")
+    # if base is an (addr array ...) in a register
+    {
+      81 7/subop/compare *(ebx+0x18)) 0/imm32  # Var-register
+      74/jump-if-= break/disp8
+$emit-save-size-to:emit-base-from-register:
+      (lookup *(ebx+0x18) *(ebx+0x1c))  # Var-register Var-register => eax
+      (write-buffered *(ebp+8) %eax)
+      eb/jump $emit-save-size-to:emit-output/disp8
+    }
+    # otherwise if base is an (array ...) on the stack
+    {
+      81 7/subop/compare *(ebx+0x14)) 0/imm32  # Var-offset
+      74/jump-if-= break/disp8
+$emit-save-size-to:emit-base-from-stack:
+      (write-buffered *(ebp+8) "(ebp+")
+      (write-int32-hex-buffered *(ebp+8) *(ebx+0x14))  # Var-offset
+      (write-buffered *(ebp+8) ")")
+    }
+$emit-save-size-to:emit-output:
+    (write-buffered *(ebp+8) " ")
+    (get Mu-registers *(ebp+0x10) 0xc "Mu-registers")  # => eax
+    (write-int32-hex-buffered *(ebp+8) *eax)
+    (write-buffered *(ebp+8) "/r32\n")
+$emit-save-size-to:end:
+    # . restore registers
+    5b/pop-to-ebx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+emit-divide-by-shift-right:  # out: (addr buffered-file), reg: (addr array byte), size: int
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    #
+    (emit-indent *(ebp+8) *Curr-block-depth)
+    (write-buffered *(ebp+8) "c1/shift 5/subop/>> %")
+    (write-buffered *(ebp+8) *(ebp+0xc))
+    (write-buffered *(ebp+8) Space)
+    (num-shift-rights *(ebp+0x10))  # => eax
+    (write-int32-hex-buffered *(ebp+8) %eax)
+    (write-buffered *(ebp+8) "/imm8\n")
+$emit-divide-by-shift-right:end:
+    # . restore registers
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+translate-mu-copy-byte-stmt:  # out: (addr buffered-file), stmt: (addr stmt), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    56/push-esi
+    # esi = stmt
+    8b/-> *(ebp+0xc) 6/r32/esi
+    #
+    (emit-indent *(ebp+8) *Curr-block-depth)
+    (write-buffered *(ebp+8) "8a/byte->")
+    # emit stmt->inouts[0]
+    (lookup *(esi+0xc) *(esi+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+    (emit-subx-var-as-rm32 *(ebp+8) %eax)
+    # emit /r32 for stmt->outputs[0]->register
+    (lookup *(esi+0x14) *(esi+0x18))  # Stmt1-outputs Stmt1-outputs => eax
+    (lookup *eax *(eax+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *(eax+0x18) *(eax+0x1c))  # Var-register Var-register => eax
+    (maybe-get Mu-registers %eax 0xc)  # => eax: (addr register-index)
+    (write-buffered *(ebp+8) Space)
+    (write-int32-hex-buffered *(ebp+8) *eax)
+    (write-buffered *(ebp+8) "/r32\n")
+    # clear rest of register
+    (emit-indent *(ebp+8) *Curr-block-depth)
+    (write-buffered *(ebp+8) "81 4/subop/and %")
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *(esi+0x14) *(esi+0x18))  # Stmt1-outputs Stmt1-outputs => eax
+    (lookup *eax *(eax+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *(eax+0x18) *(eax+0x1c))  # Var-register Var-register => eax
+    (write-buffered *(ebp+8) %eax)
+    (write-buffered *(ebp+8) " 0xff/imm32\n")
+$translate-mu-copy-byte-stmt:end:
+    # . restore registers
+    5e/pop-to-esi
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+# a little different from other translate- functions; notice the extra 'fn' argument
+translate-mu-index-stmt:  # out: (addr buffered-file), stmt: (addr stmt), fn: (addr function), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    53/push-ebx
+    # ebx = stmt
+    8b/-> *(ebp+0xc) 3/r32/ebx
+    # var base/ebx: (addr var) = stmt->inouts[0]
+    (lookup *(ebx+0xc) *(ebx+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+    (lookup *eax *(eax+4))  # Stmt-var-value Stmt-var-value => eax
+    89/<- %ebx 0/r32/eax
+    # emit bounds-check
+    (emit-mu-index-bounds-check *(ebp+8) *(ebp+0xc) *(ebp+0x10) *(ebp+0x14) *(ebp+0x18))
+    # if (var->register) do one thing
+    {
+      81 7/subop/compare *(ebx+0x18) 0/imm32  # Var-register
+      74/jump-if-= break/disp8
+      # TODO: ensure there's no dereference
+      (translate-mu-index-stmt-with-array-in-register *(ebp+8) *(ebp+0xc) *(ebp+0x14) *(ebp+0x18))
+      eb/jump $translate-mu-index-stmt:end/disp8
+    }
+    # if (var->offset) do a different thing
+    {
+      81 7/subop/compare *(ebx+0x14) 0/imm32  # Var-offset
+      74/jump-if-= break/disp8
+      # TODO: ensure there's no dereference
+      (translate-mu-index-stmt-with-array-on-stack *(ebp+8) *(ebp+0xc) *(ebp+0x14) *(ebp+0x18))
+      eb/jump $translate-mu-index-stmt:end/disp8
+    }
+$translate-mu-index-stmt:end:
+    # . restore registers
+    5b/pop-to-ebx
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+$translate-mu-index-stmt:error1:
+    (write-buffered *(ebp+0x14) "couldn't translate an index instruction. second (index) input must either lie in a register or be a literal\n")
+    (flush *(ebp+0x14))
+    (stop *(ebp+0x18) 1)
+    # never gets here
+
+$translate-mu-index-stmt:error2:
+    (write-buffered *(ebp+0x14) "couldn't translate an index instruction. second (index) input when in a register must be an int or offset\n")
+    (flush *(ebp+0x14))
+    (stop *(ebp+0x18) 1)
+    # never gets here
+
+emit-mu-index-bounds-check:  # out: (addr buffered-file), stmt: (addr stmt), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    52/push-edx
+    53/push-ebx
+    # ecx = stmt
+    8b/-> *(ebp+0xc) 1/r32/ecx
+    #
+    (emit-indent *(ebp+8) *Curr-block-depth)
+    (write-buffered *(ebp+8) "(__check-mu-array-bounds ")
+$emit-mu-index-bounds-check:compute-base:
+    # var base/ebx: (addr var) = inouts[0]
+    (lookup *(ecx+0xc) *(ecx+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+    (lookup *eax *(eax+4))  # Stmt-var-value Stmt-var-value => eax
+    89/<- %ebx 0/r32/eax
+$emit-mu-index-bounds-check:emit-index:
+    # var index/edx: (addr var) = inouts[1]
+    (lookup *(ecx+0xc) *(ecx+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+    (lookup *(eax+8) *(eax+0xc))  # Stmt-var-next Stmt-var-next => eax
+    (lookup *eax *(eax+4))  # Stmt-var-value Stmt-var-value => eax
+    89/<- %edx 0/r32/eax
+    # if index->register, print its code
+    81 7/subop/compare *(edx+0x18) 0/imm32  # Var-register
+    {
+      0f 84/jump-if-= break/disp32
+$emit-mu-index-bounds-check:emit-register-index:
+      (write-buffered *(ebp+8) "%")
+      (lookup *(edx+0x18) *(edx+0x1c))  # Var-register Var-register => eax
+      (write-buffered *(ebp+8) %eax)
+      eb/jump $emit-mu-index-bounds-check:index-done/disp8
+    }
+    # otherwise if index is a literal, print it
+$emit-mu-index-bounds-check:emit-literal-index:
+    (lookup *(edx+8) *(edx+0xc))  # Var-type Var-type => eax
+    (is-simple-mu-type? %eax 0)  # => eax
+    3d/compare-eax-and 0/imm32/false
+    {
+      0f 84/jump-if-= break/disp32
+      (lookup *edx *(edx+4))  # Var-name Var-name => eax
+      (write-buffered *(ebp+8) %eax)
+    }
+$emit-mu-index-bounds-check:index-done:
+    (write-buffered *(ebp+8) " ")
+$emit-mu-index-bounds-check:emit-element-size:
+    # if index is a literal or int, print size of array element
+    {
+      {
+        (lookup *(edx+8) *(edx+0xc))  # Var-type Var-type => eax
+        (is-simple-mu-type? %eax 0)  # literal => eax
+        3d/compare-eax-and 0/imm32/false
+        75/jump-if-!= break/disp8
+        (lookup *(edx+8) *(edx+0xc))  # Var-type Var-type => eax
+        (is-simple-mu-type? %eax 1)  # int => eax
+        3d/compare-eax-and 0/imm32/false
+        75/jump-if-!= break/disp8
+        eb/jump $emit-mu-index-bounds-check:emit-element-size-offset/disp8
+      }
+$emit-mu-index-bounds-check:emit-int-register-index:
+      (array-element-size %ebx *(ebp+0x14) *(ebp+0x18))  # => eax
+      (write-int32-hex-buffered *(ebp+8) %eax)
+      e9/jump $emit-mu-index-bounds-check:emit-base/disp32
+    }
+$emit-mu-index-bounds-check:emit-element-size-offset:
+    # if index has type (offset ...), print "1"
+    (lookup *(edx+8) *(edx+0xc))  # Var-type Var-type => eax
+    81 7/subop/compare *eax 0/imm32/false  # Type-tree-is-atom
+    {
+      75/jump-if-!= break/disp8
+      (lookup *(eax+4) *(eax+8))  # Type-tree-left Type-tree-left => eax
+      (is-simple-mu-type? %eax 7)  # => eax
+      3d/compare-eax-and 0/imm32/false
+      {
+        0f 84/jump-if-= break/disp32
+$emit-mu-index-bounds-check:emit-offset-register-index:
+        (write-buffered *(ebp+8) "1")
+      }
+    }
+$emit-mu-index-bounds-check:emit-base:
+    # if base is in a register, print " *" base->register
+    81 7/subop/compare *(ebx+0x18) 0/imm32  # Var-register
+    {
+      74/jump-if-= break/disp8
+      (write-buffered *(ebp+8) " *")
+      (lookup *(ebx+0x18) *(ebx+0x1c))  # Var-register Var-register => eax
+      (write-buffered *(ebp+8) %eax)
+      e9/jump $emit-mu-index-bounds-check:emit-function-name/disp32
+    }
+    # otherwise print " *(ebp+" base->offset ")"
+    (write-buffered *(ebp+8) " *(ebp+")
+    (write-int32-hex-buffered *(ebp+8) *(ebx+0x14))  # Var-offset
+    (write-buffered *(ebp+8) ")")
+$emit-mu-index-bounds-check:emit-function-name:
+    # " \"" function-name "\""
+    (write-buffered *(ebp+8) " \"")
+    8b/-> *(ebp+0x10) 1/r32/ecx
+    (lookup *ecx *(ecx+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+8) %eax)
+    (write-buffered *(ebp+8) "\"")
+$emit-mu-index-bounds-check:emit-array-name:
+    # " \"" base->name "\""
+    (write-buffered *(ebp+8) " \"")
+    (lookup *ebx *(ebx+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+8) %eax)
+    (write-buffered *(ebp+8) "\")\n")
+$emit-mu-index-bounds-check:end:
+    # . restore registers
+    5b/pop-to-ebx
+    5a/pop-to-edx
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+translate-mu-index-stmt-with-array-in-register:  # out: (addr buffered-file), stmt: (addr stmt), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    52/push-edx
+    53/push-ebx
+    #
+    (emit-indent *(ebp+8) *Curr-block-depth)
+    (write-buffered *(ebp+8) "8d/copy-address *(")
+    # TODO: ensure inouts[0] is in a register and not dereferenced
+$translate-mu-index-stmt-with-array-in-register:emit-base:
+    # ecx = stmt
+    8b/-> *(ebp+0xc) 1/r32/ecx
+    # var base/ebx: (addr var) = inouts[0]
+    (lookup *(ecx+0xc) *(ecx+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+    (lookup *eax *(eax+4))  # Stmt-var-value Stmt-var-value => eax
+    89/<- %ebx 0/r32/eax
+    # print base->register " + "
+    (lookup *(ebx+0x18) *(ebx+0x1c))  # Var-register Var-register => eax
+    (write-buffered *(ebp+8) %eax)
+    (write-buffered *(ebp+8) " + ")
+    # var index/edx: (addr var) = inouts[1]
+    (lookup *(ecx+0xc) *(ecx+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+    (lookup *(eax+8) *(eax+0xc))  # Stmt-var-next Stmt-var-next => eax
+    (lookup *eax *(eax+4))  # Stmt-var-value Stmt-var-value => eax
+    89/<- %edx 0/r32/eax
+    # if index->register
+    81 7/subop/compare *(edx+0x18) 0/imm32  # Var-register
+    {
+      0f 84/jump-if-= break/disp32
+$translate-mu-index-stmt-with-array-in-register:emit-register-index:
+      # if index is an int
+      (lookup *(edx+8) *(edx+0xc))  # Var-type Var-type => eax
+      (is-simple-mu-type? %eax 1)  # int => eax
+      3d/compare-eax-and 0/imm32/false
+      {
+        0f 84/jump-if-= break/disp32
+$translate-mu-index-stmt-with-array-in-register:emit-int-register-index:
+        # print index->register "<<" log2(array-element-size(base)) " + 4) "
+        # . index->register "<<"
+        (lookup *(edx+0x18) *(edx+0x1c))  # Var-register Var-register => eax
+        (write-buffered *(ebp+8) %eax)
+        (write-buffered *(ebp+8) "<<")
+        # . log2(array-element-size(base->type))
+        # we know size is a power of 2
+        (array-element-size %ebx *(ebp+0x10) *(ebp+0x14))  # => eax
+        (num-shift-rights %eax)  # => eax
+        (write-int32-hex-buffered *(ebp+8) %eax)
+        e9/jump $translate-mu-index-stmt-with-array-in-register:emit-register-index-done/disp32
+      }
+      # if index->type is any other atom, abort
+      (lookup *(edx+8) *(edx+0xc))  # Var-type Var-type => eax
+      81 7/subop/compare *eax 0/imm32/false  # Type-tree-is-atom
+      0f 85/jump-if-!= $translate-mu-index-stmt:error2/disp32
+      # if index has type (offset ...)
+      (lookup *(eax+4) *(eax+8))  # Type-tree-left Type-tree-left => eax
+      (is-simple-mu-type? %eax 7)  # => eax
+      3d/compare-eax-and 0/imm32/false
+      {
+        0f 84/jump-if-= break/disp32
+        # print index->register
+$translate-mu-index-stmt-with-array-in-register:emit-offset-register-index:
+        (lookup *(edx+0x18) *(edx+0x1c))  # Var-register Var-register => eax
+        (write-buffered *(ebp+8) %eax)
+      }
+$translate-mu-index-stmt-with-array-in-register:emit-register-index-done:
+      (write-buffered *(ebp+8) " + 4) ")
+      e9/jump $translate-mu-index-stmt-with-array-in-register:emit-output/disp32
+    }
+    # otherwise if index is a literal
+    (lookup *(edx+8) *(edx+0xc))  # Var-type Var-type => eax
+    (is-simple-mu-type? %eax 0)  # => eax
+    3d/compare-eax-and 0/imm32/false
+    {
+      0f 84/jump-if-= break/disp32
+$translate-mu-index-stmt-with-array-in-register:emit-literal-index:
+      # var index-value/edx: int = parse-hex-int(index->name)
+      (lookup *edx *(edx+4))  # Var-name Var-name => eax
+      (parse-hex-int %eax)  # => eax
+      89/<- %edx 0/r32/eax
+      # offset = idx-value * array-element-size(base->type)
+      (array-element-size %ebx *(ebp+0x10) *(ebp+0x14))  # => eax
+      f7 4/subop/multiply-into-edx-eax %edx  # clobbers edx
+      # offset += 4 for array size
+      05/add-to-eax 4/imm32
+      # TODO: check edx for overflow
+      # print offset
+      (write-int32-hex-buffered *(ebp+8) %eax)
+      (write-buffered *(ebp+8) ") ")
+      e9/jump $translate-mu-index-stmt-with-array-in-register:emit-output/disp32
+    }
+    # otherwise abort
+    e9/jump $translate-mu-index-stmt:error1/disp32
+$translate-mu-index-stmt-with-array-in-register:emit-output:
+    # outputs[0] "/r32"
+    8b/-> *(ebp+0xc) 1/r32/ecx
+    (lookup *(ecx+0x14) *(ecx+0x18))  # Stmt1-outputs Stmt1-outputs => eax
+    (lookup *eax *(eax+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *(eax+0x18) *(eax+0x1c))  # Var-register Var-register => eax
+    (get Mu-registers %eax 0xc "Mu-registers")  # => eax: (addr int)
+    (write-int32-hex-buffered *(ebp+8) *eax)
+    (write-buffered *(ebp+8) "/r32\n")
+$translate-mu-index-stmt-with-array-in-register:end:
+    # . restore registers
+    5b/pop-to-ebx
+    5a/pop-to-edx
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+translate-mu-index-stmt-with-array-on-stack:  # out: (addr buffered-file), stmt: (addr stmt), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    52/push-edx
+    53/push-ebx
+    #
+    (emit-indent *(ebp+8) *Curr-block-depth)
+    (write-buffered *(ebp+8) "8d/copy-address *(ebp + ")
+    # var curr/edx: (addr stmt-var) = lookup(stmt->inouts)
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *(eax+0xc) *(eax+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+    89/<- %edx 0/r32/eax
+    # var base/ecx: (addr var) = lookup(curr->value)
+    (lookup *eax *(eax+4))  # Stmt-var-value Stmt-var-value => eax
+    89/<- %ecx 0/r32/eax
+    # var curr2/eax: (addr stmt-var) = lookup(curr->next)
+    (lookup *(edx+8) *(edx+0xc))  # Stmt-var-next Stmt-var-next => eax
+    # var index/edx: (handle var) = curr2->value
+    (lookup *eax *(eax+4))  # Stmt-var-value Stmt-var-value => eax
+    89/<- %edx 0/r32/eax
+    # if index->register
+    81 7/subop/compare *(edx+0x18) 0/imm32  # Var-register
+    {
+      0f 84/jump-if-= break/disp32
+$translate-mu-index-stmt-with-array-on-stack:emit-register-index:
+      # if index is an int
+      (lookup *(edx+8) *(edx+0xc))  # Var-type Var-type => eax
+      (is-simple-mu-type? %eax 1)  # int => eax
+      3d/compare-eax-and 0/imm32/false
+      {
+        0f 84/jump-if-= break/disp32
+$translate-mu-index-stmt-with-array-on-stack:emit-int-register-index:
+        # print index->register "<<" log2(array-element-size(base)) " + " base->offset+4
+        # . inouts[1]->register "<<"
+        (lookup *(edx+0x18) *(edx+0x1c))  # Var-register Var-register => eax
+        (write-buffered *(ebp+8) %eax)
+        (write-buffered *(ebp+8) "<<")
+        # . log2(array-element-size(base))
+        # TODO: ensure size is a power of 2
+        (array-element-size %ecx *(ebp+0x10) *(ebp+0x14))  # => eax
+        (num-shift-rights %eax)  # => eax
+        (write-int32-hex-buffered *(ebp+8) %eax)
+        #
+        (write-buffered *(ebp+8) " + ")
+        #
+        8b/-> *(ecx+0x14) 0/r32/eax  # Var-offset
+        05/add-to-eax 4/imm32  # for array length
+        (write-int32-hex-buffered *(ebp+8) %eax)
+        e9/jump $translate-mu-index-stmt-with-array-on-stack:emit-register-index-done/disp32
+      }
+      # if index->type is any other atom, abort
+      (lookup *(edx+8) *(edx+0xc))  # Var-type Var-type => eax
+      81 7/subop/compare *eax 0/imm32/false  # Type-tree-is-atom
+      0f 85/jump-if-!= $translate-mu-index-stmt:error2/disp32
+      # if index has type (offset ...)
+      (lookup *(eax+4) *(eax+8))  # Type-tree-left Type-tree-left => eax
+      (is-simple-mu-type? %eax 7)  # => eax
+      3d/compare-eax-and 0/imm32/false
+      {
+        0f 84/jump-if-= break/disp32
+        # print index->register
+$translate-mu-index-stmt-with-array-on-stack:emit-offset-register-index:
+        (lookup *(edx+0x18) *(edx+0x1c))  # Var-register Var-register => eax
+        (write-buffered *(ebp+8) %eax)
+      }
+$translate-mu-index-stmt-with-array-on-stack:emit-register-index-done:
+      (write-buffered *(ebp+8) ") ")
+      e9/jump $translate-mu-index-stmt-with-array-on-stack:emit-output/disp32
+    }
+    # otherwise if index is a literal
+    (lookup *(edx+8) *(edx+0xc))  # Var-type Var-type => eax
+    (is-simple-mu-type? %eax 0)  # => eax
+    3d/compare-eax-and 0/imm32/false
+    {
+      0f 84/jump-if-= break/disp32
+$translate-mu-index-stmt-with-array-on-stack:emit-literal-index:
+      # var idx-value/edx: int = parse-hex-int(index->name)
+      (lookup *edx *(edx+4))  # Var-name Var-name => eax
+      (parse-hex-int %eax)  # Var-name => eax
+      89/<- %edx 0/r32/eax
+      # offset = idx-value * array-element-size(base)
+      (array-element-size %ecx *(ebp+0x10) *(ebp+0x14))  # => eax
+      f7 4/subop/multiply-into-edx-eax %edx  # clobbers edx
+      # offset += base->offset
+      03/add *(ecx+0x14) 0/r32/eax  # Var-offset
+      # offset += 4 for array size
+      05/add-to-eax 4/imm32
+      # TODO: check edx for overflow
+      # print offset
+      (write-int32-hex-buffered *(ebp+8) %eax)
+      (write-buffered *(ebp+8) ") ")
+      e9/jump $translate-mu-index-stmt-with-array-on-stack:emit-output/disp32
+    }
+    # otherwise abort
+    e9/jump $translate-mu-index-stmt:error1/disp32
+$translate-mu-index-stmt-with-array-on-stack:emit-output:
+    # outputs[0] "/r32"
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *(eax+0x14) *(eax+0x18))  # Stmt1-outputs Stmt1-outputs => eax
+    (lookup *eax *(eax+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *(eax+0x18) *(eax+0x1c))  # Var-register Var-register => eax
+    (get Mu-registers %eax 0xc "Mu-registers")  # => eax: (addr int)
+    (write-int32-hex-buffered *(ebp+8) *eax)
+    (write-buffered *(ebp+8) "/r32\n")
+$translate-mu-index-stmt-with-array-on-stack:end:
+    # . restore registers
+    5b/pop-to-ebx
+    5a/pop-to-edx
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+translate-mu-compute-index-stmt:  # out: (addr buffered-file), stmt: (addr stmt), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    52/push-edx
+    53/push-ebx
+    #
+    (emit-indent *(ebp+8) *Curr-block-depth)
+    (write-buffered *(ebp+8) "69/multiply")
+    # ecx = stmt
+    8b/-> *(ebp+0xc) 1/r32/ecx
+    # var first-inout/ebx: (addr stmt-var) = stmt->inouts[0]
+    (lookup *(ecx+0xc) *(ecx+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+    89/<- %ebx 0/r32/eax
+$translate-mu-compute-index-stmt:emit-index:
+    (lookup *(ebx+8) *(ebx+0xc))  # Stmt-var-next Stmt-var-next => eax
+    (emit-subx-var-as-rm32 *(ebp+8) %eax)
+    (write-buffered *(ebp+8) Space)
+$translate-mu-compute-index-stmt:emit-elem-size:
+    # var base/ebx: (addr var)
+    (lookup *ebx *(ebx+4))  # Stmt-var-value Stmt-var-value => eax
+    89/<- %ebx 0/r32/eax
+    # print array-element-size(base)
+    (array-element-size %ebx *(ebp+0x10) *(ebp+0x14))  # => eax
+    (write-int32-hex-buffered *(ebp+8) %eax)
+    (write-buffered *(ebp+8) "/imm32 ")
+$translate-mu-compute-index-stmt:emit-output:
+    # outputs[0] "/r32"
+    (lookup *(ecx+0x14) *(ecx+0x18))  # Stmt1-outputs Stmt1-outputs => eax
+    (lookup *eax *(eax+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *(eax+0x18) *(eax+0x1c))  # Var-register Var-register => eax
+    (get Mu-registers %eax 0xc "Mu-registers")  # => eax: (addr int)
+    (write-int32-hex-buffered *(ebp+8) *eax)
+    (write-buffered *(ebp+8) "/r32\n")
+$translate-mu-compute-index-stmt:end:
+    # . restore registers
+    5b/pop-to-ebx
+    5a/pop-to-edx
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+translate-mu-get-stmt:  # out: (addr buffered-file), stmt: (addr stmt)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    52/push-edx
+    #
+    (emit-indent *(ebp+8) *Curr-block-depth)
+    (write-buffered *(ebp+8) "8d/copy-address ")
+    # ecx = stmt
+    8b/-> *(ebp+0xc) 1/r32/ecx
+    # var offset/edx: int = get offset of stmt
+    (mu-get-offset %ecx)  # => eax
+    89/<- %edx 0/r32/eax
+    # var base/eax: (addr var) = stmt->inouts->value
+    (lookup *(ecx+0xc) *(ecx+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+    (lookup *eax *(eax+4))  # Stmt-var-value Stmt-var-value => eax
+    # if base is in a register
+    81 7/subop/compare *(eax+0x18) 0/imm32  # Var-register
+    {
+      0f 84/jump-if-= break/disp32
+$translate-mu-get-stmt:emit-register-input:
+      # emit "*(" base->register " + " offset ") "
+      (write-buffered *(ebp+8) "*(")
+      (lookup *(eax+0x18) *(eax+0x1c))  # Var-register Var-register => eax
+      (write-buffered *(ebp+8) %eax)
+      (write-buffered *(ebp+8) " + ")
+      (write-int32-hex-buffered *(ebp+8) %edx)
+      (write-buffered *(ebp+8) ") ")
+      e9/jump $translate-mu-get-stmt:emit-output/disp32
+    }
+    # otherwise base is on the stack
+    {
+$translate-mu-get-stmt:emit-stack-input:
+      # emit "*(ebp + " inouts[0]->stack-offset + offset ") "
+      (write-buffered *(ebp+8) "*(ebp+")
+      03/add *(eax+0x14) 2/r32/edx  # Var-offset
+      (write-int32-hex-buffered *(ebp+8) %edx)
+      (write-buffered *(ebp+8) ") ")
+      eb/jump $translate-mu-get-stmt:emit-output/disp8
+    }
+$translate-mu-get-stmt:emit-output:
+    # var output/eax: (addr var) = stmt->outputs->value
+    (lookup *(ecx+0x14) *(ecx+0x18))  # Stmt1-outputs Stmt1-outputs => eax
+    (lookup *eax *(eax+4))  # Stmt-var-value Stmt-var-value => eax
+    # emit offset->register "/r32"
+    (lookup *(eax+0x18) *(eax+0x1c))  # Var-register Var-register => eax
+    (get Mu-registers %eax 0xc "Mu-registers")  # => eax: (addr int)
+    (write-int32-hex-buffered *(ebp+8) *eax)
+    (write-buffered *(ebp+8) "/r32\n")
+$translate-mu-get-stmt:end:
+    # . restore registers
+    5a/pop-to-edx
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+translate-mu-copy-object-stmt:  # out: (addr buffered-file), stmt: (addr stmt), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    #
+    (emit-indent *(ebp+8) *Curr-block-depth)
+    (write-buffered *(ebp+8) "(copy-bytes")
+    # eax = stmt
+    8b/-> *(ebp+0xc) 0/r32/eax
+    # var first-inout/eax: (addr stmt-var) = stmt->inouts[0]
+    (lookup *(eax+0xc) *(eax+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+    (emit-subx-call-operand *(ebp+8) %eax)
+    # var second-inout/eax: (addr stmt-var) = stmt->inouts[1]
+    (lookup *(eax+8) *(eax+0xc))  # Stmt-var-next Stmt-var-next => eax
+    (emit-subx-call-operand *(ebp+8) %eax)
+    # emit size of inouts
+    (write-buffered *(ebp+8) Space)
+    (addr-payload-size %eax *(ebp+0x10) *(ebp+0x14))  # => eax
+    (write-int32-hex-buffered *(ebp+8) %eax)
+    (write-buffered *(ebp+8) ")\n")
+$translate-mu-copy-object-stmt:end:
+    # . restore registers
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+translate-mu-clear-object-stmt:  # out: (addr buffered-file), stmt: (addr stmt), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    #
+    (emit-indent *(ebp+8) *Curr-block-depth)
+    (write-buffered *(ebp+8) "(zero-out")
+    # eax = stmt
+    8b/-> *(ebp+0xc) 0/r32/eax
+    # var dest/eax: (addr stmt-var) = stmt->inouts[0]
+    (lookup *(eax+0xc) *(eax+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+    #
+    (emit-subx-call-operand *(ebp+8) %eax)
+    (write-buffered *(ebp+8) Space)
+    (addr-payload-size %eax *(ebp+0x10) *(ebp+0x14))  # => eax
+    (write-int32-hex-buffered *(ebp+8) %eax)
+    (write-buffered *(ebp+8) ")\n")
+$translate-mu-clear-object-stmt:end:
+    # . restore registers
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+translate-mu-allocate-stmt:  # out: (addr buffered-file), stmt: (addr stmt), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    56/push-esi
+    57/push-edi
+    # esi = stmt
+    8b/-> *(ebp+0xc) 6/r32/esi
+    # var target/edi: (addr stmt-var) = stmt->inouts[0]
+    (lookup *(esi+0xc) *(esi+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+    89/<- %edi 0/r32/eax
+    #
+    (emit-indent *(ebp+8) *Curr-block-depth)
+    (write-buffered *(ebp+8) "(allocate Heap ")
+    (addr-handle-payload-size %edi *(ebp+0x10) *(ebp+0x14))  # => eax
+    (write-int32-hex-buffered *(ebp+8) %eax)
+    (emit-subx-call-operand *(ebp+8) %edi)
+    (write-buffered *(ebp+8) ")\n")
+$translate-mu-allocate-stmt:end:
+    # . restore registers
+    5f/pop-to-edi
+    5e/pop-to-esi
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+addr-handle-payload-size:  # s: (addr stmt-var), err: (addr buffered-file), ed: (addr exit-descriptor) -> result/eax: int
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # var t/eax: (addr type-tree) = s->value->type
+    8b/-> *(ebp+8) 0/r32/eax
+    (lookup *eax *(eax+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *(eax+8) *(eax+0xc))  # Var-type Var-type => eax
+    # TODO: check eax != 0
+    # TODO: check !t->is-atom?
+    # TODO: check t->left == addr
+    # t = t->right
+$addr-handle-payload-size:skip-addr:
+    (lookup *(eax+0xc) *(eax+0x10))  # Type-tree-right Type-tree-right => eax
+    # TODO: check eax != 0
+    # TODO: check !t->is-atom?
+    # TODO: check t->left == handle
+    # t = t->right
+$addr-handle-payload-size:skip-handle:
+    (lookup *(eax+0xc) *(eax+0x10))  # Type-tree-right Type-tree-right => eax
+    # TODO: check eax != 0
+    # if !t->is-atom? t = t->left
+    81 7/subop/compare *eax 0/imm32/false
+    {
+      75/jump-if-!= break/disp8
+      (lookup *(eax+4) *(eax+8))  # Type-tree-left Type-tree-left => eax
+    }
+    # TODO: check t->is-atom?
+    # return size(t->value)
+    (size-of-type-id *(eax+4))  # Type-tree-value => eax
+$addr-handle-payload-size:end:
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+addr-payload-size:  # s: (addr stmt-var), err: (addr buffered-file), ed: (addr exit-descriptor) -> result/eax: int
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # var t/eax: (addr type-tree) = s->value->type
+    8b/-> *(ebp+8) 0/r32/eax
+    (lookup *eax *(eax+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *(eax+8) *(eax+0xc))  # Var-type Var-type => eax
+    # TODO: check eax != 0
+    # TODO: check !t->is-atom?
+    # TODO: check t->left == addr
+    # t = t->right
+$addr-payload-size:skip-addr:
+    (lookup *(eax+0xc) *(eax+0x10))  # Type-tree-right Type-tree-right => eax
+    # TODO: check eax != 0
+    # if !t->is-atom? t = t->left
+    81 7/subop/compare *eax 0/imm32/false
+    {
+      75/jump-if-!= break/disp8
+      (lookup *(eax+4) *(eax+8))  # Type-tree-left Type-tree-left => eax
+    }
+    # TODO: check t->is-atom?
+    # return size(t->value)
+    (size-of-type-id *(eax+4))  # Type-tree-value => eax
+$addr-payload-size:end:
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+translate-mu-populate-stmt:  # out: (addr buffered-file), stmt: (addr stmt), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    56/push-esi
+    57/push-edi
+    # esi = stmt
+    8b/-> *(ebp+0xc) 6/r32/esi
+    # var target/edi: (addr stmt-var) = stmt->inouts[0]
+    (lookup *(esi+0xc) *(esi+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+    89/<- %edi 0/r32/eax
+    # var len/ecx: (addr stmt-var) = stmt->inouts[1]
+    (lookup *(edi+8) *(edi+0xc))  # Stmt-var-next Stmt-var-next => eax
+    89/<- %ecx 0/r32/eax
+    #
+    (emit-indent *(ebp+8) *Curr-block-depth)
+    (write-buffered *(ebp+8) "(allocate-array2 Heap ")
+    (addr-handle-array-payload-size %edi *(ebp+0x10) *(ebp+0x14))  # => eax
+    (write-int32-hex-buffered *(ebp+8) %eax)
+    (emit-subx-call-operand *(ebp+8) %ecx)
+    (emit-subx-call-operand *(ebp+8) %edi)
+    (write-buffered *(ebp+8) ")\n")
+$translate-mu-populate-stmt:end:
+    # . restore registers
+    5f/pop-to-edi
+    5e/pop-to-esi
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+translate-mu-populate-stream-stmt:  # out: (addr buffered-file), stmt: (addr stmt), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    56/push-esi
+    57/push-edi
+    # esi = stmt
+    8b/-> *(ebp+0xc) 6/r32/esi
+    # var target/edi: (addr stmt-var) = stmt->inouts[0]
+    (lookup *(esi+0xc) *(esi+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+    89/<- %edi 0/r32/eax
+    # var len/ecx: (addr stmt-var) = stmt->inouts[1]
+    (lookup *(edi+8) *(edi+0xc))  # Stmt-var-next Stmt-var-next => eax
+    89/<- %ecx 0/r32/eax
+    #
+    (emit-indent *(ebp+8) *Curr-block-depth)
+    (write-buffered *(ebp+8) "(new-stream Heap ")
+    (addr-handle-stream-payload-size %edi *(ebp+0x10) *(ebp+0x14))  # => eax
+    (write-int32-hex-buffered *(ebp+8) %eax)
+    (emit-subx-call-operand *(ebp+8) %ecx)
+    (emit-subx-call-operand *(ebp+8) %edi)
+    (write-buffered *(ebp+8) ")\n")
+$translate-mu-populate-stream-stmt:end:
+    # . restore registers
+    5f/pop-to-edi
+    5e/pop-to-esi
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+translate-mu-read-from-stream-stmt:  # out: (addr buffered-file), stmt: (addr stmt), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    56/push-esi
+    57/push-edi
+    # esi = stmt
+    8b/-> *(ebp+0xc) 6/r32/esi
+    # var stream/ecx: (addr stmt-var) = stmt->inouts[0]
+    (lookup *(esi+0xc) *(esi+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+    89/<- %ecx 0/r32/eax
+    # var target/edi: (addr stmt-var) = stmt->inouts[1]
+    (lookup *(ecx+8) *(ecx+0xc))  # Stmt-var-next Stmt-var-next => eax
+    89/<- %edi 0/r32/eax
+    #
+    (emit-indent *(ebp+8) *Curr-block-depth)
+    (write-buffered *(ebp+8) "(read-from-stream")
+    (emit-subx-call-operand *(ebp+8) %ecx)
+    (emit-subx-call-operand *(ebp+8) %edi)
+    (write-buffered *(ebp+8) Space)
+    (addr-payload-size %edi *(ebp+0x10) *(ebp+0x14))  # => eax
+    (write-int32-hex-buffered *(ebp+8) %eax)
+    (write-buffered *(ebp+8) ")\n")
+$translate-mu-read-from-stream-stmt:end:
+    # . restore registers
+    5f/pop-to-edi
+    5e/pop-to-esi
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+translate-mu-write-to-stream-stmt:  # out: (addr buffered-file), stmt: (addr stmt), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    56/push-esi
+    57/push-edi
+    # esi = stmt
+    8b/-> *(ebp+0xc) 6/r32/esi
+    # var stream/ecx: (addr stmt-var) = stmt->inouts[0]
+    (lookup *(esi+0xc) *(esi+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+    89/<- %ecx 0/r32/eax
+    # var target/edi: (addr stmt-var) = stmt->inouts[1]
+    (lookup *(ecx+8) *(ecx+0xc))  # Stmt-var-next Stmt-var-next => eax
+    89/<- %edi 0/r32/eax
+    #
+    (emit-indent *(ebp+8) *Curr-block-depth)
+    (write-buffered *(ebp+8) "(write-to-stream")
+    (emit-subx-call-operand *(ebp+8) %ecx)
+    (flush *(ebp+8))
+    (emit-subx-call-operand *(ebp+8) %edi)
+    (flush *(ebp+8))
+    (write-buffered *(ebp+8) Space)
+    (flush *(ebp+8))
+    (addr-payload-size %edi *(ebp+0x10) *(ebp+0x14))  # => eax
+    (write-int32-hex-buffered *(ebp+8) %eax)
+    (write-buffered *(ebp+8) ")\n")
+$translate-mu-write-to-stream-stmt:end:
+    # . restore registers
+    5f/pop-to-edi
+    5e/pop-to-esi
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+addr-handle-array-payload-size:  # s: (addr stmt-var), err: (addr buffered-file), ed: (addr exit-descriptor) -> result/eax: int
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # var t/eax: (addr type-tree) = s->value->type
+    8b/-> *(ebp+8) 0/r32/eax
+    (lookup *eax *(eax+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *(eax+8) *(eax+0xc))  # Var-type Var-type => eax
+    # TODO: check eax != 0
+    # TODO: check !t->is-atom?
+    # TODO: check t->left == addr
+    # t = t->right
+$addr-handle-array-payload-size:skip-addr:
+    (lookup *(eax+0xc) *(eax+0x10))  # Type-tree-right Type-tree-right => eax
+    # TODO: check eax != 0
+    # TODO: check !t->is-atom?
+    # TODO: check t->left == handle
+    # t = t->right
+$addr-handle-array-payload-size:skip-handle:
+    (lookup *(eax+0xc) *(eax+0x10))  # Type-tree-right Type-tree-right => eax
+    # TODO: check eax != 0
+    # TODO: check !t->is-atom?
+    # TODO: check t->left == array
+    # t = t->right
+$addr-handle-array-payload-size:skip-array:
+    (lookup *(eax+0xc) *(eax+0x10))  # Type-tree-right Type-tree-right => eax
+    # TODO: check eax != 0
+    # if !t->is-atom? t = t->left
+    81 7/subop/compare *eax 0/imm32/false
+    {
+      75/jump-if-!= break/disp8
+      (lookup *(eax+4) *(eax+8))  # Type-tree-left Type-tree-left => eax
+    }
+$addr-handle-array-payload-size:compute-size:
+    # TODO: check t->is-atom?
+    # return size(t->value)
+    (size-of-type-id-as-array-element *(eax+4))  # Type-tree-value => eax
+$addr-handle-array-payload-size:end:
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+addr-handle-stream-payload-size:  # s: (addr stmt-var), err: (addr buffered-file), ed: (addr exit-descriptor) -> result/eax: int
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # var t/eax: (addr type-tree) = s->value->type
+    8b/-> *(ebp+8) 0/r32/eax
+    (lookup *eax *(eax+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *(eax+8) *(eax+0xc))  # Var-type Var-type => eax
+    # TODO: check eax != 0
+    # TODO: check !t->is-atom?
+    # TODO: check t->left == addr
+    # t = t->right
+$addr-handle-stream-payload-size:skip-addr:
+    (lookup *(eax+0xc) *(eax+0x10))  # Type-tree-right Type-tree-right => eax
+    # TODO: check eax != 0
+    # TODO: check !t->is-atom?
+    # TODO: check t->left == handle
+    # t = t->right
+$addr-handle-stream-payload-size:skip-handle:
+    (lookup *(eax+0xc) *(eax+0x10))  # Type-tree-right Type-tree-right => eax
+    # TODO: check eax != 0
+    # TODO: check !t->is-atom?
+    # TODO: check t->left == stream
+    # t = t->right
+$addr-handle-stream-payload-size:skip-stream:
+    (lookup *(eax+0xc) *(eax+0x10))  # Type-tree-right Type-tree-right => eax
+    # TODO: check eax != 0
+    # if !t->is-atom? t = t->left
+    81 7/subop/compare *eax 0/imm32/false
+    {
+      75/jump-if-!= break/disp8
+      (lookup *(eax+4) *(eax+8))  # Type-tree-left Type-tree-left => eax
+    }
+$addr-handle-stream-payload-size:compute-size:
+    # TODO: check t->is-atom?
+    # return size(t->value)
+    (size-of-type-id-as-array-element *(eax+4))  # Type-tree-value => eax
+$addr-handle-stream-payload-size:end:
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+power-of-2?:  # n: int, err: (addr buffered-file), ed: (addr exit-descriptor) -> result/eax: boolean
+    # precondition: n is positive
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # eax = n
+    8b/-> *(ebp+8) 0/r32/eax
+    # if (n < 0) abort
+    3d/compare-eax-with 0/imm32
+    0f 8c/jump-if-< $power-of-2?:abort/disp32
+    # var tmp/eax: int = n-1
+    48/decrement-eax
+    # var tmp2/eax: int = n & tmp
+    23/and-> *(ebp+8) 0/r32/eax
+    # return (tmp2 == 0)
+    3d/compare-eax-and 0/imm32
+    0f 94/set-byte-if-= %al
+    81 4/subop/and %eax 0xff/imm32
+$power-of-2?:end:
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+$power-of-2?:abort:
+    (write-buffered *(ebp+0xc) "power-of-2?: negative number\n")
+    (flush *(ebp+0xc))
+    (stop *(ebp+0x10) 1)
+    # never gets here
+
+num-shift-rights:  # n: int -> result/eax: int
+    # precondition: n is a positive power of 2
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    51/push-ecx
+    # var curr/ecx: int = n
+    8b/-> *(ebp+8) 1/r32/ecx
+    # result = 0
+    b8/copy-to-eax 0/imm32
+    {
+      # if (curr <= 1) break
+      81 7/subop/compare %ecx 1/imm32
+      7e/jump-if-<= break/disp8
+      40/increment-eax
+      c1/shift 5/subop/arithmetic-right %ecx 1/imm8
+      eb/jump loop/disp8
+    }
+$num-shift-rights:end:
+    # . restore registers
+    59/pop-to-ecx
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+mu-get-offset:  # stmt: (addr stmt) -> result/eax: int
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # var second-inout/eax: (addr stmt-var) = stmt->inouts->next
+    8b/-> *(ebp+8) 0/r32/eax
+    (lookup *(eax+0xc) *(eax+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+    (lookup *(eax+8) *(eax+0xc))  # Stmt-var-next Stmt-var-next => eax
+    # var output-var/eax: (addr var) = second-inout->value
+    (lookup *eax *(eax+4))  # Stmt-var-value Stmt-var-value => eax
+#?     (write-buffered Stderr "mu-get-offset: ")
+#?     (write-int32-hex-buffered Stderr %eax)
+#?     (write-buffered Stderr " name: ")
+#?     50/push-eax
+#?     (lookup *eax *(eax+4))  # Var-name
+#?     (write-buffered Stderr %eax)
+#?     58/pop-to-eax
+#?     (write-buffered Stderr Newline)
+#?     (flush Stderr)
+    # return output-var->stack-offset
+    8b/-> *(eax+0x14) 0/r32/eax  # Var-offset
+#?     (write-buffered Stderr "=> ")
+#?     (write-int32-hex-buffered Stderr %eax)
+#?     (write-buffered Stderr Newline)
+#?     (flush Stderr)
+$emit-get-offset:end:
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+emit-subx-block:  # out: (addr buffered-file), block: (addr block), vars: (addr stack live-var), fn: (addr function), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    56/push-esi
+    # esi = block
+    8b/-> *(ebp+0xc) 6/r32/esi
+    # block->var->block-depth = *Curr-block-depth
+    (lookup *(esi+0xc) *(esi+0x10))  # Block-var Block-var => eax
+    8b/-> *Curr-block-depth 1/r32/ecx
+    89/<- *(eax+0x10) 1/r32/ecx  # Var-block-depth
+    # var stmts/eax: (addr list stmt) = lookup(block->statements)
+    (lookup *(esi+4) *(esi+8))  # Block-stmts Block-stmts => eax
+    #
+    {
+$emit-subx-block:check-empty:
+      3d/compare-eax-and 0/imm32
+      0f 84/jump-if-= break/disp32
+      (emit-indent *(ebp+8) *Curr-block-depth)
+      (write-buffered *(ebp+8) "{\n")
+      # var v/ecx: (addr var) = lookup(block->var)
+      (lookup *(esi+0xc) *(esi+0x10))  # Block-var Block-var => eax
+      89/<- %ecx 0/r32/eax
+      #
+      (lookup *ecx *(ecx+4))  # Var-name Var-name => eax
+      (write-buffered *(ebp+8) %eax)
+      (write-buffered *(ebp+8) ":loop:\n")
+      ff 0/subop/increment *Curr-block-depth
+      (push *(ebp+0x10) *(esi+0xc))  # Block-var
+      (push *(ebp+0x10) *(esi+0x10))  # Block-var
+      (push *(ebp+0x10) 0)  # false
+      # emit block->statements
+      (lookup *(esi+4) *(esi+8))  # Block-stmts Block-stmts => eax
+      (emit-subx-stmt-list *(ebp+8) %eax *(ebp+0x10) *(ebp+0x14) *(ebp+0x18) *(ebp+0x1c))
+      (pop *(ebp+0x10))  # => eax
+      (pop *(ebp+0x10))  # => eax
+      (pop *(ebp+0x10))  # => eax
+      ff 1/subop/decrement *Curr-block-depth
+      (emit-indent *(ebp+8) *Curr-block-depth)
+      (write-buffered *(ebp+8) "}\n")
+      (lookup *ecx *(ecx+4))  # Var-name Var-name => eax
+      (write-buffered *(ebp+8) %eax)
+      (write-buffered *(ebp+8) ":break:\n")
+    }
+$emit-subx-block:end:
+    # . restore registers
+    5e/pop-to-esi
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+# Primitives supported
+# See mu_instructions for a summary of this linked-list data structure.
+#
+# For each operation, put variants with hard-coded registers before flexible ones.
+#
+# Unfortunately, our restrictions on addresses require that various fields in
+# primitives be handles, which complicates these definitions.
+#   - we need to insert dummy fields all over the place for fake alloc-ids
+#   - we can't use our syntax sugar of quoted literals for string fields
+#
+# Fake alloc-ids are needed because our type definitions up top require
+# handles but it's clearer to statically allocate these long-lived objects.
+# Fake alloc-ids are perfectly safe, but they can't be reclaimed.
+#
+# Every 'object' below starts with a fake alloc-id. It may also contain other
+# fake alloc-ids for various handle fields.
+#
+# I think of objects starting with a fake alloc-id as having type 'payload'.
+# It's not really intended to be created dynamically; for that use `allocate`
+# as usual.
+#
+# Idea for a notation to simplify such definitions:
+#   _Primitive-increment-eax:  # (payload primitive)
+#     0x11/alloc-id:fake:payload
+#     0x11 @(0x11 "increment")  # name
+#     0 0                       # inouts
+#     0x11 @(0x11/payload
+#            0x11 @(0x11/payload  # List-value
+#                   0 0             # Var-name
+#                   0x11 @(0x11     # Var-type
+#                          1/is-atom
+#                          1/value 0/unused   # Type-tree-left
+#                          0 0                # Type-tree-right
+#                         )
+#                   1               # block-depth
+#                   0               # stack-offset
+#                   0x11 @(0x11 "eax")  # Var-register
+#                  )
+#            0 0)                 # List-next
+#     ...
+#     _Primitive-increment-ecx/imm32/next
+#   ...
+# Awfully complex and non-obvious. But also clearly signals there's something
+# to learn here, so may be worth trying.
+#
+# '@' is just an initial thought. Punctuation used so far in Mu: () * % # / "
+#
+# For now we'll continue to just use comments and manually ensure they stay up
+# to date.
+== data
+Primitives:  # (addr primitive)
+# - increment/decrement
+_Primitive-increment-eax:  # (addr primitive)
+    # var/eax <- increment => 40/increment-eax
+    0x11/imm32/alloc-id:fake
+    _string-increment/imm32/name
+    0/imm32/no-inouts
+    0/imm32/no-inouts
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-eax/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_40_increment_eax/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-increment-ecx/imm32/next
+_Primitive-increment-ecx:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var/ecx <- increment => 41/increment-ecx
+    0x11/imm32/alloc-id:fake
+    _string-increment/imm32/name
+    0/imm32/no-inouts
+    0/imm32/no-inouts
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-ecx/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_41_increment_ecx/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-increment-edx/imm32/next
+_Primitive-increment-edx:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var/edx <- increment => 42/increment-edx
+    0x11/imm32/alloc-id:fake
+    _string-increment/imm32/name
+    0/imm32/no-inouts
+    0/imm32/no-inouts
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-edx/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_42_increment_edx/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-increment-ebx/imm32/next
+_Primitive-increment-ebx:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var/ebx <- increment => 43/increment-ebx
+    0x11/imm32/alloc-id:fake
+    _string-increment/imm32/name
+    0/imm32/no-inouts
+    0/imm32/no-inouts
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-ebx/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_43_increment_ebx/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-increment-esi/imm32/next
+_Primitive-increment-esi:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var/esi <- increment => 46/increment-esi
+    0x11/imm32/alloc-id:fake
+    _string-increment/imm32/name
+    0/imm32/no-inouts
+    0/imm32/no-inouts
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-esi/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_46_increment_esi/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-increment-edi/imm32/next
+_Primitive-increment-edi:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var/edi <- increment => 47/increment-edi
+    0x11/imm32/alloc-id:fake
+    _string-increment/imm32/name
+    0/imm32/no-inouts
+    0/imm32/no-inouts
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-edi/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_47_increment_edi/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-decrement-eax/imm32/next
+_Primitive-decrement-eax:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var/eax <- decrement => 48/decrement-eax
+    0x11/imm32/alloc-id:fake
+    _string-decrement/imm32/name
+    0/imm32/no-inouts
+    0/imm32/no-inouts
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-eax/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_48_decrement_eax/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-decrement-ecx/imm32/next
+_Primitive-decrement-ecx:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var/ecx <- decrement => 49/decrement-ecx
+    0x11/imm32/alloc-id:fake
+    _string-decrement/imm32/name
+    0/imm32/no-inouts
+    0/imm32/no-inouts
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-ecx/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_49_decrement_ecx/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-decrement-edx/imm32/next
+_Primitive-decrement-edx:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var/edx <- decrement => 4a/decrement-edx
+    0x11/imm32/alloc-id:fake
+    _string-decrement/imm32/name
+    0/imm32/no-inouts
+    0/imm32/no-inouts
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-edx/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_4a_decrement_edx/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-decrement-ebx/imm32/next
+_Primitive-decrement-ebx:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var/ebx <- decrement => 4b/decrement-ebx
+    0x11/imm32/alloc-id:fake
+    _string-decrement/imm32/name
+    0/imm32/no-inouts
+    0/imm32/no-inouts
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-ebx/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_4b_decrement_ebx/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-decrement-esi/imm32/next
+_Primitive-decrement-esi:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var/esi <- decrement => 4e/decrement-esi
+    0x11/imm32/alloc-id:fake
+    _string-decrement/imm32/name
+    0/imm32/no-inouts
+    0/imm32/no-inouts
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-esi/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_4e_decrement_esi/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-decrement-edi/imm32/next
+_Primitive-decrement-edi:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var/edi <- decrement => 4f/decrement-edi
+    0x11/imm32/alloc-id:fake
+    _string-decrement/imm32/name
+    0/imm32/no-inouts
+    0/imm32/no-inouts
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-edi/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_4f_decrement_edi/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-increment-mem/imm32/next
+_Primitive-increment-mem:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # increment var => ff 0/subop/increment *(ebp+__)
+    0x11/imm32/alloc-id:fake
+    _string-increment/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-mem/imm32/inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_ff_subop_increment/imm32/subx-name
+    1/imm32/rm32-is-first-inout
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-increment-reg/imm32/next
+_Primitive-increment-reg:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var/reg <- increment => ff 0/subop/increment %__
+    0x11/imm32/alloc-id:fake
+    _string-increment/imm32/name
+    0/imm32/no-inouts
+    0/imm32/no-inouts
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-some-register/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_ff_subop_increment/imm32/subx-name
+    3/imm32/rm32-is-first-output
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-decrement-mem/imm32/next
+_Primitive-decrement-mem:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # decrement var => ff 1/subop/decrement *(ebp+__)
+    0x11/imm32/alloc-id:fake
+    _string-decrement/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-mem/imm32/inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_ff_subop_decrement/imm32/subx-name
+    1/imm32/rm32-is-first-inout
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-decrement-reg/imm32/next
+_Primitive-decrement-reg:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var/reg <- decrement => ff 1/subop/decrement %__
+    0x11/imm32/alloc-id:fake
+    _string-decrement/imm32/name
+    0/imm32/no-inouts
+    0/imm32/no-inouts
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-some-register/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_ff_subop_decrement/imm32/subx-name
+    3/imm32/rm32-is-first-output
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-add-to-eax/imm32/next
+# - add
+_Primitive-add-to-eax:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var/eax <- add lit => 05/add-to-eax lit/imm32
+    0x11/imm32/alloc-id:fake
+    _string-add/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-lit-var/imm32/inouts
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-eax/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_05_add_to_eax/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    1/imm32/imm32-is-first-inout
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-add-reg-to-reg/imm32/next
+_Primitive-add-reg-to-reg:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var1/reg <- add var2/reg => 01/add-to var1/rm32 var2/r32
+    0x11/imm32/alloc-id:fake
+    _string-add/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-some-register/imm32/inouts
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-some-register/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_01_add_to/imm32/subx-name
+    3/imm32/rm32-is-first-output
+    1/imm32/r32-is-first-inout
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-add-reg-to-mem/imm32/next
+_Primitive-add-reg-to-mem:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # add-to var1 var2/reg => 01/add-to var1 var2/r32
+    0x11/imm32/alloc-id:fake
+    _string-add-to/imm32/name
+    0x11/imm32/alloc-id:fake
+    Two-args-int-stack-int-reg/imm32/inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_01_add_to/imm32/subx-name
+    1/imm32/rm32-is-first-inout
+    2/imm32/r32-is-second-inout
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-add-mem-to-reg/imm32/next
+_Primitive-add-mem-to-reg:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var1/reg <- add var2 => 03/add var2/rm32 var1/r32
+    0x11/imm32/alloc-id:fake
+    _string-add/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-mem/imm32/inouts
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-some-register/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_03_add/imm32/subx-name
+    1/imm32/rm32-is-first-inout
+    3/imm32/r32-is-first-output
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-add-lit-to-reg/imm32/next
+_Primitive-add-lit-to-reg:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var1/reg <- add lit => 81 0/subop/add var1/rm32 lit/imm32
+    0x11/imm32/alloc-id:fake
+    _string-add/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-lit-var/imm32/inouts
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-some-register/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_81_subop_add/imm32/subx-name
+    3/imm32/rm32-is-first-output
+    0/imm32/no-r32
+    1/imm32/imm32-is-first-inout
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-add-lit-to-mem/imm32/next
+_Primitive-add-lit-to-mem:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # add-to var1, lit => 81 0/subop/add var1/rm32 lit/imm32
+    0x11/imm32/alloc-id:fake
+    _string-add-to/imm32/name
+    0x11/imm32/alloc-id:fake
+    Int-var-and-literal/imm32/inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_81_subop_add/imm32/subx-name
+    1/imm32/rm32-is-first-inout
+    0/imm32/no-r32
+    2/imm32/imm32-is-second-inout
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-subtract-from-eax/imm32/next
+# - subtract
+_Primitive-subtract-from-eax:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var/eax <- subtract lit => 2d/subtract-from-eax lit/imm32
+    0x11/imm32/alloc-id:fake
+    _string-subtract/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-lit-var/imm32/inouts
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-eax/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_2d_subtract_from_eax/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    1/imm32/imm32-is-first-inout
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-subtract-reg-from-reg/imm32/next
+_Primitive-subtract-reg-from-reg:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var1/reg <- subtract var2/reg => 29/subtract-from var1/rm32 var2/r32
+    0x11/imm32/alloc-id:fake
+    _string-subtract/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-some-register/imm32/inouts
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-some-register/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_29_subtract_from/imm32/subx-name
+    3/imm32/rm32-is-first-output
+    1/imm32/r32-is-first-inout
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-subtract-reg-from-mem/imm32/next
+_Primitive-subtract-reg-from-mem:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # subtract-from var1 var2/reg => 29/subtract-from var1 var2/r32
+    0x11/imm32/alloc-id:fake
+    _string-subtract-from/imm32/name
+    0x11/imm32/alloc-id:fake
+    Two-args-int-stack-int-reg/imm32/inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_29_subtract_from/imm32/subx-name
+    1/imm32/rm32-is-first-inout
+    2/imm32/r32-is-second-inout
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-subtract-mem-from-reg/imm32/next
+_Primitive-subtract-mem-from-reg:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var1/reg <- subtract var2 => 2b/subtract var2/rm32 var1/r32
+    0x11/imm32/alloc-id:fake
+    _string-subtract/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-mem/imm32/inouts
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-some-register/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_2b_subtract/imm32/subx-name
+    1/imm32/rm32-is-first-inout
+    3/imm32/r32-is-first-output
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-subtract-lit-from-reg/imm32/next
+_Primitive-subtract-lit-from-reg:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var1/reg <- subtract lit => 81 5/subop/subtract var1/rm32 lit/imm32
+    0x11/imm32/alloc-id:fake
+    _string-subtract/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-lit-var/imm32/inouts
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-some-register/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_81_subop_subtract/imm32/subx-name
+    3/imm32/rm32-is-first-output
+    0/imm32/no-r32
+    1/imm32/imm32-is-first-inout
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-subtract-lit-from-mem/imm32/next
+_Primitive-subtract-lit-from-mem:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # subtract-from var1, lit => 81 5/subop/subtract var1/rm32 lit/imm32
+    0x11/imm32/alloc-id:fake
+    _string-subtract-from/imm32/name
+    0x11/imm32/alloc-id:fake
+    Int-var-and-literal/imm32/inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_81_subop_subtract/imm32/subx-name
+    1/imm32/rm32-is-first-inout
+    0/imm32/no-r32
+    2/imm32/imm32-is-second-inout
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-and-with-eax/imm32/next
+# - and
+_Primitive-and-with-eax:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var/eax <- and lit => 25/and-with-eax lit/imm32
+    0x11/imm32/alloc-id:fake
+    _string-and/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-lit-var/imm32/inouts
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-eax/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_25_and_with_eax/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    1/imm32/imm32-is-first-inout
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-and-reg-with-reg/imm32/next
+_Primitive-and-reg-with-reg:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var1/reg <- and var2/reg => 21/and-with var1/rm32 var2/r32
+    0x11/imm32/alloc-id:fake
+    _string-and/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-some-register/imm32/inouts
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-some-register/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_21_and_with/imm32/subx-name
+    3/imm32/rm32-is-first-output
+    1/imm32/r32-is-first-inout
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-and-reg-with-mem/imm32/next
+_Primitive-and-reg-with-mem:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # and-with var1 var2/reg => 21/and-with var1 var2/r32
+    0x11/imm32/alloc-id:fake
+    _string-and-with/imm32/name
+    0x11/imm32/alloc-id:fake
+    Two-args-int-stack-int-reg/imm32/inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_21_and_with/imm32/subx-name
+    1/imm32/rm32-is-first-inout
+    2/imm32/r32-is-second-inout
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-and-mem-with-reg/imm32/next
+_Primitive-and-mem-with-reg:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var1/reg <- and var2 => 23/and var2/rm32 var1/r32
+    0x11/imm32/alloc-id:fake
+    _string-and/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-mem/imm32/inouts
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-some-register/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_23_and/imm32/subx-name
+    1/imm32/rm32-is-first-inout
+    3/imm32/r32-is-first-output
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-and-lit-with-reg/imm32/next
+_Primitive-and-lit-with-reg:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var1/reg <- and lit => 81 4/subop/and var1/rm32 lit/imm32
+    0x11/imm32/alloc-id:fake
+    _string-and/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-lit-var/imm32/inouts
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-some-register/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_81_subop_and/imm32/subx-name
+    3/imm32/rm32-is-first-output
+    0/imm32/no-r32
+    1/imm32/imm32-is-first-inout
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-and-lit-with-mem/imm32/next
+_Primitive-and-lit-with-mem:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # and-with var1, lit => 81 4/subop/and var1/rm32 lit/imm32
+    0x11/imm32/alloc-id:fake
+    _string-and-with/imm32/name
+    0x11/imm32/alloc-id:fake
+    Int-var-and-literal/imm32/inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_81_subop_and/imm32/subx-name
+    1/imm32/rm32-is-first-inout
+    0/imm32/no-r32
+    2/imm32/imm32-is-second-inout
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-or-with-eax/imm32/next
+# - or
+_Primitive-or-with-eax:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var/eax <- or lit => 0d/or-with-eax lit/imm32
+    0x11/imm32/alloc-id:fake
+    _string-or/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-lit-var/imm32/inouts
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-eax/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_0d_or_with_eax/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    1/imm32/imm32-is-first-inout
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-or-reg-with-reg/imm32/next
+_Primitive-or-reg-with-reg:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var1/reg <- or var2/reg => 09/or-with var1/rm32 var2/r32
+    0x11/imm32/alloc-id:fake
+    _string-or/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-some-register/imm32/inouts
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-some-register/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_09_or_with/imm32/subx-name
+    3/imm32/rm32-is-first-output
+    1/imm32/r32-is-first-inout
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-or-reg-with-mem/imm32/next
+_Primitive-or-reg-with-mem:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # or-with var1 var2/reg => 09/or-with var1 var2/r32
+    0x11/imm32/alloc-id:fake
+    _string-or-with/imm32/name
+    0x11/imm32/alloc-id:fake
+    Two-args-int-stack-int-reg/imm32/inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_09_or_with/imm32/subx-name
+    1/imm32/rm32-is-first-inout
+    2/imm32/r32-is-second-inout
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-or-mem-with-reg/imm32/next
+_Primitive-or-mem-with-reg:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var1/reg <- or var2 => 0b/or var2/rm32 var1/r32
+    0x11/imm32/alloc-id:fake
+    _string-or/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-mem/imm32/inouts
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-some-register/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_0b_or/imm32/subx-name
+    1/imm32/rm32-is-first-inout
+    3/imm32/r32-is-first-output
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-or-lit-with-reg/imm32/next
+_Primitive-or-lit-with-reg:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var1/reg <- or lit => 81 1/subop/or var1/rm32 lit/imm32
+    0x11/imm32/alloc-id:fake
+    _string-or/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-lit-var/imm32/inouts
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-some-register/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_81_subop_or/imm32/subx-name
+    3/imm32/rm32-is-first-output
+    0/imm32/no-r32
+    1/imm32/imm32-is-first-inout
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-or-lit-with-mem/imm32/next
+_Primitive-or-lit-with-mem:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # or-with var1, lit => 81 1/subop/or var1/rm32 lit/imm32
+    0x11/imm32/alloc-id:fake
+    _string-or-with/imm32/name
+    0x11/imm32/alloc-id:fake
+    Int-var-and-literal/imm32/inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_81_subop_or/imm32/subx-name
+    1/imm32/rm32-is-first-inout
+    0/imm32/no-r32
+    2/imm32/imm32-is-second-inout
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-xor-with-eax/imm32/next
+# - xor
+_Primitive-xor-with-eax:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var/eax <- xor lit => 35/xor-with-eax lit/imm32
+    0x11/imm32/alloc-id:fake
+    _string-xor/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-lit-var/imm32/inouts
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-eax/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_35_xor_with_eax/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    1/imm32/imm32-is-first-inout
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-xor-reg-with-reg/imm32/next
+_Primitive-xor-reg-with-reg:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var1/reg <- xor var2/reg => 31/xor-with var1/rm32 var2/r32
+    0x11/imm32/alloc-id:fake
+    _string-xor/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-some-register/imm32/inouts
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-some-register/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_31_xor_with/imm32/subx-name
+    3/imm32/rm32-is-first-output
+    1/imm32/r32-is-first-inout
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-xor-reg-with-mem/imm32/next
+_Primitive-xor-reg-with-mem:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # xor-with var1 var2/reg => 31/xor-with var1 var2/r32
+    0x11/imm32/alloc-id:fake
+    _string-xor-with/imm32/name
+    0x11/imm32/alloc-id:fake
+    Two-args-int-stack-int-reg/imm32/inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_31_xor_with/imm32/subx-name
+    1/imm32/rm32-is-first-inout
+    2/imm32/r32-is-second-inout
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-xor-mem-with-reg/imm32/next
+_Primitive-xor-mem-with-reg:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var1/reg <- xor var2 => 33/xor var2/rm32 var1/r32
+    0x11/imm32/alloc-id:fake
+    _string-xor/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-mem/imm32/inouts
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-some-register/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_33_xor/imm32/subx-name
+    1/imm32/rm32-is-first-inout
+    3/imm32/r32-is-first-output
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-xor-lit-with-reg/imm32/next
+_Primitive-xor-lit-with-reg:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var1/reg <- xor lit => 81 6/subop/xor var1/rm32 lit/imm32
+    0x11/imm32/alloc-id:fake
+    _string-xor/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-lit-var/imm32/inouts
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-some-register/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_81_subop_xor/imm32/subx-name
+    3/imm32/rm32-is-first-output
+    0/imm32/no-r32
+    1/imm32/imm32-is-first-inout
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-xor-lit-with-mem/imm32/next
+_Primitive-xor-lit-with-mem:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # xor-with var1, lit => 81 6/subop/xor var1/rm32 lit/imm32
+    0x11/imm32/alloc-id:fake
+    _string-xor-with/imm32/name
+    0x11/imm32/alloc-id:fake
+    Int-var-and-literal/imm32/inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_81_subop_xor/imm32/subx-name
+    1/imm32/rm32-is-first-inout
+    0/imm32/no-r32
+    2/imm32/imm32-is-second-inout
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-shift-reg-left-by-lit/imm32/next
+_Primitive-shift-reg-left-by-lit:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var1/reg <- shift-left lit => c1/shift 4/subop/left var1/rm32 lit/imm32
+    0x11/imm32/alloc-id:fake
+    _string-shift-left/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-lit-var/imm32/inouts
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-some-register/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_c1_subop_shift_left/imm32/subx-name
+    3/imm32/rm32-is-first-output
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    1/imm32/imm8-is-first-inout
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-shift-reg-right-by-lit/imm32/next
+_Primitive-shift-reg-right-by-lit:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var1/reg <- shift-right lit => c1/shift 5/subop/right var1/rm32 lit/imm32
+    0x11/imm32/alloc-id:fake
+    _string-shift-right/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-lit-var/imm32/inouts
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-some-register/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_c1_subop_shift_right_padding_zeroes/imm32/subx-name
+    3/imm32/rm32-is-first-output
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    1/imm32/imm8-is-first-inout
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-shift-reg-right-signed-by-lit/imm32/next
+_Primitive-shift-reg-right-signed-by-lit:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var1/reg <- shift-right-signed lit => c1/shift 7/subop/right-preserving-sign var1/rm32 lit/imm32
+    0x11/imm32/alloc-id:fake
+    _string-shift-right-signed/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-lit-var/imm32/inouts
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-some-register/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_c1_subop_shift_right_preserving_sign/imm32/subx-name
+    3/imm32/rm32-is-first-output
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    1/imm32/imm8-is-first-inout
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-shift-mem-left-by-lit/imm32/next
+_Primitive-shift-mem-left-by-lit:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # shift-left var1, lit => c1/shift 4/subop/left var1/rm32 lit/imm32
+    0x11/imm32/alloc-id:fake
+    _string-shift-left/imm32/name
+    0x11/imm32/alloc-id:fake
+    Int-var-and-literal/imm32/inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_c1_subop_shift_left/imm32/subx-name
+    1/imm32/rm32-is-first-inout
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    2/imm32/imm8-is-second-inout
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-shift-mem-right-by-lit/imm32/next
+_Primitive-shift-mem-right-by-lit:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # shift-right var1, lit => c1/shift 5/subop/right var1/rm32 lit/imm32
+    0x11/imm32/alloc-id:fake
+    _string-shift-right/imm32/name
+    0x11/imm32/alloc-id:fake
+    Int-var-and-literal/imm32/inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_c1_subop_shift_right_padding_zeroes/imm32/subx-name
+    1/imm32/rm32-is-first-inout
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    2/imm32/imm8-is-second-inout
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-shift-mem-right-signed-by-lit/imm32/next
+_Primitive-shift-mem-right-signed-by-lit:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # shift-right-signed var1, lit => c1/shift 7/subop/right-preserving-sign var1/rm32 lit/imm32
+    0x11/imm32/alloc-id:fake
+    _string-shift-right-signed/imm32/name
+    0x11/imm32/alloc-id:fake
+    Int-var-and-literal/imm32/inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_c1_subop_shift_right_preserving_sign/imm32/subx-name
+    1/imm32/rm32-is-first-inout
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    2/imm32/imm8-is-second-inout
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-copy-to-eax/imm32/next
+# - copy
+_Primitive-copy-to-eax:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var/eax <- copy lit => b8/copy-to-eax lit/imm32
+    0x11/imm32/alloc-id:fake
+    _string-copy/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-lit-var/imm32/inouts
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-eax/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_b8_copy_to_eax/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    1/imm32/imm32-is-first-inout
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-copy-to-ecx/imm32/next
+_Primitive-copy-to-ecx:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var/ecx <- copy lit => b9/copy-to-ecx lit/imm32
+    0x11/imm32/alloc-id:fake
+    _string-copy/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-lit-var/imm32/inouts
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-ecx/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_b9_copy_to_ecx/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    1/imm32/imm32-is-first-inout
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-copy-to-edx/imm32/next
+_Primitive-copy-to-edx:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var/edx <- copy lit => ba/copy-to-edx lit/imm32
+    0x11/imm32/alloc-id:fake
+    _string-copy/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-lit-var/imm32/inouts
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-edx/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_ba_copy_to_edx/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    1/imm32/imm32-is-first-inout
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-copy-to-ebx/imm32/next
+_Primitive-copy-to-ebx:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var/ebx <- copy lit => bb/copy-to-ebx lit/imm32
+    0x11/imm32/alloc-id:fake
+    _string-copy/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-lit-var/imm32/inouts
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-ebx/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_bb_copy_to_ebx/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    1/imm32/imm32-is-first-inout
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-copy-to-esi/imm32/next
+_Primitive-copy-to-esi:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var/esi <- copy lit => be/copy-to-esi lit/imm32
+    0x11/imm32/alloc-id:fake
+    _string-copy/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-lit-var/imm32/inouts
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-esi/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_be_copy_to_esi/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    1/imm32/imm32-is-first-inout
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-copy-to-edi/imm32/next
+_Primitive-copy-to-edi:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var/edi <- copy lit => bf/copy-to-edi lit/imm32
+    0x11/imm32/alloc-id:fake
+    _string-copy/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-lit-var/imm32/inouts
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-edi/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_bf_copy_to_edi/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    1/imm32/imm32-is-first-inout
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-copy-reg-to-reg/imm32/next
+_Primitive-copy-reg-to-reg:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var1/reg <- copy var2/reg => 89/<- var1/rm32 var2/r32
+    0x11/imm32/alloc-id:fake
+    _string-copy/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-some-register/imm32/inouts
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-some-register/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_89_<-/imm32/subx-name
+    3/imm32/rm32-is-first-output
+    1/imm32/r32-is-first-inout
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-copy-reg-to-mem/imm32/next
+_Primitive-copy-reg-to-mem:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # copy-to var1 var2/reg => 89/<- var1 var2/r32
+    0x11/imm32/alloc-id:fake
+    _string-copy-to/imm32/name
+    0x11/imm32/alloc-id:fake
+    Two-args-int-stack-int-reg/imm32/inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_89_<-/imm32/subx-name
+    1/imm32/rm32-is-first-inout
+    2/imm32/r32-is-second-inout
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-copy-mem-to-reg/imm32/next
+_Primitive-copy-mem-to-reg:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var1/reg <- copy var2 => 8b/-> var2/rm32 var1/r32
+    0x11/imm32/alloc-id:fake
+    _string-copy/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-mem/imm32/inouts
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-some-register/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_8b_->/imm32/subx-name
+    1/imm32/rm32-is-first-inout
+    3/imm32/r32-is-first-output
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-copy-lit-to-reg/imm32/next
+_Primitive-copy-lit-to-reg:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var1/reg <- copy lit => c7 0/subop/copy var1/rm32 lit/imm32
+    0x11/imm32/alloc-id:fake
+    _string-copy/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-lit-var/imm32/inouts
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-some-register/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_c7_subop_copy/imm32/subx-name
+    3/imm32/rm32-is-first-output
+    0/imm32/no-r32
+    1/imm32/imm32-is-first-inout
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-copy-lit-to-mem/imm32/next
+_Primitive-copy-lit-to-mem:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # copy-to var1, lit => c7 0/subop/copy var1/rm32 lit/imm32
+    0x11/imm32/alloc-id:fake
+    _string-copy-to/imm32/name
+    0x11/imm32/alloc-id:fake
+    Int-var-and-literal/imm32/inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_c7_subop_copy/imm32/subx-name
+    1/imm32/rm32-is-first-inout
+    0/imm32/no-r32
+    2/imm32/imm32-is-second-inout
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-copy-byte-from-reg/imm32/next
+# - copy byte
+_Primitive-copy-byte-from-reg:
+    0x11/imm32/alloc-id:fake:payload
+    # var/reg <- copy-byte var2/reg2 => 8a/byte-> %var2 var/r32
+    0x11/imm32/alloc-id:fake
+    _string-copy-byte/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-byte-var-in-some-register/imm32/inouts
+    0x11/imm32/alloc-id:fake
+    Single-byte-var-in-some-register/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_8a_copy_byte/imm32/subx-name
+    1/imm32/rm32-is-first-inout
+    3/imm32/r32-is-first-output
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-copy-byte-from-mem/imm32/next
+_Primitive-copy-byte-from-mem:
+    0x11/imm32/alloc-id:fake:payload
+    # var/reg <- copy-byte *var2/reg2 => 8a/byte-> *var2 var/r32
+    0x11/imm32/alloc-id:fake
+    _string-copy-byte/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-byte-var-in-mem/imm32/inouts
+    0x11/imm32/alloc-id:fake
+    Single-byte-var-in-some-register/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_8a_copy_byte/imm32/subx-name
+    1/imm32/rm32-is-first-inout
+    3/imm32/r32-is-first-output
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-copy-byte-to-mem/imm32/next
+_Primitive-copy-byte-to-mem:
+    0x11/imm32/alloc-id:fake:payload
+    # copy-byte-to *var1/reg1, var2/reg2 => 88/byte<- *reg1 reg2/r32
+    0x11/imm32/alloc-id:fake
+    _string-copy-byte-to/imm32/name
+    0x11/imm32/alloc-id:fake
+    Two-args-byte-stack-byte-reg/imm32/inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_88_copy_byte/imm32/subx-name
+    1/imm32/rm32-is-first-inout
+    2/imm32/r32-is-second-inout
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-address/imm32/next
+# - address
+_Primitive-address:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var1/reg <- address var2 => 8d/copy-address var2/rm32 var1/r32
+    0x11/imm32/alloc-id:fake
+    _string-address/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-mem/imm32/inouts
+    0x11/imm32/alloc-id:fake
+    Single-addr-var-in-some-register/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_8d_copy_address/imm32/subx-name
+    1/imm32/rm32-is-first-inout
+    3/imm32/r32-is-first-output
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-compare-reg-with-reg/imm32/next
+# - compare
+_Primitive-compare-reg-with-reg:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # compare var1/reg1 var2/reg2 => 39/compare var1/rm32 var2/r32
+    0x11/imm32/alloc-id:fake
+    _string-compare/imm32/name
+    0x11/imm32/alloc-id:fake
+    Two-int-args-in-regs/imm32/inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_39_compare->/imm32/subx-name
+    1/imm32/rm32-is-first-inout
+    2/imm32/r32-is-second-inout
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-compare-mem-with-reg/imm32/next
+_Primitive-compare-mem-with-reg:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # compare var1 var2/reg => 39/compare var1/rm32 var2/r32
+    0x11/imm32/alloc-id:fake
+    _string-compare/imm32/name
+    0x11/imm32/alloc-id:fake
+    Two-args-int-stack-int-reg/imm32/inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_39_compare->/imm32/subx-name
+    1/imm32/rm32-is-first-inout
+    2/imm32/r32-is-second-inout
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-compare-reg-with-mem/imm32/next
+_Primitive-compare-reg-with-mem:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # compare var1/reg var2 => 3b/compare<- var2/rm32 var1/r32
+    0x11/imm32/alloc-id:fake
+    _string-compare/imm32/name
+    0x11/imm32/alloc-id:fake
+    Two-args-int-reg-int-stack/imm32/inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_3b_compare<-/imm32/subx-name
+    2/imm32/rm32-is-second-inout
+    1/imm32/r32-is-first-inout
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-compare-eax-with-literal/imm32/next
+_Primitive-compare-eax-with-literal:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # compare var1/eax n => 3d/compare-eax-with n/imm32
+    0x11/imm32/alloc-id:fake
+    _string-compare/imm32/name
+    0x11/imm32/alloc-id:fake
+    Two-args-int-eax-int-literal/imm32/inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_3d_compare_eax_with/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    2/imm32/imm32-is-second-inout
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-compare-reg-with-literal/imm32/next
+_Primitive-compare-reg-with-literal:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # compare var1/reg n => 81 7/subop/compare %reg n/imm32
+    0x11/imm32/alloc-id:fake
+    _string-compare/imm32/name
+    0x11/imm32/alloc-id:fake
+    Int-var-in-register-and-literal/imm32/inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_81_subop_compare/imm32/subx-name
+    1/imm32/rm32-is-first-inout
+    0/imm32/no-r32
+    2/imm32/imm32-is-second-inout
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-compare-mem-with-literal/imm32/next
+_Primitive-compare-mem-with-literal:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # compare var1 n => 81 7/subop/compare *(ebp+___) n/imm32
+    0x11/imm32/alloc-id:fake
+    _string-compare/imm32/name
+    0x11/imm32/alloc-id:fake
+    Int-var-and-literal/imm32/inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_81_subop_compare/imm32/subx-name
+    1/imm32/rm32-is-first-inout
+    0/imm32/no-r32
+    2/imm32/imm32-is-second-inout
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-negate-reg/imm32/next
+# - negate
+_Primitive-negate-reg:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var1/reg <- negate => f7 3/subop/negate var1/rm32
+    0x11/imm32/alloc-id:fake
+    _string-negate/imm32/name
+    0/imm32/no-inouts
+    0/imm32/no-inouts
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-some-register/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_f7_subop_negate/imm32/subx-name
+    3/imm32/rm32-is-first-output
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-negate-mem/imm32/next
+_Primitive-negate-mem:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # negate var1 => f7 3/subop/negate var1/rm32
+    0x11/imm32/alloc-id:fake
+    _string-negate/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-mem/imm32/inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_f7_subop_negate/imm32/subx-name
+    1/imm32/rm32-is-first-inout
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-multiply-reg-by-reg/imm32/next
+# - multiply
+_Primitive-multiply-reg-by-reg:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var1/reg <- multiply var2 => 0f af/multiply var2/rm32 var1/r32
+    0x11/imm32/alloc-id:fake
+    _string-multiply/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-some-register/imm32/inouts
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-some-register/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_0f_af_multiply/imm32/subx-name
+    1/imm32/rm32-is-first-inout
+    3/imm32/r32-is-first-output
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-multiply-reg-by-mem/imm32/next
+_Primitive-multiply-reg-by-mem:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var1/reg <- multiply var2 => 0f af/multiply var2/rm32 var1/r32
+    0x11/imm32/alloc-id:fake
+    _string-multiply/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-mem/imm32/inouts
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-some-register/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_0f_af_multiply/imm32/subx-name
+    1/imm32/rm32-is-first-inout
+    3/imm32/r32-is-first-output
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-convert-mem-to-xreg/imm32/next
+# - convert int to floating point
+_Primitive-convert-mem-to-xreg:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var1/xreg <- convert var2 => f3 0f 2a/convert-to-float var2/rm32 var1/x32
+    0x11/imm32/alloc-id:fake
+    _string-convert/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-mem/imm32/inouts
+    0x11/imm32/alloc-id:fake
+    Single-float-var-in-some-register/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_f3_0f_2a_convert_to_float/imm32/subx-name
+    1/imm32/rm32-is-first-inout
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    3/imm32/x32-is-first-output
+    0x11/imm32/alloc-id:fake
+    _Primitive-convert-reg-to-xreg/imm32/next
+_Primitive-convert-reg-to-xreg:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var1/xreg <- convert var2/reg => f3 0f 2a/convert-to-float var2/rm32 var1/x32
+    0x11/imm32/alloc-id:fake
+    _string-convert/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-some-register/imm32/inouts
+    0x11/imm32/alloc-id:fake
+    Single-float-var-in-some-register/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_f3_0f_2a_convert_to_float/imm32/subx-name
+    1/imm32/rm32-is-first-inout
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    3/imm32/x32-is-first-output
+    0x11/imm32/alloc-id:fake
+    _Primitive-convert-xmem-to-reg/imm32/next
+# - convert floating point to int
+_Primitive-convert-xmem-to-reg:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var1/reg <- convert var2 => f3 0f 2d/convert-to-int var2/xm32 var1/r32
+    0x11/imm32/alloc-id:fake
+    _string-convert/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-float-var-in-mem/imm32/inouts
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-some-register/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_f3_0f_2d_convert_to_int/imm32/subx-name
+    0/imm32/no-rm32
+    3/imm32/r32-is-first-output
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    1/imm32/xm32-is-first-inout
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-convert-xreg-to-reg/imm32/next
+_Primitive-convert-xreg-to-reg:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var1/reg <- convert var2/xreg => f3 0f 2d/convert-to-int var2/xm32 var1/r32
+    0x11/imm32/alloc-id:fake
+    _string-convert/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-float-var-in-some-register/imm32/inouts
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-some-register/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_f3_0f_2d_convert_to_int/imm32/subx-name
+    0/imm32/no-rm32
+    3/imm32/r32-is-first-output
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    1/imm32/xm32-is-first-inout
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-truncate-xmem-to-reg/imm32/next
+_Primitive-truncate-xmem-to-reg:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var1/reg <- truncate var2 => f3 0f 2c/truncate-to-int var2/xm32 var1/r32
+    0x11/imm32/alloc-id:fake
+    _string-truncate/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-float-var-in-mem/imm32/inouts
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-some-register/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_f3_0f_2c_truncate_to_int/imm32/subx-name
+    0/imm32/no-rm32
+    3/imm32/r32-is-first-output
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    1/imm32/xm32-is-first-inout
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-truncate-xreg-to-reg/imm32/next
+_Primitive-truncate-xreg-to-reg:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var1/reg <- truncate var2/xreg => f3 0f 2c/truncate-to-int var2/xm32 var1/r32
+    0x11/imm32/alloc-id:fake
+    _string-truncate/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-float-var-in-some-register/imm32/inouts
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-some-register/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_f3_0f_2c_truncate_to_int/imm32/subx-name
+    0/imm32/no-rm32
+    3/imm32/r32-is-first-output
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    1/imm32/xm32-is-first-inout
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-reinterpret-xmem-as-reg/imm32/next
+# - reinterpret bytes (just for debugging)
+_Primitive-reinterpret-xmem-as-reg:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var1/reg <- reinterpret var2 => 8b/-> var2/xm32 var1/r32
+    0x11/imm32/alloc-id:fake
+    _string-reinterpret/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-float-var-in-mem/imm32/inouts
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-some-register/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_8b_->/imm32/subx-name
+    0/imm32/no-rm32
+    3/imm32/r32-is-first-output
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    1/imm32/xm32-is-first-inout
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-reinterpret-mem-as-xreg/imm32/next
+_Primitive-reinterpret-mem-as-xreg:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var1/xreg <- reinterpret var2 => f3 0f 10/-> var2/rm32 var1/x32
+    0x11/imm32/alloc-id:fake
+    _string-reinterpret/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-mem/imm32/inouts
+    0x11/imm32/alloc-id:fake
+    Single-float-var-in-some-register/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_f3_0f_10_copy/imm32/subx-name
+    1/imm32/rm32-is-first-inout
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    3/imm32/x32-is-first-output
+    0x11/imm32/alloc-id:fake
+    _Primitive-copy-xreg-to-xreg/imm32/next
+# - floating-point copy
+_Primitive-copy-xreg-to-xreg:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var1/xreg <- copy var2/xreg => f3 0f 11/<- var1/xm32 var2/x32
+    0x11/imm32/alloc-id:fake
+    _string-copy/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-float-var-in-some-register/imm32/inouts
+    0x11/imm32/alloc-id:fake
+    Single-float-var-in-some-register/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_f3_0f_11_copy/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    3/imm32/xm32-is-first-output
+    1/imm32/x32-is-first-inout
+    0x11/imm32/alloc-id:fake
+    _Primitive-copy-xreg-to-mem/imm32/next
+_Primitive-copy-xreg-to-mem:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # copy-to var1 var2/xreg => f3 0f 11/<- var1 var2/x32
+    0x11/imm32/alloc-id:fake
+    _string-copy-to/imm32/name
+    0x11/imm32/alloc-id:fake
+    Two-args-float-stack-float-reg/imm32/inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_f3_0f_11_copy/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    1/imm32/xm32-is-first-inout
+    2/imm32/x32-is-second-inout
+    0x11/imm32/alloc-id:fake
+    _Primitive-copy-mem-to-xreg/imm32/next
+_Primitive-copy-mem-to-xreg:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var1/xreg <- copy var2 => f3 0f 10/-> var2/rm32 var1/x32
+    0x11/imm32/alloc-id:fake
+    _string-copy/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-float-var-in-mem/imm32/inouts
+    0x11/imm32/alloc-id:fake
+    Single-float-var-in-some-register/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_f3_0f_10_copy/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    1/imm32/xm32-is-first-inout
+    3/imm32/x32-is-first-output
+    0x11/imm32/alloc-id:fake
+    _Primitive-address-of-xmem/imm32/next
+# - floating-point-address
+_Primitive-address-of-xmem:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var1/reg <- address var2 => 8d/copy-address var2/rm32 var1/r32
+    0x11/imm32/alloc-id:fake
+    _string-address/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-float-var-in-mem/imm32/inouts
+    0x11/imm32/alloc-id:fake
+    Single-addr-var-in-some-register/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_8d_copy_address/imm32/subx-name
+    1/imm32/rm32-is-first-inout
+    3/imm32/r32-is-first-output
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-add-xreg-to-xreg/imm32/next
+# - floating-point add
+_Primitive-add-xreg-to-xreg:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var1/xreg <- add var2/xreg => f3 0f 58/add var1/xm32 var2/x32
+    0x11/imm32/alloc-id:fake
+    _string-add/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-float-var-in-some-register/imm32/inouts
+    0x11/imm32/alloc-id:fake
+    Single-float-var-in-some-register/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_f3_0f_58_add/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    1/imm32/xm32-is-first-inout
+    3/imm32/x32-is-first-output
+    0x11/imm32/alloc-id:fake
+    _Primitive-add-mem-to-xreg/imm32/next
+_Primitive-add-mem-to-xreg:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var1/xreg <- add var2 => f3 0f 58/add var2/xm32 var1/x32
+    0x11/imm32/alloc-id:fake
+    _string-add/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-float-var-in-mem/imm32/inouts
+    0x11/imm32/alloc-id:fake
+    Single-float-var-in-some-register/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_f3_0f_58_add/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    1/imm32/xm32-is-first-inout
+    3/imm32/x32-is-first-output
+    0x11/imm32/alloc-id:fake
+    _Primitive-subtract-xreg-from-xreg/imm32/next
+# - floating-point subtract
+_Primitive-subtract-xreg-from-xreg:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var1/xreg <- subtract var2/xreg => f3 0f 5c/subtract var1/xm32 var2/x32
+    0x11/imm32/alloc-id:fake
+    _string-subtract/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-float-var-in-some-register/imm32/inouts
+    0x11/imm32/alloc-id:fake
+    Single-float-var-in-some-register/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_f3_0f_5c_subtract/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    1/imm32/xm32-is-first-inout
+    3/imm32/x32-is-first-output
+    0x11/imm32/alloc-id:fake
+    _Primitive-subtract-mem-from-xreg/imm32/next
+_Primitive-subtract-mem-from-xreg:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var1/xreg <- subtract var2 => f3 0f 5c/subtract var2/xm32 var1/x32
+    0x11/imm32/alloc-id:fake
+    _string-subtract/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-float-var-in-mem/imm32/inouts
+    0x11/imm32/alloc-id:fake
+    Single-float-var-in-some-register/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_f3_0f_5c_subtract/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    1/imm32/xm32-is-first-inout
+    3/imm32/x32-is-first-output
+    0x11/imm32/alloc-id:fake
+    _Primitive-multiply-xreg-by-xreg/imm32/next
+# - floating-point multiply
+_Primitive-multiply-xreg-by-xreg:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var1/xreg <- multiply var2 => f3 0f 59/multiply var2/xm32 var1/x32
+    0x11/imm32/alloc-id:fake
+    _string-multiply/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-float-var-in-some-register/imm32/inouts
+    0x11/imm32/alloc-id:fake
+    Single-float-var-in-some-register/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_f3_0f_59_multiply/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    1/imm32/xm32-is-first-inout
+    3/imm32/x32-is-first-output
+    0x11/imm32/alloc-id:fake
+    _Primitive-multiply-xreg-by-mem/imm32/next
+_Primitive-multiply-xreg-by-mem:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var1/xreg <- multiply var2 => 53 0f 59/multiply var2/xm32 var1/x32
+    0x11/imm32/alloc-id:fake
+    _string-multiply/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-float-var-in-mem/imm32/inouts
+    0x11/imm32/alloc-id:fake
+    Single-float-var-in-some-register/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_f3_0f_59_multiply/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    1/imm32/xm32-is-first-inout
+    3/imm32/x32-is-first-output
+    0x11/imm32/alloc-id:fake
+    _Primitive-divide-xreg-by-xreg/imm32/next
+# - floating-point divide
+_Primitive-divide-xreg-by-xreg:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var1/xreg <- divide var2 => f3 0f 5e/divide var2/xm32 var1/x32
+    0x11/imm32/alloc-id:fake
+    _string-divide/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-float-var-in-some-register/imm32/inouts
+    0x11/imm32/alloc-id:fake
+    Single-float-var-in-some-register/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_f3_0f_5e_divide/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    1/imm32/xm32-is-first-inout
+    3/imm32/x32-is-first-output
+    0x11/imm32/alloc-id:fake
+    _Primitive-divide-xreg-by-mem/imm32/next
+_Primitive-divide-xreg-by-mem:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var1/xreg <- divide var2 => f3 0f 5e/divide var2/xm32 var1/x32
+    0x11/imm32/alloc-id:fake
+    _string-divide/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-float-var-in-mem/imm32/inouts
+    0x11/imm32/alloc-id:fake
+    Single-float-var-in-some-register/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_f3_0f_5e_divide/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    1/imm32/xm32-is-first-inout
+    3/imm32/x32-is-first-output
+    0x11/imm32/alloc-id:fake
+    _Primitive-max-xreg-with-xreg/imm32/next
+# - floating-point maximum
+_Primitive-max-xreg-with-xreg:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var1/xreg <- max var2 => f3 0f 5f/max var2/xm32 var1/x32
+    0x11/imm32/alloc-id:fake
+    _string-max/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-float-var-in-some-register/imm32/inouts
+    0x11/imm32/alloc-id:fake
+    Single-float-var-in-some-register/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_f3_0f_5f_max/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    1/imm32/xm32-is-first-inout
+    3/imm32/x32-is-first-output
+    0x11/imm32/alloc-id:fake
+    _Primitive-max-xreg-with-mem/imm32/next
+_Primitive-max-xreg-with-mem:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var1/xreg <- divide var2 => f3 0f 5f/max var2/xm32 var1/x32
+    0x11/imm32/alloc-id:fake
+    _string-max/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-float-var-in-mem/imm32/inouts
+    0x11/imm32/alloc-id:fake
+    Single-float-var-in-some-register/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_f3_0f_5f_max/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    1/imm32/xm32-is-first-inout
+    3/imm32/x32-is-first-output
+    0x11/imm32/alloc-id:fake
+    _Primitive-min-xreg-with-xreg/imm32/next
+# - floating-point minimum
+_Primitive-min-xreg-with-xreg:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var1/xreg <- divide var2 => f3 0f 5d/min var2/xm32 var1/x32
+    0x11/imm32/alloc-id:fake
+    _string-min/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-float-var-in-some-register/imm32/inouts
+    0x11/imm32/alloc-id:fake
+    Single-float-var-in-some-register/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_f3_0f_5d_min/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    1/imm32/xm32-is-first-inout
+    3/imm32/x32-is-first-output
+    0x11/imm32/alloc-id:fake
+    _Primitive-min-xreg-with-mem/imm32/next
+_Primitive-min-xreg-with-mem:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var1/xreg <- divide var2 => f3 0f 5d/min var2/xm32 var1/x32
+    0x11/imm32/alloc-id:fake
+    _string-min/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-float-var-in-mem/imm32/inouts
+    0x11/imm32/alloc-id:fake
+    Single-float-var-in-some-register/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_f3_0f_5d_min/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    1/imm32/xm32-is-first-inout
+    3/imm32/x32-is-first-output
+    0x11/imm32/alloc-id:fake
+    _Primitive-reciprocal-xreg-to-xreg/imm32/next
+# - floating-point reciprocal
+_Primitive-reciprocal-xreg-to-xreg:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var1/xreg <- reciprocal var2 => f3 0f 53/reciprocal var2/xm32 var1/x32
+    0x11/imm32/alloc-id:fake
+    _string-reciprocal/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-float-var-in-some-register/imm32/inouts
+    0x11/imm32/alloc-id:fake
+    Single-float-var-in-some-register/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_f3_0f_53_reciprocal/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    1/imm32/xm32-is-first-inout
+    3/imm32/x32-is-first-output
+    0x11/imm32/alloc-id:fake
+    _Primitive-reciprocal-mem-to-xreg/imm32/next
+_Primitive-reciprocal-mem-to-xreg:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var1/xreg <- divide var2 => f3 0f 53/reciprocal var2/xm32 var1/x32
+    0x11/imm32/alloc-id:fake
+    _string-reciprocal/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-float-var-in-mem/imm32/inouts
+    0x11/imm32/alloc-id:fake
+    Single-float-var-in-some-register/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_f3_0f_53_reciprocal/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    1/imm32/xm32-is-first-inout
+    3/imm32/x32-is-first-output
+    0x11/imm32/alloc-id:fake
+    _Primitive-square-root-xreg-to-xreg/imm32/next
+# - floating-point square root
+_Primitive-square-root-xreg-to-xreg:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var1/xreg <- square-root var2 => f3 0f 51/square-root var2/xm32 var1/x32
+    0x11/imm32/alloc-id:fake
+    _string-square-root/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-float-var-in-some-register/imm32/inouts
+    0x11/imm32/alloc-id:fake
+    Single-float-var-in-some-register/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_f3_0f_51_square_root/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    1/imm32/xm32-is-first-inout
+    3/imm32/x32-is-first-output
+    0x11/imm32/alloc-id:fake
+    _Primitive-square-root-mem-to-xreg/imm32/next
+_Primitive-square-root-mem-to-xreg:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var1/xreg <- divide var2 => f3 0f 51/square-root var2/xm32 var1/x32
+    0x11/imm32/alloc-id:fake
+    _string-square-root/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-float-var-in-mem/imm32/inouts
+    0x11/imm32/alloc-id:fake
+    Single-float-var-in-some-register/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_f3_0f_51_square_root/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    1/imm32/xm32-is-first-inout
+    3/imm32/x32-is-first-output
+    0x11/imm32/alloc-id:fake
+    _Primitive-inverse-square-root-xreg-to-xreg/imm32/next
+# - floating-point inverse square root 1/sqrt(x)
+_Primitive-inverse-square-root-xreg-to-xreg:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var1/xreg <- reciprocal var2 => f3 0f 52/inverse-square-root var2/xm32 var1/x32
+    0x11/imm32/alloc-id:fake
+    _string-inverse-square-root/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-float-var-in-some-register/imm32/inouts
+    0x11/imm32/alloc-id:fake
+    Single-float-var-in-some-register/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_f3_0f_52_inverse_square_root/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    1/imm32/xm32-is-first-inout
+    3/imm32/x32-is-first-output
+    0x11/imm32/alloc-id:fake
+    _Primitive-inverse-square-root-mem-to-xreg/imm32/next
+_Primitive-inverse-square-root-mem-to-xreg:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # var1/xreg <- divide var2 => f3 0f 52/inverse-square-root var2/xm32 var1/x32
+    0x11/imm32/alloc-id:fake
+    _string-inverse-square-root/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-float-var-in-mem/imm32/inouts
+    0x11/imm32/alloc-id:fake
+    Single-float-var-in-some-register/imm32/outputs
+    0x11/imm32/alloc-id:fake
+    _string_f3_0f_52_inverse_square_root/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    1/imm32/xm32-is-first-inout
+    3/imm32/x32-is-first-output
+    0x11/imm32/alloc-id:fake
+    _Primitive-compare-xreg-with-xreg/imm32/next
+# - floating-point compare
+_Primitive-compare-xreg-with-xreg:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # compare var1/reg1 var2/reg2 => 0f 2f/compare var2/x32 var1/xm32
+    0x11/imm32/alloc-id:fake
+    _string-compare/imm32/name
+    0x11/imm32/alloc-id:fake
+    Two-float-args-in-regs/imm32/inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_0f_2f_compare/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    2/imm32/xm32-is-second-inout
+    1/imm32/x32-is-first-inout
+    0x11/imm32/alloc-id:fake
+    _Primitive-compare-xreg-with-mem/imm32/next
+_Primitive-compare-xreg-with-mem:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    # compare var1/xreg var2 => 0f 2f/compare var1/x32 var2/xm32
+    0x11/imm32/alloc-id:fake
+    _string-compare/imm32/name
+    0x11/imm32/alloc-id:fake
+    Two-args-float-reg-float-stack/imm32/inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_0f_2f_compare/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    2/imm32/xm32-is-second-inout
+    1/imm32/x32-is-first-inout
+    0x11/imm32/alloc-id:fake
+    _Primitive-break-if-addr</imm32/next
+# - branches
+_Primitive-break-if-addr<:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    _string-break-if-addr</imm32/name
+    0/imm32/no-inouts
+    0/imm32/no-inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_0f_82_jump_break/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-break-if-addr>=/imm32/next
+_Primitive-break-if-addr>=:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    _string-break-if-addr>=/imm32/name
+    0/imm32/no-inouts
+    0/imm32/no-inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_0f_83_jump_break/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-break-if-=/imm32/next
+_Primitive-break-if-=:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    _string-break-if-=/imm32/name
+    0/imm32/no-inouts
+    0/imm32/no-inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_0f_84_jump_break/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-break-if-!=/imm32/next
+_Primitive-break-if-!=:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    _string-break-if-!=/imm32/name
+    0/imm32/no-inouts
+    0/imm32/no-inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_0f_85_jump_break/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-break-if-addr<=/imm32/next
+_Primitive-break-if-addr<=:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    _string-break-if-addr<=/imm32/name
+    0/imm32/no-inouts
+    0/imm32/no-inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_0f_86_jump_break/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-break-if-addr>/imm32/next
+_Primitive-break-if-addr>:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    _string-break-if-addr>/imm32/name
+    0/imm32/no-inouts
+    0/imm32/no-inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_0f_87_jump_break/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-break-if-</imm32/next
+_Primitive-break-if-<:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    _string-break-if-</imm32/name
+    0/imm32/no-inouts
+    0/imm32/no-inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_0f_8c_jump_break/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-break-if->=/imm32/next
+_Primitive-break-if->=:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    _string-break-if->=/imm32/name
+    0/imm32/no-inouts
+    0/imm32/no-inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_0f_8d_jump_break/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-break-if-<=/imm32/next
+_Primitive-break-if-<=:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    _string-break-if-<=/imm32/name
+    0/imm32/no-inouts
+    0/imm32/no-inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_0f_8e_jump_break/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-break-if->/imm32/next
+_Primitive-break-if->:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    _string-break-if->/imm32/name
+    0/imm32/no-inouts
+    0/imm32/no-inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_0f_8f_jump_break/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-break/imm32/next
+_Primitive-break:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    _string-break/imm32/name
+    0/imm32/no-inouts
+    0/imm32/no-inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_e9_jump_break/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-loop-if-addr</imm32/next
+_Primitive-loop-if-addr<:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    _string-loop-if-addr</imm32/name
+    0/imm32/no-inouts
+    0/imm32/no-inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_0f_82_jump_loop/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-loop-if-addr>=/imm32/next
+_Primitive-loop-if-addr>=:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    _string-loop-if-addr>=/imm32/name
+    0/imm32/no-inouts
+    0/imm32/no-inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_0f_83_jump_loop/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-loop-if-=/imm32/next
+_Primitive-loop-if-=:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    _string-loop-if-=/imm32/name
+    0/imm32/no-inouts
+    0/imm32/no-inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_0f_84_jump_loop/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-loop-if-!=/imm32/next
+_Primitive-loop-if-!=:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    _string-loop-if-!=/imm32/name
+    0/imm32/no-inouts
+    0/imm32/no-inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_0f_85_jump_loop/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-loop-if-addr<=/imm32/next
+_Primitive-loop-if-addr<=:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    _string-loop-if-addr<=/imm32/name
+    0/imm32/no-inouts
+    0/imm32/no-inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_0f_86_jump_loop/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-loop-if-addr>/imm32/next
+_Primitive-loop-if-addr>:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    _string-loop-if-addr>/imm32/name
+    0/imm32/no-inouts
+    0/imm32/no-inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_0f_87_jump_loop/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-loop-if-</imm32/next
+_Primitive-loop-if-<:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    _string-loop-if-</imm32/name
+    0/imm32/no-inouts
+    0/imm32/no-inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_0f_8c_jump_loop/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-loop-if->=/imm32/next
+_Primitive-loop-if->=:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    _string-loop-if->=/imm32/name
+    0/imm32/no-inouts
+    0/imm32/no-inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_0f_8d_jump_loop/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-loop-if-<=/imm32/next
+_Primitive-loop-if-<=:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    _string-loop-if-<=/imm32/name
+    0/imm32/no-inouts
+    0/imm32/no-inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_0f_8e_jump_loop/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-loop-if->/imm32/next
+_Primitive-loop-if->:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    _string-loop-if->/imm32/name
+    0/imm32/no-inouts
+    0/imm32/no-inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_0f_8f_jump_loop/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-loop/imm32/next  # we probably don't need an unconditional break
+_Primitive-loop:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    _string-loop/imm32/name
+    0/imm32/no-inouts
+    0/imm32/no-inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_e9_jump_loop/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-break-if-addr<-named/imm32/next
+# - branches to named blocks
+_Primitive-break-if-addr<-named:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    _string-break-if-addr</imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-lit-var/imm32/inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_0f_82_jump_label/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    1/imm32/disp32-is-first-inout
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-break-if-addr>=-named/imm32/next
+_Primitive-break-if-addr>=-named:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    _string-break-if-addr>=/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-lit-var/imm32/inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_0f_83_jump_label/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    1/imm32/disp32-is-first-inout
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-break-if-=-named/imm32/next
+_Primitive-break-if-=-named:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    _string-break-if-=/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-lit-var/imm32/inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_0f_84_jump_label/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    1/imm32/disp32-is-first-inout
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-break-if-!=-named/imm32/next
+_Primitive-break-if-!=-named:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    _string-break-if-!=/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-lit-var/imm32/inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_0f_85_jump_label/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    1/imm32/disp32-is-first-inout
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-break-if-addr<=-named/imm32/next
+_Primitive-break-if-addr<=-named:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    _string-break-if-addr<=/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-lit-var/imm32/inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_0f_86_jump_label/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    1/imm32/disp32-is-first-inout
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-break-if-addr>-named/imm32/next
+_Primitive-break-if-addr>-named:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    _string-break-if-addr>/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-lit-var/imm32/inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_0f_87_jump_label/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    1/imm32/disp32-is-first-inout
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-break-if-<-named/imm32/next
+_Primitive-break-if-<-named:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    _string-break-if-</imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-lit-var/imm32/inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_0f_8c_jump_label/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    1/imm32/disp32-is-first-inout
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-break-if->=-named/imm32/next
+_Primitive-break-if->=-named:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    _string-break-if->=/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-lit-var/imm32/inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_0f_8d_jump_label/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    1/imm32/disp32-is-first-inout
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-break-if-<=-named/imm32/next
+_Primitive-break-if-<=-named:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    _string-break-if-<=/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-lit-var/imm32/inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_0f_8e_jump_label/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    1/imm32/disp32-is-first-inout
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-break-if->-named/imm32/next
+_Primitive-break-if->-named:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    _string-break-if->/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-lit-var/imm32/inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_0f_8f_jump_label/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    1/imm32/disp32-is-first-inout
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-break-named/imm32/next
+_Primitive-break-named:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    _string-break/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-lit-var/imm32/inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_e9_jump_label/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    1/imm32/disp32-is-first-inout
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-loop-if-addr<-named/imm32/next
+_Primitive-loop-if-addr<-named:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    _string-loop-if-addr</imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-lit-var/imm32/inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_0f_82_jump_label/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    1/imm32/disp32-is-first-inout
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-loop-if-addr>=-named/imm32/next
+_Primitive-loop-if-addr>=-named:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    _string-loop-if-addr>=/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-lit-var/imm32/inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_0f_83_jump_label/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    1/imm32/disp32-is-first-inout
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-loop-if-=-named/imm32/next
+_Primitive-loop-if-=-named:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    _string-loop-if-=/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-lit-var/imm32/inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_0f_84_jump_label/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    1/imm32/disp32-is-first-inout
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-loop-if-!=-named/imm32/next
+_Primitive-loop-if-!=-named:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    _string-loop-if-!=/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-lit-var/imm32/inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_0f_85_jump_label/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    1/imm32/disp32-is-first-inout
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-loop-if-addr<=-named/imm32/next
+_Primitive-loop-if-addr<=-named:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    _string-loop-if-addr<=/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-lit-var/imm32/inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_0f_86_jump_label/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    1/imm32/disp32-is-first-inout
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-loop-if-addr>-named/imm32/next
+_Primitive-loop-if-addr>-named:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    _string-loop-if-addr>/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-lit-var/imm32/inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_0f_87_jump_label/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    1/imm32/disp32-is-first-inout
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-loop-if-<-named/imm32/next
+_Primitive-loop-if-<-named:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    _string-loop-if-</imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-lit-var/imm32/inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_0f_8c_jump_label/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    1/imm32/disp32-is-first-inout
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-loop-if->=-named/imm32/next
+_Primitive-loop-if->=-named:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    _string-loop-if->=/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-lit-var/imm32/inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_0f_8d_jump_label/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    1/imm32/disp32-is-first-inout
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-loop-if-<=-named/imm32/next
+_Primitive-loop-if-<=-named:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    _string-loop-if-<=/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-lit-var/imm32/inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_0f_8e_jump_label/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    1/imm32/disp32-is-first-inout
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-loop-if->-named/imm32/next
+_Primitive-loop-if->-named:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    _string-loop-if->/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-lit-var/imm32/inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_0f_8f_jump_label/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    1/imm32/disp32-is-first-inout
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-loop-named/imm32/next  # we probably don't need an unconditional break
+_Primitive-loop-named:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    _string-loop/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-lit-var/imm32/inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_e9_jump_label/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    1/imm32/disp32-is-first-inout
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-break-if-float</imm32/next
+# - branches based on floating-point comparisons
+_Primitive-break-if-float<:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    _string-break-if-float</imm32/name
+    0/imm32/no-inouts
+    0/imm32/no-inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_0f_82_jump_break/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-break-if-float>=/imm32/next
+_Primitive-break-if-float>=:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    _string-break-if-float>=/imm32/name
+    0/imm32/no-inouts
+    0/imm32/no-inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_0f_83_jump_break/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-break-if-float<=/imm32/next
+_Primitive-break-if-float<=:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    _string-break-if-float<=/imm32/name
+    0/imm32/no-inouts
+    0/imm32/no-inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_0f_86_jump_break/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-break-if-float>/imm32/next
+_Primitive-break-if-float>:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    _string-break-if-float>/imm32/name
+    0/imm32/no-inouts
+    0/imm32/no-inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_0f_87_jump_break/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-loop-if-float</imm32/next
+_Primitive-loop-if-float<:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    _string-loop-if-float</imm32/name
+    0/imm32/no-inouts
+    0/imm32/no-inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_0f_82_jump_loop/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-loop-if-float>=/imm32/next
+_Primitive-loop-if-float>=:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    _string-loop-if-float>=/imm32/name
+    0/imm32/no-inouts
+    0/imm32/no-inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_0f_83_jump_loop/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-loop-if-float<=/imm32/next
+_Primitive-loop-if-float<=:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    _string-loop-if-float<=/imm32/name
+    0/imm32/no-inouts
+    0/imm32/no-inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_0f_86_jump_loop/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-loop-if-float>/imm32/next
+_Primitive-loop-if-float>:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    _string-loop-if-float>/imm32/name
+    0/imm32/no-inouts
+    0/imm32/no-inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_0f_87_jump_loop/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    0/imm32/no-disp32
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-break-if-float<-named/imm32/next
+_Primitive-break-if-float<-named:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    _string-break-if-float</imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-lit-var/imm32/inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_0f_82_jump_label/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    1/imm32/disp32-is-first-inout
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-break-if-float>=-named/imm32/next
+_Primitive-break-if-float>=-named:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    _string-break-if-float>=/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-lit-var/imm32/inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_0f_83_jump_label/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    1/imm32/disp32-is-first-inout
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-break-if-float<=-named/imm32/next
+_Primitive-break-if-float<=-named:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    _string-break-if-float<=/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-lit-var/imm32/inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_0f_86_jump_label/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    1/imm32/disp32-is-first-inout
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-break-if-float>-named/imm32/next
+_Primitive-break-if-float>-named:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    _string-break-if-float>/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-lit-var/imm32/inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_0f_87_jump_label/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    1/imm32/disp32-is-first-inout
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-loop-if-float<-named/imm32/next
+_Primitive-loop-if-float<-named:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    _string-loop-if-float</imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-lit-var/imm32/inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_0f_82_jump_label/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    1/imm32/disp32-is-first-inout
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-loop-if-float>=-named/imm32/next
+_Primitive-loop-if-float>=-named:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    _string-loop-if-float>=/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-lit-var/imm32/inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_0f_83_jump_label/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    1/imm32/disp32-is-first-inout
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-loop-if-float<=-named/imm32/next
+_Primitive-loop-if-float<=-named:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    _string-loop-if-float<=/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-lit-var/imm32/inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_0f_86_jump_label/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    1/imm32/disp32-is-first-inout
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0x11/imm32/alloc-id:fake
+    _Primitive-loop-if-float>-named/imm32/next
+_Primitive-loop-if-float>-named:  # (payload primitive)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    _string-loop-if-float>/imm32/name
+    0x11/imm32/alloc-id:fake
+    Single-lit-var/imm32/inouts
+    0/imm32/no-outputs
+    0/imm32/no-outputs
+    0x11/imm32/alloc-id:fake
+    _string_0f_87_jump_label/imm32/subx-name
+    0/imm32/no-rm32
+    0/imm32/no-r32
+    0/imm32/no-imm32
+    0/imm32/no-imm8
+    1/imm32/disp32-is-first-inout
+    0/imm32/no-xm32
+    0/imm32/no-x32
+    0/imm32/next
+    0/imm32/next
+
+# string literals for Mu instructions
+_string-add:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "add"
+    0x3/imm32/size
+    0x61/a 0x64/d 0x64/d
+_string-address:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "address"
+    0x7/imm32/size
+    0x61/a 0x64/d 0x64/d 0x72/r 0x65/e 0x73/s 0x73/s
+_string-add-to:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "add-to"
+    0x6/imm32/size
+    0x61/a 0x64/d 0x64/d 0x2d/dash 0x74/t 0x6f/o
+_string-and:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "and"
+    0x3/imm32/size
+    0x61/a 0x6e/n 0x64/d
+_string-and-with:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "and-with"
+    0x8/imm32/size
+    0x61/a 0x6e/n 0x64/d 0x2d/dash 0x77/w 0x69/i 0x74/t 0x68/h
+_string-break:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "break"
+    0x5/imm32/size
+    0x62/b 0x72/r 0x65/e 0x61/a 0x6b/k
+_string-break-if-<:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "break-if-<"
+    0xa/imm32/size
+    0x62/b 0x72/r 0x65/e 0x61/a 0x6b/k 0x2d/dash 0x69/i 0x66/f 0x2d/dash 0x3c/<
+_string-break-if-<=:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "break-if-<="
+    0xb/imm32/size
+    0x62/b 0x72/r 0x65/e 0x61/a 0x6b/k 0x2d/dash 0x69/i 0x66/f 0x2d/dash 0x3c/< 0x3d/=
+_string-break-if-=:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "break-if-="
+    0xa/imm32/size
+    0x62/b 0x72/r 0x65/e 0x61/a 0x6b/k 0x2d/dash 0x69/i 0x66/f 0x2d/dash 0x3d/=
+_string-break-if->:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "break-if->"
+    0xa/imm32/size
+    0x62/b 0x72/r 0x65/e 0x61/a 0x6b/k 0x2d/dash 0x69/i 0x66/f 0x2d/dash 0x3e/>
+_string-break-if->=:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "break-if->="
+    0xb/imm32/size
+    0x62/b 0x72/r 0x65/e 0x61/a 0x6b/k 0x2d/dash 0x69/i 0x66/f 0x2d/dash 0x3e/> 0x3d/=
+_string-break-if-!=:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "break-if-!="
+    0xb/imm32/size
+    0x62/b 0x72/r 0x65/e 0x61/a 0x6b/k 0x2d/dash 0x69/i 0x66/f 0x2d/dash 0x21/! 0x3d/=
+_string-break-if-addr<:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "break-if-addr<"
+    0xe/imm32/size
+    0x62/b 0x72/r 0x65/e 0x61/a 0x6b/k 0x2d/dash 0x69/i 0x66/f 0x2d/dash 0x61/a 0x64/d 0x64/d 0x72/r 0x3c/<
+_string-break-if-addr<=:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "break-if-addr<="
+    0xf/imm32/size
+    0x62/b 0x72/r 0x65/e 0x61/a 0x6b/k 0x2d/dash 0x69/i 0x66/f 0x2d/dash 0x61/a 0x64/d 0x64/d 0x72/r 0x3c/< 0x3d/=
+_string-break-if-addr>:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "break-if-addr>"
+    0xe/imm32/size
+    0x62/b 0x72/r 0x65/e 0x61/a 0x6b/k 0x2d/dash 0x69/i 0x66/f 0x2d/dash 0x61/a 0x64/d 0x64/d 0x72/r 0x3e/>
+_string-break-if-addr>=:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "break-if-addr>="
+    0xf/imm32/size
+    0x62/b 0x72/r 0x65/e 0x61/a 0x6b/k 0x2d/dash 0x69/i 0x66/f 0x2d/dash 0x61/a 0x64/d 0x64/d 0x72/r 0x3e/> 0x3d/=
+_string-break-if-float<:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "break-if-float<"
+    0xf/imm32/size
+    0x62/b 0x72/r 0x65/e 0x61/a 0x6b/k 0x2d/dash 0x69/i 0x66/f 0x2d/dash 0x66/f 0x6c/l 0x6f/o 0x61/a 0x74/t 0x3c/<
+_string-break-if-float<=:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "break-if-float<="
+    0x10/imm32/size
+    0x62/b 0x72/r 0x65/e 0x61/a 0x6b/k 0x2d/dash 0x69/i 0x66/f 0x2d/dash 0x66/f 0x6c/l 0x6f/o 0x61/a 0x74/t 0x3c/< 0x3d/=
+_string-break-if-float>:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "break-if-float>"
+    0xf/imm32/size
+    0x62/b 0x72/r 0x65/e 0x61/a 0x6b/k 0x2d/dash 0x69/i 0x66/f 0x2d/dash 0x66/f 0x6c/l 0x6f/o 0x61/a 0x74/t 0x3e/>
+_string-break-if-float>=:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "break-if-float>="
+    0x10/imm32/size
+    0x62/b 0x72/r 0x65/e 0x61/a 0x6b/k 0x2d/dash 0x69/i 0x66/f 0x2d/dash 0x66/f 0x6c/l 0x6f/o 0x61/a 0x74/t 0x3e/> 0x3d/=
+_string-compare:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "compare"
+    0x7/imm32/size
+    0x63/c 0x6f/o 0x6d/m 0x70/p 0x61/a 0x72/r 0x65/e
+_string-copy:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "copy"
+    0x4/imm32/size
+    0x63/c 0x6f/o 0x70/p 0x79/y
+_string-copy-to:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "copy-to"
+    0x7/imm32/size
+    0x63/c 0x6f/o 0x70/p 0x79/y 0x2d/dash 0x74/t 0x6f/o
+_string-copy-byte:
+    0x11/imm32/alloc-id:fake:payload
+    # "copy-byte"
+    0x9/imm32/size
+    0x63/c 0x6f/o 0x70/p 0x79/y 0x2d/- 0x62/b 0x79/y 0x74/t 0x65/e
+_string-copy-byte-to:
+    0x11/imm32/alloc-id:fake:payload
+    # "copy-byte-to"
+    0xc/imm32/size
+    0x63/c 0x6f/o 0x70/p 0x79/y 0x2d/- 0x62/b 0x79/y 0x74/t 0x65/e 0x2d/- 0x74/t 0x6f/o
+_string-decrement:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "decrement"
+    0x9/imm32/size
+    0x64/d 0x65/e 0x63/c 0x72/r 0x65/e 0x6d/m 0x65/e 0x6e/n 0x74/t
+_string-increment:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "increment"
+    0x9/imm32/size
+    0x69/i 0x6e/n 0x63/c 0x72/r 0x65/e 0x6d/m 0x65/e 0x6e/n 0x74/t
+_string-loop:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "loop"
+    0x4/imm32/size
+    0x6c/l 0x6f/o 0x6f/o 0x70/p
+_string-loop-if-<:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "loop-if-<"
+    0x9/imm32/size
+    0x6c/l 0x6f/o 0x6f/o 0x70/p 0x2d/dash 0x69/i 0x66/f 0x2d/dash 0x3c/<
+_string-loop-if-<=:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "loop-if-<="
+    0xa/imm32/size
+    0x6c/l 0x6f/o 0x6f/o 0x70/p 0x2d/dash 0x69/i 0x66/f 0x2d/dash 0x3c/< 0x3d/=
+_string-loop-if-=:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "loop-if-="
+    0x9/imm32/size
+    0x6c/l 0x6f/o 0x6f/o 0x70/p 0x2d/dash 0x69/i 0x66/f 0x2d/dash 0x3d/=
+_string-loop-if->:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "loop-if->"
+    0x9/imm32/size
+    0x6c/l 0x6f/o 0x6f/o 0x70/p 0x2d/dash 0x69/i 0x66/f 0x2d/dash 0x3e/>
+_string-loop-if->=:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "loop-if->="
+    0xa/imm32/size
+    0x6c/l 0x6f/o 0x6f/o 0x70/p 0x2d/dash 0x69/i 0x66/f 0x2d/dash 0x3e/> 0x3d/=
+_string-loop-if-!=:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "loop-if-!="
+    0xa/imm32/size
+    0x6c/l 0x6f/o 0x6f/o 0x70/p 0x2d/dash 0x69/i 0x66/f 0x2d/dash 0x21/! 0x3d/=
+_string-loop-if-addr<:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "loop-if-addr<"
+    0xd/imm32/size
+    0x6c/l 0x6f/o 0x6f/o 0x70/p 0x2d/dash 0x69/i 0x66/f 0x2d/dash 0x61/a 0x64/d 0x64/d 0x72/r 0x3c/<
+_string-loop-if-addr<=:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "loop-if-addr<="
+    0xe/imm32/size
+    0x6c/l 0x6f/o 0x6f/o 0x70/p 0x2d/dash 0x69/i 0x66/f 0x2d/dash 0x61/a 0x64/d 0x64/d 0x72/r 0x3c/< 0x3d/=
+_string-loop-if-addr>:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "loop-if-addr>"
+    0xd/imm32/size
+    0x6c/l 0x6f/o 0x6f/o 0x70/p 0x2d/dash 0x69/i 0x66/f 0x2d/dash 0x61/a 0x64/d 0x64/d 0x72/r 0x3e/>
+_string-loop-if-addr>=:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "loop-if-addr>="
+    0xe/imm32/size
+    0x6c/l 0x6f/o 0x6f/o 0x70/p 0x2d/dash 0x69/i 0x66/f 0x2d/dash 0x61/a 0x64/d 0x64/d 0x72/r 0x3e/> 0x3d/=
+_string-loop-if-float<:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "loop-if-float<"
+    0xe/imm32/size
+    0x6c/l 0x6f/o 0x6f/o 0x70/p 0x2d/dash 0x69/i 0x66/f 0x2d/dash 0x66/f 0x6c/l 0x6f/o 0x61/a 0x74/t 0x3c/<
+_string-loop-if-float<=:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "loop-if-float<="
+    0xf/imm32/size
+    0x6c/l 0x6f/o 0x6f/o 0x70/p 0x2d/dash 0x69/i 0x66/f 0x2d/dash 0x66/f 0x6c/l 0x6f/o 0x61/a 0x74/t 0x3c/< 0x3d/=
+_string-loop-if-float>:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "loop-if-float>"
+    0xe/imm32/size
+    0x6c/l 0x6f/o 0x6f/o 0x70/p 0x2d/dash 0x69/i 0x66/f 0x2d/dash 0x66/f 0x6c/l 0x6f/o 0x61/a 0x74/t 0x3e/>
+_string-loop-if-float>=:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "loop-if-float>="
+    0xf/imm32/size
+    0x6c/l 0x6f/o 0x6f/o 0x70/p 0x2d/dash 0x69/i 0x66/f 0x2d/dash 0x66/f 0x6c/l 0x6f/o 0x61/a 0x74/t 0x3e/> 0x3d/=
+_string-multiply:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "multiply"
+    0x8/imm32/size
+    0x6d/m 0x75/u 0x6c/l 0x74/t 0x69/i 0x70/p 0x6c/l 0x79/y
+_string-convert:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "convert"
+    0x7/imm32/size
+    0x63/c 0x6f/o 0x6e/n 0x76/v 0x65/e 0x72/r 0x74/t
+_string-truncate:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "truncate"
+    0x8/imm32/size
+    0x74/t 0x72/r 0x75/u 0x6e/n 0x63/c 0x61/a 0x74/t 0x65/e
+_string-reinterpret:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "reinterpret"
+    0xb/imm32/size
+    0x72/r 0x65/e 0x69/i 0x6e/n 0x74/t 0x65/e 0x72/r 0x70/p 0x72/r 0x65/e 0x74/t
+_string-divide:
+    0x11/imm32/alloc-id:fake:payload
+    # "divide"
+    0x6/imm32/size
+    0x64/d 0x69/i 0x76/v 0x69/i 0x64/d 0x65/e
+_string-max:
+    0x11/imm32/alloc-id:fake:payload
+    # "max"
+    0x3/imm32/size
+    0x6d/m 0x61/a 0x78/x
+_string-min:
+    0x11/imm32/alloc-id:fake:payload
+    # "min"
+    0x3/imm32/size
+    0x6d/m 0x69/i 0x6e/n
+_string-reciprocal:
+    0x11/imm32/alloc-id:fake:payload
+    # "reciprocal"
+    0xa/imm32/size
+    0x72/r 0x65/e 0x63/c 0x69/i 0x70/p 0x72/r 0x6f/o 0x63/c 0x61/a 0x6c/l
+_string-square-root:
+    0x11/imm32/alloc-id:fake:payload
+    # "square-root"
+    0xb/imm32/size
+    0x73/s 0x71/q 0x75/u 0x61/a 0x72/r 0x65/e 0x2d/- 0x72/r 0x6f/o 0x6f/o 0x74/t
+_string-inverse-square-root:
+    0x11/imm32/alloc-id:fake:payload
+    # "inverse-square-root"
+    0x13/imm32/size
+    0x69/i 0x6e/n 0x76/v 0x65/e 0x72/r 0x73/s 0x65/e 0x2d/- 0x73/s 0x71/q 0x75/u 0x61/a 0x72/r 0x65/e 0x2d/- 0x72/r 0x6f/o 0x6f/o 0x74/t
+_string-negate:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "negate"
+    0x6/imm32/size
+    0x6e/n 0x65/e 0x67/g 0x61/a 0x74/t 0x65/e
+_string-or:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "or"
+    0x2/imm32/size
+    0x6f/o 0x72/r
+_string-or-with:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "or-with"
+    0x7/imm32/size
+    0x6f/o 0x72/r 0x2d/dash 0x77/w 0x69/i 0x74/t 0x68/h
+_string-subtract:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "subtract"
+    0x8/imm32/size
+    0x73/s 0x75/u 0x62/b 0x74/t 0x72/r 0x61/a 0x63/c 0x74/t
+_string-subtract-from:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "subtract-from"
+    0xd/imm32/size
+    0x73/s 0x75/u 0x62/b 0x74/t 0x72/r 0x61/a 0x63/c 0x74/t 0x2d/dash 0x66/f 0x72/r 0x6f/o 0x6d/m
+_string-xor:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "xor"
+    0x3/imm32/size
+    0x78/x 0x6f/o 0x72/r
+_string-xor-with:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "xor-with"
+    0x8/imm32/size
+    0x78/x 0x6f/o 0x72/r 0x2d/dash 0x77/w 0x69/i 0x74/t 0x68/h
+_string-shift-left:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "shift-left"
+    0xa/imm32/size
+    0x73/s 0x68/h 0x69/i 0x66/f 0x74/t 0x2d/dash 0x6c/l 0x65/e 0x66/f 0x74/t
+_string-shift-right:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "shift-right"
+    0xb/imm32/size
+    0x73/s 0x68/h 0x69/i 0x66/f 0x74/t 0x2d/dash 0x72/r 0x69/i 0x67/g 0x68/h 0x74/t
+_string-shift-right-signed:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "shift-right-signed"
+    0x12/imm32/size
+    0x73/s 0x68/h 0x69/i 0x66/f 0x74/t 0x2d/dash 0x72/r 0x69/i 0x67/g 0x68/h 0x74/t 0x2d/dash 0x73/s 0x69/i 0x67/g 0x6e/n 0x65/e 0x64/d
+
+# string literals for SubX instructions
+_string_01_add_to:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "01/add-to"
+    0x9/imm32/size
+    0x30/0 0x31/1 0x2f/slash 0x61/a 0x64/d 0x64/d 0x2d/dash 0x74/t 0x6f/o
+_string_03_add:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "03/add"
+    0x6/imm32/size
+    0x30/0 0x33/3 0x2f/slash 0x61/a 0x64/d 0x64/d
+_string_05_add_to_eax:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "05/add-to-eax"
+    0xd/imm32/size
+    0x30/0 0x35/5 0x2f/slash 0x61/a 0x64/d 0x64/d 0x2d/dash 0x74/t 0x6f/o 0x2d/dash 0x65/e 0x61/a 0x78/x
+_string_09_or_with:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "09/or-with"
+    0xa/imm32/size
+    0x30/0 0x39/9 0x2f/slash 0x6f/o 0x72/r 0x2d/dash 0x77/w 0x69/i 0x74/t 0x68/h
+_string_0b_or:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "0b/or"
+    0x5/imm32/size
+    0x30/0 0x62/b 0x2f/slash 0x6f/o 0x72/r
+_string_0d_or_with_eax:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "0d/or-with-eax"
+    0xe/imm32/size
+    0x30/0 0x64/d 0x2f/slash 0x6f/o 0x72/r 0x2d/dash 0x77/w 0x69/i 0x74/t 0x68/h 0x2d/dash 0x65/e 0x61/a 0x78/x
+_string_0f_82_jump_label:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "0f 82/jump-if-addr<"
+    0x13/imm32/size
+    0x30/0 0x66/f 0x20/space 0x38/8 0x32/2 0x2f/slash 0x6a/j 0x75/u 0x6d/m 0x70/p 0x2d/dash 0x69/i 0x66/f 0x2d/dash 0x61/a 0x64/d 0x64/d 0x72/r 0x3c/<
+_string_0f_82_jump_break:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "0f 82/jump-if-addr< break/disp32"
+    0x20/imm32/size
+    0x30/0 0x66/f 0x20/space 0x38/8 0x32/2 0x2f/slash 0x6a/j 0x75/u 0x6d/m 0x70/p 0x2d/dash 0x69/i 0x66/f 0x2d/dash 0x61/a 0x64/d 0x64/d 0x72/r 0x3c/< 0x20/space 0x62/b 0x72/r 0x65/e 0x61/a 0x6b/k 0x2f/slash 0x64/d 0x69/i 0x73/s 0x70/p 0x33/3 0x32/2
+_string_0f_82_jump_loop:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "0f 82/jump-if-addr< loop/disp32"
+    0x1f/imm32/size
+    0x30/0 0x66/f 0x20/space 0x38/8 0x32/2 0x2f/slash 0x6a/j 0x75/u 0x6d/m 0x70/p 0x2d/dash 0x69/i 0x66/f 0x2d/dash 0x61/a 0x64/d 0x64/d 0x72/r 0x3c/< 0x20/space 0x6c/l 0x6f/o 0x6f/o 0x70/p 0x2f/slash 0x64/d 0x69/i 0x73/s 0x70/p 0x33/3 0x32/2
+_string_0f_83_jump_label:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "0f 83/jump-if-addr>="
+    0x14/imm32/size
+    0x30/0 0x66/f 0x20/space 0x38/8 0x33/3 0x2f/slash 0x6a/j 0x75/u 0x6d/m 0x70/p 0x2d/dash 0x69/i 0x66/f 0x2d/dash 0x61/a 0x64/d 0x64/d 0x72/r 0x3e/> 0x3d/=
+_string_0f_83_jump_break:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "0f 83/jump-if-addr>= break/disp32"
+    0x21/imm32/size
+    0x30/0 0x66/f 0x20/space 0x38/8 0x33/3 0x2f/slash 0x6a/j 0x75/u 0x6d/m 0x70/p 0x2d/dash 0x69/i 0x66/f 0x2d/dash 0x61/a 0x64/d 0x64/d 0x72/r 0x3e/> 0x3d/= 0x20/space 0x62/b 0x72/r 0x65/e 0x61/a 0x6b/k 0x2f/slash 0x64/d 0x69/i 0x73/s 0x70/p 0x33/3 0x32/2
+_string_0f_83_jump_loop:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "0f 83/jump-if-addr>= loop/disp32"
+    0x20/imm32/size
+    0x30/0 0x66/f 0x20/space 0x38/8 0x33/3 0x2f/slash 0x6a/j 0x75/u 0x6d/m 0x70/p 0x2d/dash 0x69/i 0x66/f 0x2d/dash 0x61/a 0x64/d 0x64/d 0x72/r 0x3e/> 0x3d/= 0x20/space 0x6c/l 0x6f/o 0x6f/o 0x70/p 0x2f/slash 0x64/d 0x69/i 0x73/s 0x70/p 0x33/3 0x32/2
+_string_0f_84_jump_label:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "0f 84/jump-if-="
+    0xf/imm32/size
+    0x30/0 0x66/f 0x20/space 0x38/8 0x34/4 0x2f/slash 0x6a/j 0x75/u 0x6d/m 0x70/p 0x2d/dash 0x69/i 0x66/f 0x2d/dash 0x3d/=
+_string_0f_84_jump_break:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "0f 84/jump-if-= break/disp32"
+    0x1c/imm32/size
+    0x30/0 0x66/f 0x20/space 0x38/8 0x34/4 0x2f/slash 0x6a/j 0x75/u 0x6d/m 0x70/p 0x2d/dash 0x69/i 0x66/f 0x2d/dash 0x3d/= 0x20/space 0x62/b 0x72/r 0x65/e 0x61/a 0x6b/k 0x2f/slash 0x64/d 0x69/i 0x73/s 0x70/p 0x33/3 0x32/2
+_string_0f_84_jump_loop:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "0f 84/jump-if-= loop/disp32"
+    0x1b/imm32/size
+    0x30/0 0x66/f 0x20/space 0x38/8 0x34/4 0x2f/slash 0x6a/j 0x75/u 0x6d/m 0x70/p 0x2d/dash 0x69/i 0x66/f 0x2d/dash 0x3d/= 0x6c/l 0x6f/o 0x6f/o 0x70/p 0x2f/slash 0x64/d 0x69/i 0x73/s 0x70/p 0x33/3 0x32/2
+_string_0f_85_jump_label:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "0f 85/jump-if-!="
+    0x10/imm32/size
+    0x30/0 0x66/f 0x20/space 0x38/8 0x35/5 0x2f/slash 0x6a/j 0x75/u 0x6d/m 0x70/p 0x2d/dash 0x69/i 0x66/f 0x2d/dash 0x21/! 0x3d/=
+_string_0f_85_jump_break:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "0f 85/jump-if-!= break/disp32"
+    0x1d/imm32/size
+    0x30/0 0x66/f 0x20/space 0x38/8 0x35/5 0x2f/slash 0x6a/j 0x75/u 0x6d/m 0x70/p 0x2d/dash 0x69/i 0x66/f 0x2d/dash 0x21/! 0x3d/= 0x20/space 0x62/b 0x72/r 0x65/e 0x61/a 0x6b/k 0x2f/slash 0x64/d 0x69/i 0x73/s 0x70/p 0x33/3 0x32/2
+_string_0f_85_jump_loop:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "0f 85/jump-if-!= loop/disp32"
+    0x1c/imm32/size
+    0x30/0 0x66/f 0x20/space 0x38/8 0x35/5 0x2f/slash 0x6a/j 0x75/u 0x6d/m 0x70/p 0x2d/dash 0x69/i 0x66/f 0x2d/dash 0x21/! 0x3d/= 0x20/space 0x6c/l 0x6f/o 0x6f/o 0x70/p 0x2f/slash 0x64/d 0x69/i 0x73/s 0x70/p 0x33/3 0x32/2
+_string_0f_86_jump_label:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "0f 86/jump-if-addr<="
+    0x14/imm32/size
+    0x30/0 0x66/f 0x20/space 0x38/8 0x36/6 0x2f/slash 0x6a/j 0x75/u 0x6d/m 0x70/p 0x2d/dash 0x69/i 0x66/f 0x2d/dash 0x61/a 0x64/d 0x64/d 0x72/r 0x3c/< 0x3d/=
+_string_0f_86_jump_break:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "0f 86/jump-if-addr<= break/disp32"
+    0x21/imm32/size
+    0x30/0 0x66/f 0x20/space 0x38/8 0x36/6 0x2f/slash 0x6a/j 0x75/u 0x6d/m 0x70/p 0x2d/dash 0x69/i 0x66/f 0x2d/dash 0x61/a 0x64/d 0x64/d 0x72/r 0x3c/< 0x3d/= 0x20/space 0x62/b 0x72/r 0x65/e 0x61/a 0x6b/k 0x2f/slash 0x64/d 0x69/i 0x73/s 0x70/p 0x33/3 0x32/2
+_string_0f_86_jump_loop:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "0f 86/jump-if-addr<= loop/disp32"
+    0x20/imm32/size
+    0x30/0 0x66/f 0x20/space 0x38/8 0x36/6 0x2f/slash 0x6a/j 0x75/u 0x6d/m 0x70/p 0x2d/dash 0x69/i 0x66/f 0x2d/dash 0x61/a 0x64/d 0x64/d 0x72/r 0x3c/< 0x3d/= 0x20/space 0x6c/l 0x6f/o 0x6f/o 0x70/p 0x2f/slash 0x64/d 0x69/i 0x73/s 0x70/p 0x33/3 0x32/2
+_string_0f_87_jump_label:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "0f 87/jump-if-addr>"
+    0x13/imm32/size
+    0x30/0 0x66/f 0x20/space 0x38/8 0x37/7 0x2f/slash 0x6a/j 0x75/u 0x6d/m 0x70/p 0x2d/dash 0x69/i 0x66/f 0x2d/dash 0x61/a 0x64/d 0x64/d 0x72/r 0x3e/>
+_string_0f_87_jump_break:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "0f 87/jump-if-addr> break/disp32"
+    0x20/imm32/size
+    0x30/0 0x66/f 0x20/space 0x38/8 0x37/7 0x2f/slash 0x6a/j 0x75/u 0x6d/m 0x70/p 0x2d/dash 0x69/i 0x66/f 0x2d/dash 0x61/a 0x64/d 0x64/d 0x72/r 0x3e/> 0x20/space 0x62/b 0x72/r 0x65/e 0x61/a 0x6b/k 0x2f/slash 0x64/d 0x69/i 0x73/s 0x70/p 0x33/3 0x32/2
+_string_0f_87_jump_loop:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "0f 87/jump-if-addr> loop/disp32"
+    0x1f/imm32/size
+    0x30/0 0x66/f 0x20/space 0x38/8 0x37/7 0x2f/slash 0x6a/j 0x75/u 0x6d/m 0x70/p 0x2d/dash 0x69/i 0x66/f 0x2d/dash 0x61/a 0x64/d 0x64/d 0x72/r 0x3e/> 0x20/space 0x6c/l 0x6f/o 0x6f/o 0x70/p 0x2f/slash 0x64/d 0x69/i 0x73/s 0x70/p 0x33/3 0x32/2
+_string_0f_8c_jump_label:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "0f 8c/jump-if-<"
+    0xf/imm32/size
+    0x30/0 0x66/f 0x20/space 0x38/8 0x63/c 0x2f/slash 0x6a/j 0x75/u 0x6d/m 0x70/p 0x2d/dash 0x69/i 0x66/f 0x2d/dash 0x3c/<
+_string_0f_8c_jump_break:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "0f 8c/jump-if-< break/disp32"
+    0x1c/imm32/size
+    0x30/0 0x66/f 0x20/space 0x38/8 0x63/c 0x2f/slash 0x6a/j 0x75/u 0x6d/m 0x70/p 0x2d/dash 0x69/i 0x66/f 0x2d/dash 0x3c/< 0x20/space 0x62/b 0x72/r 0x65/e 0x61/a 0x6b/k 0x2f/slash 0x64/d 0x69/i 0x73/s 0x70/p 0x33/3 0x32/2
+_string_0f_8c_jump_loop:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "0f 8c/jump-if-< loop/disp32"
+    0x1b/imm32/size
+    0x30/0 0x66/f 0x20/space 0x38/8 0x63/c 0x2f/slash 0x6a/j 0x75/u 0x6d/m 0x70/p 0x2d/dash 0x69/i 0x66/f 0x2d/dash 0x3c/< 0x20/space 0x6c/l 0x6f/o 0x6f/o 0x70/p 0x2f/slash 0x64/d 0x69/i 0x73/s 0x70/p 0x33/3 0x32/2
+_string_0f_8d_jump_label:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "0f 8d/jump-if->="
+    0x10/imm32/size
+    0x30/0 0x66/f 0x20/space 0x38/8 0x64/d 0x2f/slash 0x6a/j 0x75/u 0x6d/m 0x70/p 0x2d/dash 0x69/i 0x66/f 0x2d/dash 0x3e/> 0x3d/=
+_string_0f_8d_jump_break:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "0f 8d/jump-if->= break/disp32"
+    0x1d/imm32/size
+    0x30/0 0x66/f 0x20/space 0x38/8 0x64/d 0x2f/slash 0x6a/j 0x75/u 0x6d/m 0x70/p 0x2d/dash 0x69/i 0x66/f 0x2d/dash 0x3e/> 0x3d/= 0x20/space 0x62/b 0x72/r 0x65/e 0x61/a 0x6b/k 0x2f/slash 0x64/d 0x69/i 0x73/s 0x70/p 0x33/3 0x32/2
+_string_0f_8d_jump_loop:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "0f 8d/jump-if->= loop/disp32"
+    0x1c/imm32/size
+    0x30/0 0x66/f 0x20/space 0x38/8 0x64/d 0x2f/slash 0x6a/j 0x75/u 0x6d/m 0x70/p 0x2d/dash 0x69/i 0x66/f 0x2d/dash 0x3e/> 0x3d/= 0x20/space 0x6c/l 0x6f/o 0x6f/o 0x70/p 0x2f/slash 0x64/d 0x69/i 0x73/s 0x70/p 0x33/3 0x32/2
+_string_0f_8e_jump_label:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "0f 8e/jump-if-<="
+    0x10/imm32/size
+    0x30/0 0x66/f 0x20/space 0x38/8 0x65/e 0x2f/slash 0x6a/j 0x75/u 0x6d/m 0x70/p 0x2d/dash 0x69/i 0x66/f 0x2d/dash 0x3c/< 0x3d/=
+_string_0f_8e_jump_break:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "0f 8e/jump-if-<= break/disp32"
+    0x1d/imm32/size
+    0x30/0 0x66/f 0x20/space 0x38/8 0x65/e 0x2f/slash 0x6a/j 0x75/u 0x6d/m 0x70/p 0x2d/dash 0x69/i 0x66/f 0x2d/dash 0x3c/< 0x3d/= 0x20/space 0x62/b 0x72/r 0x65/e 0x61/a 0x6b/k 0x2f/slash 0x64/d 0x69/i 0x73/s 0x70/p 0x33/3 0x32/2
+_string_0f_8e_jump_loop:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "0f 8e/jump-if-<= loop/disp32"
+    0x1c/imm32/size
+    0x30/0 0x66/f 0x20/space 0x38/8 0x65/e 0x2f/slash 0x6a/j 0x75/u 0x6d/m 0x70/p 0x2d/dash 0x69/i 0x66/f 0x2d/dash 0x3c/< 0x3d/= 0x20/space 0x6c/l 0x6f/o 0x6f/o 0x70/p 0x2f/slash 0x64/d 0x69/i 0x73/s 0x70/p 0x33/3 0x32/2
+_string_0f_8f_jump_label:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "0f 8f/jump-if->"
+    0xf/imm32/size
+    0x30/0 0x66/f 0x20/space 0x38/8 0x66/f 0x2f/slash 0x6a/j 0x75/u 0x6d/m 0x70/p 0x2d/dash 0x69/i 0x66/f 0x2d/dash 0x3e/>
+_string_0f_8f_jump_break:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "0f 8f/jump-if-> break/disp32"
+    0x1c/imm32/size
+    0x30/0 0x66/f 0x20/space 0x38/8 0x66/f 0x2f/slash 0x6a/j 0x75/u 0x6d/m 0x70/p 0x2d/dash 0x69/i 0x66/f 0x2d/dash 0x3e/> 0x20/space 0x62/b 0x72/r 0x65/e 0x61/a 0x6b/k 0x2f/slash 0x64/d 0x69/i 0x73/s 0x70/p 0x33/3 0x32/2
+_string_0f_8f_jump_loop:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "0f 8f/jump-if-> loop/disp32"
+    0x1b/imm32/size
+    0x30/0 0x66/f 0x20/space 0x38/8 0x66/f 0x2f/slash 0x6a/j 0x75/u 0x6d/m 0x70/p 0x2d/dash 0x69/i 0x66/f 0x2d/dash 0x3e/> 0x20/space 0x6c/l 0x6f/o 0x6f/o 0x70/p 0x2f/slash 0x64/d 0x69/i 0x73/s 0x70/p 0x33/3 0x32/2
+_string_0f_af_multiply:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "0f af/multiply"
+    0xe/imm32/size
+    0x30/0 0x66/f 0x20/space 0x61/a 0x66/f 0x2f/slash 0x6d/m 0x75/u 0x6c/l 0x74/t 0x69/i 0x70/p 0x6c/l 0x79/y
+_string_f3_0f_2a_convert_to_float:
+    0x11/imm32/alloc-id:fake:payload
+    # "f3 0f 2a/convert-to-float"
+    0x19/imm32/size
+    0x66/f 0x33/3 0x20/space 0x30/0 0x66/f 0x20/space 0x32/2 0x61/a 0x2f/slash 0x63/c 0x6f/o 0x6e/n 0x76/v 0x65/e 0x72/r 0x74/t 0x2d/dash 0x74/t 0x6f/o 0x2d/dash 0x66/f 0x6c/l 0x6f/o 0x61/a 0x74/t
+_string_f3_0f_2d_convert_to_int:
+    0x11/imm32/alloc-id:fake:payload
+    # "f3 0f 2d/convert-to-int"
+    0x17/imm32/size
+    0x66/f 0x33/3 0x20/space 0x30/0 0x66/f 0x20/space 0x32/2 0x64/d 0x2f/slash 0x63/c 0x6f/o 0x6e/n 0x76/v 0x65/e 0x72/r 0x74/t 0x2d/dash 0x74/t 0x6f/o 0x2d/dash 0x69/i 0x6e/n 0x74/t
+_string_f3_0f_2c_truncate_to_int:
+    0x11/imm32/alloc-id:fake:payload
+    # "f3 0f 2c/truncate-to-int"
+    0x18/imm32/size
+    0x66/f 0x33/3 0x20/space 0x30/0 0x66/f 0x20/space 0x32/2 0x63/c 0x2f/slash 0x74/t 0x72/r 0x75/u 0x6e/n 0x63/c 0x61/a 0x74/t 0x65/e 0x2d/dash 0x74/t 0x6f/o 0x2d/dash 0x69/i 0x6e/n 0x74/t
+_string_f3_0f_58_add:
+    0x11/imm32/alloc-id:fake:payload
+    # "f3 0f 58/add"
+    0xc/imm32/size
+    0x66/f 0x33/3 0x20/space 0x30/0 0x66/f 0x20/space 0x35/5 0x38/8 0x2f/slash 0x61/a 0x64/d 0x64/d
+_string_f3_0f_5c_subtract:
+    0x11/imm32/alloc-id:fake:payload
+    # "f3 0f 5c/subtract"
+    0x11/imm32/size
+    0x66/f 0x33/3 0x20/space 0x30/0 0x66/f 0x20/space 0x35/5 0x63/c 0x2f/slash 0x73/s 0x75/u 0x62/b 0x74/t 0x72/r 0x61/a 0x63/c 0x74/t
+_string_f3_0f_59_multiply:
+    0x11/imm32/alloc-id:fake:payload
+    # "f3 0f 59/multiply"
+    0x11/imm32/size
+    0x66/f 0x33/3 0x20/space 0x30/0 0x66/f 0x20/space 0x35/5 0x39/9 0x2f/slash 0x6d/m 0x75/u 0x6c/l 0x74/t 0x69/i 0x70/p 0x6c/l 0x79/y
+_string_f3_0f_5e_divide:
+    0x11/imm32/alloc-id:fake:payload
+    # "f3 0f 5e/divide"
+    0xf/imm32/size
+    0x66/f 0x33/3 0x20/space 0x30/0 0x66/f 0x20/space 0x35/5 0x65/e 0x2f/slash 0x64/d 0x69/i 0x76/v 0x69/i 0x64/d 0x65/e
+_string_f3_0f_53_reciprocal:
+    0x11/imm32/alloc-id:fake:payload
+    # "f3 0f 53/reciprocal"
+    0x13/imm32/size
+    0x66/f 0x33/3 0x20/space 0x30/0 0x66/f 0x20/space 0x35/5 0x33/3 0x2f/slash 0x72/r 0x65/e 0x63/c 0x69/i 0x70/p 0x72/r 0x6f/o 0x63/c 0x61/a 0x6c/l
+_string_f3_0f_51_square_root:
+    0x11/imm32/alloc-id:fake:payload
+    # "f3 0f 51/square-root"
+    0x14/imm32/size
+    0x66/f 0x33/3 0x20/space 0x30/0 0x66/f 0x20/space 0x35/5 0x31/1 0x2f/slash 0x73/s 0x71/q 0x75/u 0x61/a 0x72/r 0x65/e 0x2d/dash 0x72/r 0x6f/o 0x6f/o 0x74/t
+_string_f3_0f_52_inverse_square_root:
+    0x11/imm32/alloc-id:fake:payload
+    # "f3 0f 52/inverse-square-root"
+    0x1c/imm32/size
+    0x66/f 0x33/3 0x20/space 0x30/0 0x66/f 0x20/space 0x35/5 0x32/2 0x2f/slash 0x69/i 0x6e/n 0x76/v 0x65/e 0x72/r 0x73/s 0x65/e 0x2d/dash 0x73/s 0x71/q 0x75/u 0x61/a 0x72/r 0x65/e 0x2d/dash 0x72/r 0x6f/o 0x6f/o 0x74/t
+_string_f3_0f_5d_min:
+    0x11/imm32/alloc-id:fake:payload
+    # "f3 0f 5d/min"
+    0xc/imm32/size
+    0x66/f 0x33/3 0x20/space 0x30/0 0x66/f 0x20/space 0x35/5 0x64/d 0x2f/slash 0x6d/m 0x69/i 0x6e/n
+_string_f3_0f_5f_max:
+    0x11/imm32/alloc-id:fake:payload
+    # "f3 0f 5f/max"
+    0xc/imm32/size
+    0x66/f 0x33/3 0x20/space 0x30/0 0x66/f 0x20/space 0x35/5 0x66/f 0x2f/slash 0x6d/m 0x61/a 0x78/x
+_string_f3_0f_10_copy:
+    0x11/imm32/alloc-id:fake:payload
+    # "f3 0f 10/copy"
+    0xd/imm32/size
+    0x66/f 0x33/3 0x20/space 0x30/0 0x66/f 0x20/space 0x31/1 0x30/0 0x2f/slash 0x63/c 0x6f/o 0x70/p 0x79/y
+_string_f3_0f_11_copy:
+    0x11/imm32/alloc-id:fake:payload
+    # "f3 0f 11/copy"
+    0xd/imm32/size
+    0x66/f 0x33/3 0x20/space 0x30/0 0x66/f 0x20/space 0x31/1 0x31/1 0x2f/slash 0x63/c 0x6f/o 0x70/p 0x79/y
+_string_0f_2f_compare:
+    0x11/imm32/alloc-id:fake:payload
+    # "0f 2f/compare"
+    0xd/imm32/size
+    0x30/0 0x66/f 0x20/space 0x32/2 0x66/f 0x2f/slash 0x63/c 0x6f/o 0x6d/m 0x70/p 0x61/a 0x72/r 0x65/e
+_string_21_and_with:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "21/and-with"
+    0xb/imm32/size
+    0x32/2 0x31/1 0x2f/slash 0x61/a 0x6e/n 0x64/d 0x2d/dash 0x77/w 0x69/i 0x74/t 0x68/h
+_string_23_and:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "23/and"
+    0x6/imm32/size
+    0x32/2 0x33/3 0x2f/slash 0x61/a 0x6e/n 0x64/d
+_string_25_and_with_eax:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "25/and-with-eax"
+    0xf/imm32/size
+    0x32/2 0x35/5 0x2f/slash 0x61/a 0x6e/n 0x64/d 0x2d/dash 0x77/w 0x69/i 0x74/t 0x68/h 0x2d/dash 0x65/e 0x61/a 0x78/x
+_string_29_subtract_from:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "29/subtract-from"
+    0x10/imm32/size
+    0x32/2 0x39/9 0x2f/slash 0x73/s 0x75/u 0x62/b 0x74/t 0x72/r 0x61/a 0x63/c 0x74/t 0x2d/dash 0x66/f 0x72/r 0x6f/o 0x6d/m
+_string_2b_subtract:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "2b/subtract"
+    0xb/imm32/size
+    0x32/2 0x62/b 0x2f/slash 0x73/s 0x75/u 0x62/b 0x74/t 0x72/r 0x61/a 0x63/c 0x74/t
+_string_2d_subtract_from_eax:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "2d/subtract-from-eax"
+    0x14/imm32/size
+    0x32/2 0x64/d 0x2f/slash 0x73/s 0x75/u 0x62/b 0x74/t 0x72/r 0x61/a 0x63/c 0x74/t 0x2d/dash 0x66/f 0x72/r 0x6f/o 0x6d/m 0x2d/dash 0x65/e 0x61/a 0x78/x
+_string_31_xor_with:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "31/xor-with"
+    0xb/imm32/size
+    0x33/3 0x31/1 0x2f/slash 0x78/x 0x6f/o 0x72/r 0x2d/dash 0x77/w 0x69/i 0x74/t 0x68/h
+_string_33_xor:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "33/xor"
+    0x6/imm32/size
+    0x33/3 0x33/3 0x2f/slash 0x78/x 0x6f/o 0x72/r
+_string_35_xor_with_eax:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "35/xor-with-eax"
+    0xf/imm32/size
+    0x33/3 0x35/5 0x2f/slash 0x78/x 0x6f/o 0x72/r 0x2d/dash 0x77/w 0x69/i 0x74/t 0x68/h 0x2d/dash 0x65/e 0x61/a 0x78/x
+_string_39_compare->:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "39/compare->"
+    0xc/imm32/size
+    0x33/3 0x39/9 0x2f/slash 0x63/c 0x6f/o 0x6d/m 0x70/p 0x61/a 0x72/r 0x65/e 0x2d/dash 0x3e/>
+_string_3b_compare<-:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "3b/compare<-"
+    0xc/imm32/size
+    0x33/3 0x62/b 0x2f/slash 0x63/c 0x6f/o 0x6d/m 0x70/p 0x61/a 0x72/r 0x65/e 0x3c/< 0x2d/dash
+_string_3d_compare_eax_with:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "3d/compare-eax-with"
+    0x13/imm32/size
+    0x33/3 0x64/d 0x2f/slash 0x63/c 0x6f/o 0x6d/m 0x70/p 0x61/a 0x72/r 0x65/e 0x2d/dash 0x65/e 0x61/a 0x78/x 0x2d/dash 0x77/w 0x69/i 0x74/t 0x68/h
+_string_40_increment_eax:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "40/increment-eax"
+    0x10/imm32/size
+    0x34/4 0x30/0 0x2f/slash 0x69/i 0x6e/n 0x63/c 0x72/r 0x65/e 0x6d/m 0x65/e 0x6e/n 0x74/t 0x2d/dash 0x65/e 0x61/a 0x78/x
+_string_41_increment_ecx:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "41/increment-ecx"
+    0x10/imm32/size
+    0x34/4 0x31/1 0x2f/slash 0x69/i 0x6e/n 0x63/c 0x72/r 0x65/e 0x6d/m 0x65/e 0x6e/n 0x74/t 0x2d/dash 0x65/e 0x63/c 0x78/x
+_string_42_increment_edx:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "42/increment-edx"
+    0x10/imm32/size
+    0x34/4 0x32/2 0x2f/slash 0x69/i 0x6e/n 0x63/c 0x72/r 0x65/e 0x6d/m 0x65/e 0x6e/n 0x74/t 0x2d/dash 0x65/e 0x64/d 0x78/x
+_string_43_increment_ebx:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "43/increment-ebx"
+    0x10/imm32/size
+    0x34/4 0x33/3 0x2f/slash 0x69/i 0x6e/n 0x63/c 0x72/r 0x65/e 0x6d/m 0x65/e 0x6e/n 0x74/t 0x2d/dash 0x65/e 0x62/b 0x78/x
+_string_46_increment_esi:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "46/increment-esi"
+    0x10/imm32/size
+    0x34/4 0x36/6 0x2f/slash 0x69/i 0x6e/n 0x63/c 0x72/r 0x65/e 0x6d/m 0x65/e 0x6e/n 0x74/t 0x2d/dash 0x65/e 0x73/s 0x69/i
+_string_47_increment_edi:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "47/increment-edi"
+    0x10/imm32/size
+    0x34/4 0x37/7 0x2f/slash 0x69/i 0x6e/n 0x63/c 0x72/r 0x65/e 0x6d/m 0x65/e 0x6e/n 0x74/t 0x2d/dash 0x65/e 0x64/d 0x69/i
+_string_48_decrement_eax:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "48/decrement-eax"
+    0x10/imm32/size
+    0x34/4 0x38/8 0x2f/slash 0x64/d 0x65/e 0x63/c 0x72/r 0x65/e 0x6d/m 0x65/e 0x6e/n 0x74/t 0x2d/dash 0x65/e 0x61/a 0x78/x
+_string_49_decrement_ecx:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "49/decrement-ecx"
+    0x10/imm32/size
+    0x34/4 0x39/9 0x2f/slash 0x64/d 0x65/e 0x63/c 0x72/r 0x65/e 0x6d/m 0x65/e 0x6e/n 0x74/t 0x2d/dash 0x65/e 0x63/c 0x78/x
+_string_4a_decrement_edx:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "4a/decrement-edx"
+    0x10/imm32/size
+    0x34/4 0x61/a 0x2f/slash 0x64/d 0x65/e 0x63/c 0x72/r 0x65/e 0x6d/m 0x65/e 0x6e/n 0x74/t 0x2d/dash 0x65/e 0x64/d 0x78/x
+_string_4b_decrement_ebx:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "4b/decrement-ebx"
+    0x10/imm32/size
+    0x34/4 0x62/b 0x2f/slash 0x64/d 0x65/e 0x63/c 0x72/r 0x65/e 0x6d/m 0x65/e 0x6e/n 0x74/t 0x2d/dash 0x65/e 0x62/b 0x78/x
+_string_4e_decrement_esi:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "4e/decrement-esi"
+    0x10/imm32/size
+    0x34/4 0x65/e 0x2f/slash 0x64/d 0x65/e 0x63/c 0x72/r 0x65/e 0x6d/m 0x65/e 0x6e/n 0x74/t 0x2d/dash 0x65/e 0x73/s 0x69/i
+_string_4f_decrement_edi:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "4f/decrement-edi"
+    0x10/imm32/size
+    0x34/4 0x66/f 0x2f/slash 0x64/d 0x65/e 0x63/c 0x72/r 0x65/e 0x6d/m 0x65/e 0x6e/n 0x74/t 0x2d/dash 0x65/e 0x64/d 0x69/i
+_string_81_subop_add:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "81 0/subop/add"
+    0xe/imm32/size
+    0x38/8 0x31/1 0x20/space 0x30/0 0x2f/slash 0x73/s 0x75/u 0x62/b 0x6f/o 0x70/p 0x2f/slash 0x61/a 0x64/d 0x64/d
+_string_81_subop_or:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "81 1/subop/or"
+    0xd/imm32/size
+    0x38/8 0x31/1 0x20/space 0x31/1 0x2f/slash 0x73/s 0x75/u 0x62/b 0x6f/o 0x70/p 0x2f/slash 0x6f/o 0x72/r
+_string_81_subop_and:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "81 4/subop/and"
+    0xe/imm32/size
+    0x38/8 0x31/1 0x20/space 0x34/4 0x2f/slash 0x73/s 0x75/u 0x62/b 0x6f/o 0x70/p 0x2f/slash 0x61/a 0x6e/n 0x64/d
+_string_81_subop_subtract:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "81 5/subop/subtract"
+    0x13/imm32/size
+    0x38/8 0x31/1 0x20/space 0x35/5 0x2f/slash 0x73/s 0x75/u 0x62/b 0x6f/o 0x70/p 0x2f/slash 0x73/s 0x75/u 0x62/b 0x74/t 0x72/r 0x61/a 0x63/c 0x74/t
+_string_81_subop_xor:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "81 6/subop/xor"
+    0xe/imm32/size
+    0x38/8 0x31/1 0x20/space 0x36/6 0x2f/slash 0x73/s 0x75/u 0x62/b 0x6f/o 0x70/p 0x2f/slash 0x78/x 0x6f/o 0x72/r
+_string_81_subop_compare:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "81 7/subop/compare"
+    0x12/imm32/size
+    0x38/8 0x31/1 0x20/space 0x37/7 0x2f/slash 0x73/s 0x75/u 0x62/b 0x6f/o 0x70/p 0x2f/slash 0x63/c 0x6f/o 0x6d/m 0x70/p 0x61/a 0x72/r 0x65/e
+_string_89_<-:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "89/<-"
+    0x5/imm32/size
+    0x38/8 0x39/9 0x2f/slash 0x3c/< 0x2d/dash
+_string_8b_->:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "8b/->"
+    0x5/imm32/size
+    0x38/8 0x62/b 0x2f/slash 0x2d/dash 0x3e/>
+_string_8a_copy_byte:
+    0x11/imm32/alloc-id:fake:payload
+    # "8a/byte->"
+    0x9/imm32/size
+    0x38/8 0x61/a 0x2f// 0x62/b 0x79/y 0x74/t 0x65/e 0x2d/- 0x3e/>
+_string_88_copy_byte:
+    0x11/imm32/alloc-id:fake:payload
+    # "88/byte<-"
+    0x9/imm32/size
+    0x38/8 0x38/8 0x2f// 0x62/b 0x79/y 0x74/t 0x65/e 0x3c/< 0x2d/-
+_string_8d_copy_address:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "8d/copy-address"
+    0xf/imm32/size
+    0x38/8 0x64/d 0x2f/slash 0x63/c 0x6f/o 0x70/p 0x79/y 0x2d/dash 0x61/a 0x64/d 0x64/d 0x72/r 0x65/e 0x73/s 0x73/s
+_string_b8_copy_to_eax:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "b8/copy-to-eax"
+    0xe/imm32/size
+    0x62/b 0x38/8 0x2f/slash 0x63/c 0x6f/o 0x70/p 0x79/y 0x2d/dash 0x74/t 0x6f/o 0x2d/dash 0x65/e 0x61/a 0x78/x
+_string_b9_copy_to_ecx:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "b9/copy-to-ecx"
+    0xe/imm32/size
+    0x62/b 0x39/9 0x2f/slash 0x63/c 0x6f/o 0x70/p 0x79/y 0x2d/dash 0x74/t 0x6f/o 0x2d/dash 0x65/e 0x63/c 0x78/x
+_string_ba_copy_to_edx:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "ba/copy-to-edx"
+    0xe/imm32/size
+    0x62/b 0x61/a 0x2f/slash 0x63/c 0x6f/o 0x70/p 0x79/y 0x2d/dash 0x74/t 0x6f/o 0x2d/dash 0x65/e 0x64/d 0x78/x
+_string_bb_copy_to_ebx:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "bb/copy-to-ebx"
+    0xe/imm32/size
+    0x62/b 0x62/b 0x2f/slash 0x63/c 0x6f/o 0x70/p 0x79/y 0x2d/dash 0x74/t 0x6f/o 0x2d/dash 0x65/e 0x62/b 0x78/x
+_string_be_copy_to_esi:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "be/copy-to-esi"
+    0xe/imm32/size
+    0x62/b 0x65/e 0x2f/slash 0x63/c 0x6f/o 0x70/p 0x79/y 0x2d/dash 0x74/t 0x6f/o 0x2d/dash 0x65/e 0x73/s 0x69/i
+_string_bf_copy_to_edi:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "bf/copy-to-edi"
+    0xe/imm32/size
+    0x62/b 0x66/f 0x2f/slash 0x63/c 0x6f/o 0x70/p 0x79/y 0x2d/dash 0x74/t 0x6f/o 0x2d/dash 0x65/e 0x64/d 0x69/i
+_string_c7_subop_copy:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "c7 0/subop/copy"
+    0xf/imm32/size
+    0x63/c 0x37/7 0x20/space 0x30/0 0x2f/slash 0x73/s 0x75/u 0x62/b 0x6f/o 0x70/p 0x2f/slash 0x63/c 0x6f/o 0x70/p 0x79/y
+_string_e9_jump_label:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "e9/jump"
+    0x7/imm32/size
+    0x65/e 0x39/9 0x2f/slash 0x6a/j 0x75/u 0x6d/m 0x70/p
+_string_e9_jump_break:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "e9/jump break/disp32"
+    0x14/imm32/size
+    0x65/e 0x39/9 0x2f/slash 0x6a/j 0x75/u 0x6d/m 0x70/p 0x20/space 0x62/b 0x72/r 0x65/e 0x61/a 0x6b/k 0x2f/slash 0x64/d 0x69/i 0x73/s 0x70/p 0x33/3 0x32/2
+_string_e9_jump_loop:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "e9/jump loop/disp32"
+    0x13/imm32/size
+    0x65/e 0x39/9 0x2f/slash 0x6a/j 0x75/u 0x6d/m 0x70/p 0x20/space 0x6c/l 0x6f/o 0x6f/o 0x70/p 0x2f/slash 0x64/d 0x69/i 0x73/s 0x70/p 0x33/3 0x32/2
+_string_f7_subop_negate:
+    0x11/imm32/alloc-id:fake:payload
+    # "f7 3/subop/negate"
+    0x11/imm32/size
+    0x66/f 0x37/7 0x20/space 0x33/3 0x2f/slash 0x73/s 0x75/u 0x62/b 0x6f/o 0x70/p 0x2f/slash 0x6e/n 0x65/e 0x67/g 0x61/a 0x74/t 0x65/e
+_string_ff_subop_increment:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "ff 0/subop/increment"
+    0x14/imm32/size
+    0x66/f 0x66/f 0x20/space 0x30/0 0x2f/slash 0x73/s 0x75/u 0x62/b 0x6f/o 0x70/p 0x2f/slash 0x69/i 0x6e/n 0x63/c 0x72/r 0x65/e 0x6d/m 0x65/e 0x6e/n 0x74/t
+_string_ff_subop_decrement:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "ff 1/subop/decrement"
+    0x14/imm32/size
+    0x66/f 0x66/f 0x20/space 0x31/1 0x2f/slash 0x73/s 0x75/u 0x62/b 0x6f/o 0x70/p 0x2f/slash 0x64/d 0x65/e 0x63/c 0x72/r 0x65/e 0x6d/m 0x65/e 0x6e/n 0x74/t
+_string_c1_subop_shift_left:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "c1/shift 4/subop/left"
+    0x15/imm32/size
+    0x63/c 0x31/1 0x2f/slash 0x73/s 0x68/h 0x69/i 0x66/f 0x74/t 0x20/space 0x34/4 0x2f/slash 0x73/s 0x75/u 0x62/b 0x6f/o 0x70/p 0x2f/slash 0x6c/l 0x65/e 0x66/f 0x74/t
+_string_c1_subop_shift_right_padding_zeroes:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "c1/shift 5/subop/right-padding-zeroes"
+    0x25/imm32/size
+    0x63/c 0x31/1 0x2f/slash 0x73/s 0x68/h 0x69/i 0x66/f 0x74/t 0x20/space 0x35/5 0x2f/slash 0x73/s 0x75/u 0x62/b 0x6f/o 0x70/p 0x2f/slash 0x72/r 0x69/i 0x67/g 0x68/h 0x74/t 0x2d/dash 0x70/p 0x61/a 0x64/d 0x64/d 0x69/i 0x6e/n 0x67/g 0x2d/dash 0x7a/z 0x65/e 0x72/r 0x6f/o 0x65/e 0x73/s
+_string_c1_subop_shift_right_preserving_sign:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    # "c1/shift 7/subop/right-preserving-sign"
+    0x26/imm32/size
+    0x63/c 0x31/1 0x2f/slash 0x73/s 0x68/h 0x69/i 0x66/f 0x74/t 0x20/space 0x37/7 0x2f/slash 0x73/s 0x75/u 0x62/b 0x6f/o 0x70/p 0x2f/slash 0x72/r 0x69/i 0x67/g 0x68/h 0x74/t 0x2d/dash 0x70/p 0x72/r 0x65/e 0x73/s 0x65/e 0x72/r 0x76/v 0x69/i 0x6e/n 0x67/g 0x2d/dash 0x73/s 0x69/i 0x67/g 0x6e/n
+
+Single-int-var-in-mem:  # (payload list var)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    Int-var-in-mem/imm32
+    0/imm32/next
+    0/imm32/next
+
+Int-var-in-mem:  # (payload var)
+    0x11/imm32/alloc-id:fake:payload
+    0/imm32/name
+    0/imm32/name
+    0x11/imm32/alloc-id:fake
+    Type-int/imm32
+    1/imm32/some-block-depth
+    1/imm32/some-stack-offset
+    0/imm32/no-register
+    0/imm32/no-register
+
+# Not really legal, but closest we can currently represent a dereference of an (addr byte)
+Single-byte-var-in-mem:  # (payload list var)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    Byte-var-in-mem/imm32
+    0/imm32/next
+    0/imm32/next
+
+# Not really legal, but closest we can currently represent a dereference of an (addr byte)
+Byte-var-in-mem:  # (payload var)
+    0x11/imm32/alloc-id:fake:payload
+    0/imm32/name
+    0/imm32/name
+    0x11/imm32/alloc-id:fake
+    Type-byte/imm32
+    1/imm32/some-block-depth
+    1/imm32/some-stack-offset
+    0/imm32/no-register
+    0/imm32/no-register
+
+Two-args-int-stack-int-reg:  # (payload list var)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    Int-var-in-mem/imm32
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-some-register/imm32/next
+
+Two-int-args-in-regs:  # (payload list var)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    Int-var-in-some-register/imm32
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-some-register/imm32/next
+
+# Not really legal, but closest we can currently represent a dereference of an (addr byte)
+Two-args-byte-stack-byte-reg:  # (payload list var)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    Byte-var-in-mem/imm32
+    0x11/imm32/alloc-id:fake
+    Single-byte-var-in-some-register/imm32/next
+
+Two-args-int-reg-int-stack:  # (payload list var)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    Int-var-in-some-register/imm32
+    0x11/imm32/alloc-id:fake
+    Single-int-var-in-mem/imm32/next
+
+Two-args-int-eax-int-literal:  # (payload list var)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    Int-var-in-eax/imm32
+    0x11/imm32/alloc-id:fake
+    Single-lit-var/imm32/next
+
+Int-var-and-literal:  # (payload list var)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    Int-var-in-mem/imm32
+    0x11/imm32/alloc-id:fake
+    Single-lit-var/imm32/next
+
+Int-var-in-register-and-literal:  # (payload list var)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    Int-var-in-some-register/imm32
+    0x11/imm32/alloc-id:fake
+    Single-lit-var/imm32/next
+
+Two-float-args-in-regs:  # (payload list var)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    Float-var-in-some-register/imm32
+    0x11/imm32/alloc-id:fake
+    Single-float-var-in-some-register/imm32/next
+
+Two-args-float-reg-float-stack:  # (payload list var)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    Float-var-in-some-register/imm32
+    0x11/imm32/alloc-id:fake
+    Single-float-var-in-mem/imm32/next
+
+Two-args-float-stack-float-reg:  # (payload list var)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    Float-var-in-mem/imm32
+    0x11/imm32/alloc-id:fake
+    Single-float-var-in-some-register/imm32/next
+
+Single-int-var-in-some-register:  # (payload list var)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    Int-var-in-some-register/imm32
+    0/imm32/next
+    0/imm32/next
+
+Single-addr-var-in-some-register:  # (payload list var)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    Addr-var-in-some-register/imm32
+    0/imm32/next
+    0/imm32/next
+
+Single-byte-var-in-some-register:  # (payload list var)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    Byte-var-in-some-register/imm32
+    0/imm32/next
+    0/imm32/next
+
+Int-var-in-some-register:  # (payload var)
+    0x11/imm32/alloc-id:fake:payload
+    0/imm32/name
+    0/imm32/name
+    0x11/imm32/alloc-id:fake
+    Type-int/imm32
+    1/imm32/some-block-depth
+    0/imm32/no-stack-offset
+    0x11/imm32/alloc-id:fake
+    Any-register/imm32
+
+Any-register:  # (payload array byte)
+    0x11/imm32/alloc-id:fake:payload
+    1/imm32/size
+    # data
+    2a/asterisk
+
+Addr-var-in-some-register:  # (payload var)
+    0x11/imm32/alloc-id:fake:payload
+    0/imm32/name
+    0/imm32/name
+    0x11/imm32/alloc-id:fake
+    Type-addr/imm32
+    1/imm32/some-block-depth
+    0/imm32/no-stack-offset
+    0x11/imm32/alloc-id:fake
+    Any-register/imm32
+
+Byte-var-in-some-register:  # (payload var)
+    0x11/imm32/alloc-id:fake:payload
+    0/imm32/name
+    0/imm32/name
+    0x11/imm32/alloc-id:fake
+    Type-byte/imm32
+    1/imm32/some-block-depth
+    0/imm32/no-stack-offset
+    0x11/imm32/alloc-id:fake
+    Any-register/imm32
+
+Single-int-var-in-eax:  # (payload list var)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    Int-var-in-eax/imm32
+    0/imm32/next
+    0/imm32/next
+
+Int-var-in-eax:
+    0x11/imm32/alloc-id:fake:payload
+    0/imm32/name
+    0/imm32/name
+    0x11/imm32/alloc-id:fake
+    Type-int/imm32
+    1/imm32/some-block-depth
+    0/imm32/no-stack-offset
+    0x11/imm32/alloc-id:fake
+    $Mu-register-eax/imm32  # can't use Register-eax only to keep our buggy tools/treeshake.cc happy (TODO)
+
+Single-int-var-in-ecx:  # (payload list var)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    Int-var-in-ecx/imm32
+    0/imm32/next
+    0/imm32/next
+
+Int-var-in-ecx:
+    0x11/imm32/alloc-id:fake:payload
+    0/imm32/name
+    0/imm32/name
+    0x11/imm32/alloc-id:fake
+    Type-int/imm32
+    1/imm32/some-block-depth
+    0/imm32/no-stack-offset
+    0x11/imm32/alloc-id:fake
+    $Register-ecx/imm32/register
+
+Single-int-var-in-edx:  # (payload list var)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    Int-var-in-edx/imm32
+    0/imm32/next
+    0/imm32/next
+
+Int-var-in-edx:  # (payload list var)
+    0x11/imm32/alloc-id:fake:payload
+    0/imm32/name
+    0/imm32/name
+    0x11/imm32/alloc-id:fake
+    Type-int/imm32
+    1/imm32/some-block-depth
+    0/imm32/no-stack-offset
+    0x11/imm32/alloc-id:fake
+    $Register-edx/imm32/register
+
+Single-int-var-in-ebx:  # (payload list var)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    Int-var-in-ebx/imm32
+    0/imm32/next
+    0/imm32/next
+
+Int-var-in-ebx:  # (payload list var)
+    0x11/imm32/alloc-id:fake:payload
+    0/imm32/name
+    0/imm32/name
+    0x11/imm32/alloc-id:fake
+    Type-int/imm32
+    1/imm32/some-block-depth
+    0/imm32/no-stack-offset
+    0x11/imm32/alloc-id:fake
+    $Register-ebx/imm32/register
+
+Single-int-var-in-esi:  # (payload list var)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    Int-var-in-esi/imm32
+    0/imm32/next
+    0/imm32/next
+
+Int-var-in-esi:  # (payload list var)
+    0x11/imm32/alloc-id:fake:payload
+    0/imm32/name
+    0/imm32/name
+    0x11/imm32/alloc-id:fake
+    Type-int/imm32
+    1/imm32/some-block-depth
+    0/imm32/no-stack-offset
+    0x11/imm32/alloc-id:fake
+    $Register-esi/imm32/register
+
+Single-int-var-in-edi:  # (payload list var)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    Int-var-in-edi/imm32
+    0/imm32/next
+    0/imm32/next
+
+Int-var-in-edi:  # (payload list var)
+    0x11/imm32/alloc-id:fake:payload
+    0/imm32/name
+    0/imm32/name
+    0x11/imm32/alloc-id:fake
+    Type-int/imm32
+    1/imm32/some-block-depth
+    0/imm32/no-stack-offset
+    0x11/imm32/alloc-id:fake
+    $Register-edi/imm32/register
+
+Single-lit-var:  # (payload list var)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    Lit-var/imm32
+    0/imm32/next
+    0/imm32/next
+
+Lit-var:  # (payload var)
+    0x11/imm32/alloc-id:fake:payload
+    0/imm32/name
+    0/imm32/name
+    0x11/imm32/alloc-id:fake
+    Type-literal/imm32
+    1/imm32/some-block-depth
+    0/imm32/no-stack-offset
+    0/imm32/no-register
+    0/imm32/no-register
+
+Single-float-var-in-mem:  # (payload list var)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    Float-var-in-mem/imm32
+    0/imm32/next
+    0/imm32/next
+
+Float-var-in-mem:  # (payload var)
+    0x11/imm32/alloc-id:fake:payload
+    0/imm32/name
+    0/imm32/name
+    0x11/imm32/alloc-id:fake
+    Type-float/imm32
+    1/imm32/some-block-depth
+    1/imm32/some-stack-offset
+    0/imm32/no-register
+    0/imm32/no-register
+
+Single-float-var-in-some-register:  # (payload list var)
+    0x11/imm32/alloc-id:fake:payload
+    0x11/imm32/alloc-id:fake
+    Float-var-in-some-register/imm32
+    0/imm32/next
+    0/imm32/next
+
+Float-var-in-some-register:  # (payload var)
+    0x11/imm32/alloc-id:fake:payload
+    0/imm32/name
+    0/imm32/name
+    0x11/imm32/alloc-id:fake
+    Type-float/imm32
+    1/imm32/some-block-depth
+    0/imm32/no-stack-offset
+    0x11/imm32/alloc-id:fake
+    Any-register/imm32
+
+Type-int:  # (payload type-tree)
+    0x11/imm32/alloc-id:fake:payload
+    1/imm32/is-atom
+    1/imm32/value:int
+    0/imm32/left:unused
+    0/imm32/right:null
+    0/imm32/right:null
+
+Type-literal:  # (payload type-tree)
+    0x11/imm32/alloc-id:fake:payload
+    1/imm32/is-atom
+    0/imm32/value:literal
+    0/imm32/left:unused
+    0/imm32/right:null
+    0/imm32/right:null
+
+Type-addr:  # (payload type-tree)
+    0x11/imm32/alloc-id:fake:payload
+    1/imm32/is-atom
+    2/imm32/value:addr
+    0/imm32/left:unused
+    0/imm32/right:null
+    0/imm32/right:null
+
+Type-byte:  # (payload type-tree)
+    0x11/imm32/alloc-id:fake:payload
+    1/imm32/is-atom
+    8/imm32/value:byte
+    0/imm32/left:unused
+    0/imm32/right:null
+    0/imm32/right:null
+
+Type-float:  # (payload type-tree)
+    0x11/imm32/alloc-id:fake:payload
+    1/imm32/is-atom
+    0xf/imm32/value:float
+    0/imm32/left:unused
+    0/imm32/right:null
+    0/imm32/right:null
+
+== code
+emit-subx-primitive:  # out: (addr buffered-file), stmt: (addr stmt), primitive: (addr primitive), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    # ecx = primitive
+    8b/-> *(ebp+0x10) 1/r32/ecx
+    # emit primitive name
+    (emit-indent *(ebp+8) *Curr-block-depth)
+    (lookup *(ecx+0x18) *(ecx+0x1c))  # Primitive-subx-name Primitive-subx-name => eax
+    (write-buffered *(ebp+8) %eax)
+    # emit rm32 if necessary
+    (emit-subx-rm32 *(ebp+8) *(ecx+0x20) *(ebp+0xc) *(ebp+0x14) *(ebp+0x18))  # Primitive-subx-rm32
+    # emit xm32 if necessary
+    (emit-subx-rm32 *(ebp+8) *(ecx+0x34) *(ebp+0xc) *(ebp+0x14) *(ebp+0x18))  # Primitive-subx-xm32
+    # emit r32 if necessary
+    (emit-subx-r32 *(ebp+8) *(ecx+0x24) *(ebp+0xc))  # Primitive-subx-r32
+    # emit x32 if necessary
+    (emit-subx-x32 *(ebp+8) *(ecx+0x38) *(ebp+0xc))  # Primitive-subx-x32
+    # emit imm32 if necessary
+    (emit-subx-imm32 *(ebp+8) *(ecx+0x28) *(ebp+0xc))  # Primitive-subx-imm32
+    # emit imm8 if necessary
+    (emit-subx-imm8 *(ebp+8) *(ecx+0x2c) *(ebp+0xc))  # Primitive-subx-imm8
+    # emit disp32 if necessary
+    (emit-subx-disp32 *(ebp+8) *(ecx+0x30) *(ebp+0xc) *(ebp+0x14) *(ebp+0x18))  # Primitive-subx-disp32
+    (write-buffered *(ebp+8) Newline)
+$emit-subx-primitive:end:
+    # . restore registers
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+emit-subx-rm32:  # out: (addr buffered-file), l: arg-location, stmt: (addr stmt), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    # if (l == 0) return
+    81 7/subop/compare *(ebp+0xc) 0/imm32
+    74/jump-if-= $emit-subx-rm32:end/disp8
+    # var v/eax: (addr stmt-var)
+    (get-stmt-operand-from-arg-location *(ebp+0x10) *(ebp+0xc) *(ebp+0x14) *(ebp+0x18))  # => eax
+    (emit-subx-var-as-rm32 *(ebp+8) %eax)
+$emit-subx-rm32:end:
+    # . restore registers
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+get-stmt-operand-from-arg-location:  # stmt: (addr stmt), l: arg-location, err: (addr buffered-file), ed: (addr exit-descriptor) -> var/eax: (addr stmt-var)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    51/push-ecx
+    # eax = l
+    8b/-> *(ebp+0xc) 0/r32/eax
+    # ecx = stmt
+    8b/-> *(ebp+8) 1/r32/ecx
+    # if (l == 1) return stmt->inouts
+    {
+      3d/compare-eax-and 1/imm32
+      75/jump-if-!= break/disp8
+$get-stmt-operand-from-arg-location:1:
+      (lookup *(ecx+0xc) *(ecx+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+      eb/jump $get-stmt-operand-from-arg-location:end/disp8
+    }
+    # if (l == 2) return stmt->inouts->next
+    {
+      3d/compare-eax-and 2/imm32
+      75/jump-if-!= break/disp8
+$get-stmt-operand-from-arg-location:2:
+      (lookup *(ecx+0xc) *(ecx+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+      (lookup *(eax+8) *(eax+0xc))  # Stmt-var-next Stmt-var-next => eax
+      eb/jump $get-stmt-operand-from-arg-location:end/disp8
+    }
+    # if (l == 3) return stmt->outputs
+    {
+      3d/compare-eax-and 3/imm32
+      75/jump-if-!= break/disp8
+$get-stmt-operand-from-arg-location:3:
+      (lookup *(ecx+0x14) *(ecx+0x18))  # Stmt1-outputs Stmt1-outputs => eax
+      eb/jump $get-stmt-operand-from-arg-location:end/disp8
+    }
+    # abort
+    e9/jump $get-stmt-operand-from-arg-location:abort/disp32
+$get-stmt-operand-from-arg-location:end:
+    # . restore registers
+    59/pop-to-ecx
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+$get-stmt-operand-from-arg-location:abort:
+    # error("invalid arg-location " eax)
+    (write-buffered *(ebp+0x10) "invalid arg-location ")
+    (write-int32-hex-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) Newline)
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+emit-subx-r32:  # out: (addr buffered-file), l: arg-location, stmt: (addr stmt)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    # if (l == 0) return
+    81 7/subop/compare *(ebp+0xc) 0/imm32
+    0f 84/jump-if-= $emit-subx-r32:end/disp32
+    # var v/eax: (addr stmt-var)
+    (get-stmt-operand-from-arg-location *(ebp+0x10) *(ebp+0xc))  # => eax
+    (lookup *eax *(eax+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *(eax+0x18) *(eax+0x1c))  # Var-register Var-register => eax
+#?     (write-buffered Stderr "looking up ")
+#?     (write-buffered Stderr %eax)
+#?     (write-buffered Stderr Newline)
+#?     (flush Stderr)
+    (maybe-get Mu-registers %eax 0xc)  # => eax: (addr register-index)
+    (write-buffered *(ebp+8) Space)
+    (write-int32-hex-buffered *(ebp+8) *eax)
+    (write-buffered *(ebp+8) "/r32")
+$emit-subx-r32:end:
+    # . restore registers
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+emit-subx-x32:  # out: (addr buffered-file), l: arg-location, stmt: (addr stmt)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    # if (l == 0) return
+    81 7/subop/compare *(ebp+0xc) 0/imm32
+    0f 84/jump-if-= $emit-subx-x32:end/disp32
+    # var v/eax: (addr stmt-var)
+    (get-stmt-operand-from-arg-location *(ebp+0x10) *(ebp+0xc))  # => eax
+    (lookup *eax *(eax+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *(eax+0x18) *(eax+0x1c))  # Var-register Var-register => eax
+#?     (write-buffered Stderr "looking up ")
+#?     (write-buffered Stderr %eax)
+#?     (write-buffered Stderr Newline)
+#?     (flush Stderr)
+    (maybe-get Mu-registers %eax 0xc)  # => eax: (addr register-index)
+    (write-buffered *(ebp+8) Space)
+    (write-int32-hex-buffered *(ebp+8) *eax)
+    (write-buffered *(ebp+8) "/x32")
+$emit-subx-x32:end:
+    # . restore registers
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+emit-subx-imm32:  # out: (addr buffered-file), l: arg-location, stmt: (addr stmt)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    # if (l == 0) return
+    81 7/subop/compare *(ebp+0xc) 0/imm32
+    0f 84/jump-if-= $emit-subx-imm32:end/disp32
+    # var v/eax: (handle var)
+    (get-stmt-operand-from-arg-location *(ebp+0x10) *(ebp+0xc))  # => eax
+    (lookup *eax *(eax+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *eax *(eax+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+8) Space)
+    (write-buffered *(ebp+8) %eax)
+    (write-buffered *(ebp+8) "/imm32")
+$emit-subx-imm32:end:
+    # . restore registers
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+emit-subx-imm8:  # out: (addr buffered-file), l: arg-location, stmt: (addr stmt)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    # if (l == 0) return
+    81 7/subop/compare *(ebp+0xc) 0/imm32
+    0f 84/jump-if-= $emit-subx-imm32:end/disp32
+    # var v/eax: (handle var)
+    (get-stmt-operand-from-arg-location *(ebp+0x10) *(ebp+0xc))  # => eax
+    (lookup *eax *(eax+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *eax *(eax+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+8) Space)
+    (write-buffered *(ebp+8) %eax)
+    (write-buffered *(ebp+8) "/imm8")
+$emit-subx-imm8:end:
+    # . restore registers
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+emit-subx-disp32:  # out: (addr buffered-file), l: arg-location, stmt: (addr stmt), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    # if (location == 0) return
+    81 7/subop/compare *(ebp+0xc) 0/imm32
+    0f 84/jump-if-= $emit-subx-disp32:end/disp32
+    # var v/eax: (addr stmt-var)
+    (get-stmt-operand-from-arg-location *(ebp+0x10) *(ebp+0xc) *(ebp+0x14) *(ebp+0x18))  # => eax
+    (lookup *eax *(eax+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *eax *(eax+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+8) Space)
+    (write-buffered *(ebp+8) %eax)
+    # hack: if instruction operation starts with "break", emit ":break"
+    # var name/ecx: (addr array byte) = lookup(stmt->operation)
+    8b/-> *(ebp+0x10) 0/r32/eax
+    (lookup *(eax+4) *(eax+8))  # Stmt1-operation Stmt1-operation => eax
+    89/<- %ecx 0/r32/eax
+    {
+      (string-starts-with? %ecx "break")  # => eax
+      3d/compare-eax-and 0/imm32/false
+      74/jump-if-= break/disp8
+      (write-buffered *(ebp+8) ":break")
+    }
+    # hack: if instruction operation starts with "loop", emit ":loop"
+    {
+      (string-starts-with? %ecx "loop")  # => eax
+      3d/compare-eax-and 0/imm32/false
+      74/jump-if-= break/disp8
+      (write-buffered *(ebp+8) ":loop")
+    }
+    (write-buffered *(ebp+8) "/disp32")
+$emit-subx-disp32:end:
+    # . restore registers
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+emit-call:  # out: (addr buffered-file), stmt: (addr stmt)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    #
+    (emit-indent *(ebp+8) *Curr-block-depth)
+    (write-buffered *(ebp+8) "(")
+    # ecx = stmt
+    8b/-> *(ebp+0xc) 1/r32/ecx
+    # - emit function name
+    (lookup *(ecx+4) *(ecx+8))  # Stmt1-operation Stmt1-operation => eax
+    (write-buffered *(ebp+8) %eax)
+    # - emit arguments
+    # var curr/eax: (addr stmt-var) = lookup(stmt->inouts)
+    (lookup *(ecx+0xc) *(ecx+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+    {
+      # if (curr == null) break
+      3d/compare-eax-and 0/imm32
+      74/jump-if-= break/disp8
+      #
+      (emit-subx-call-operand *(ebp+8) %eax)
+      # curr = lookup(curr->next)
+      (lookup *(eax+8) *(eax+0xc))  # Stmt-var-next Stmt-var-next => eax
+      eb/jump loop/disp8
+    }
+    #
+    (write-buffered *(ebp+8) ")\n")
+$emit-call:end:
+    # . restore registers
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+emit-subx-call-operand:  # out: (addr buffered-file), s: (addr stmt-var)
+    # shares code with emit-subx-var-as-rm32
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    56/push-esi
+    # ecx = s
+    8b/-> *(ebp+0xc) 1/r32/ecx
+    # var operand/esi: (addr var) = lookup(s->value)
+    (lookup *ecx *(ecx+4))  # Stmt-var-value Stmt-var-value => eax
+    89/<- %esi 0/r32/eax
+    # if (operand->register && !s->is-deref?) emit "%__"
+    {
+$emit-subx-call-operand:check-for-register-direct:
+      81 7/subop/compare *(esi+0x18) 0/imm32  # Var-register
+      74/jump-if-= break/disp8
+      81 7/subop/compare *(ecx+0x10) 0/imm32/false  # Stmt-var-is-deref
+      75/jump-if-!= break/disp8
+$emit-subx-call-operand:register-direct:
+      (write-buffered *(ebp+8) " %")
+      (lookup *(esi+0x18) *(esi+0x1c))  # Var-register Var-register => eax
+      (write-buffered *(ebp+8) %eax)
+      e9/jump $emit-subx-call-operand:end/disp32
+    }
+    # else if (operand->register && s->is-deref?) emit "*__"
+    {
+$emit-subx-call-operand:check-for-register-indirect:
+      81 7/subop/compare *(esi+0x18) 0/imm32  # Var-register
+      74/jump-if-= break/disp8
+      81 7/subop/compare *(ecx+0x10) 0/imm32/false  # Stmt-var-is-deref
+      74/jump-if-= break/disp8
+$emit-subx-call-operand:register-indirect:
+      (emit-subx-call-operand-register-indirect *(ebp+8) %esi)
+      e9/jump $emit-subx-call-operand:end/disp32
+    }
+    # else if (operand->stack-offset) emit "*(ebp+__)"
+    {
+      81 7/subop/compare *(esi+0x14) 0/imm32  # Var-offset
+      74/jump-if-= break/disp8
+$emit-subx-call-operand:stack:
+      (emit-subx-call-operand-stack *(ebp+8) %esi)
+      e9/jump $emit-subx-call-operand:end/disp32
+    }
+    # else if (operand->type == literal) emit "__"
+    {
+      (lookup *(esi+8) *(esi+0xc))  # Var-type Var-type => eax
+      81 7/subop/compare *(eax+4) 0/imm32  # Type-tree-value
+      75/jump-if-!= break/disp8
+$emit-subx-call-operand:literal:
+      (write-buffered *(ebp+8) Space)
+      (lookup *esi *(esi+4))  # Var-name Var-name => eax
+      (write-buffered *(ebp+8) %eax)
+      e9/jump $emit-subx-call-operand:end/disp32
+    }
+    # else if (operand->type == literal-string) emit "__"
+    {
+      (lookup *(esi+8) *(esi+0xc))  # Var-type Var-type => eax
+      81 7/subop/compare *(eax+4) 0x10/imm32  # Type-tree-value
+      75/jump-if-!= break/disp8
+$emit-subx-call-operand:literal-string:
+      (write-buffered *(ebp+8) Space)
+      (lookup *esi *(esi+4))  # Var-name Var-name => eax
+      (write-buffered *(ebp+8) %eax)
+    }
+$emit-subx-call-operand:end:
+    # . restore registers
+    5e/pop-to-esi
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+emit-subx-call-operand-register-indirect:  # out: (addr buffered-file), v: (addr var)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    56/push-esi
+    # esi = v
+    8b/-> *(ebp+0xc) 6/r32/esi
+    # var size/ecx: int = size-of-deref(v)
+    (size-of-deref %esi)  # => eax
+    89/<- %ecx 0/r32/eax
+    # var reg-name/esi: (addr array byte) = lookup(v->register)
+    (lookup *(esi+0x18) *(esi+0x1c))  # Var-register Var-register => eax
+    89/<- %esi 0/r32/eax
+    # TODO: assert size is a multiple of 4
+    # var i/eax: int = 0
+    b8/copy-to-eax 0/imm32
+    {
+$emit-subx-call-operand-register-indirect:loop:
+      # if (i >= size) break
+      39/compare %eax 1/r32/ecx
+      7d/jump-if->= break/disp8
+      # emit " *(" v->register "+" i ")"
+      (write-buffered *(ebp+8) " *(")
+      (write-buffered *(ebp+8) %esi)
+      (write-buffered *(ebp+8) "+")
+      (write-int32-hex-buffered *(ebp+8) %eax)
+      (write-buffered *(ebp+8) ")")
+      # i += 4
+      05/add-to-eax 4/imm32
+      #
+      eb/jump loop/disp8
+    }
+$emit-subx-call-operand-register-indirect:end:
+    # . restore registers
+    5e/pop-to-esi
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+emit-subx-call-operand-stack:  # out: (addr buffered-file), v: (addr var)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    56/push-esi
+    # esi = v
+    8b/-> *(ebp+0xc) 6/r32/esi
+    # var curr/ecx: int = v->offset
+    8b/-> *(esi+0x14) 1/r32/ecx  # Var-offset
+    # var max/eax: int = v->offset + size-of(v)
+    (size-of %esi)  # => eax
+    # TODO: assert size is a multiple of 4
+    01/add-to %eax 1/r32/ecx
+    {
+$emit-subx-call-operand-stack:loop:
+      # if (curr >= max) break
+      39/compare %ecx 0/r32/eax
+      7d/jump-if->= break/disp8
+      # emit " *(ebp+" curr ")"
+      (write-buffered *(ebp+8) " *(ebp+")
+      (write-int32-hex-buffered *(ebp+8) %ecx)
+      (write-buffered *(ebp+8) ")")
+      # i += 4
+      81 0/subop/add %ecx 4/imm32
+      #
+      eb/jump loop/disp8
+    }
+$emit-subx-call-operand-stack:end:
+    # . restore registers
+    5e/pop-to-esi
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+emit-subx-var-as-rm32:  # out: (addr buffered-file), s: (addr stmt-var)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    56/push-esi
+    # ecx = s
+    8b/-> *(ebp+0xc) 1/r32/ecx
+    # var operand/esi: (addr var) = lookup(s->value)
+    (lookup *ecx *(ecx+4))  # Stmt-var-value Stmt-var-value => eax
+    89/<- %esi 0/r32/eax
+    # if (operand->register && s->is-deref?) emit "*__"
+    {
+$emit-subx-var-as-rm32:check-for-register-indirect:
+      81 7/subop/compare *(esi+0x18) 0/imm32  # Var-register
+      74/jump-if-= break/disp8
+      81 7/subop/compare *(ecx+0x10) 0/imm32/false  # Stmt-var-is-deref
+      74/jump-if-= break/disp8
+$emit-subx-var-as-rm32:register-indirect:
+      (write-buffered *(ebp+8) " *")
+      (lookup *(esi+0x18) *(esi+0x1c))  # Var-register Var-register => eax
+      (write-buffered *(ebp+8) %eax)
+      e9/jump $emit-subx-var-as-rm32:end/disp32
+    }
+    # if (operand->register && !s->is-deref?) emit "%__"
+    {
+$emit-subx-var-as-rm32:check-for-register-direct:
+      81 7/subop/compare *(esi+0x18) 0/imm32  # Var-register
+      74/jump-if-= break/disp8
+      81 7/subop/compare *(ecx+0x10) 0/imm32/false  # Stmt-var-is-deref
+      75/jump-if-!= break/disp8
+$emit-subx-var-as-rm32:register-direct:
+      (write-buffered *(ebp+8) " %")
+      (lookup *(esi+0x18) *(esi+0x1c))  # Var-register Var-register => eax
+      (write-buffered *(ebp+8) %eax)
+      e9/jump $emit-subx-var-as-rm32:end/disp32
+    }
+    # else if (operand->stack-offset) emit "*(ebp+__)"
+    {
+      81 7/subop/compare *(esi+0x14) 0/imm32  # Var-offset
+      74/jump-if-= break/disp8
+$emit-subx-var-as-rm32:stack:
+      (write-buffered *(ebp+8) Space)
+      (write-buffered *(ebp+8) "*(ebp+")
+      (write-int32-hex-buffered *(ebp+8) *(esi+0x14))  # Var-offset
+      (write-buffered *(ebp+8) ")")
+    }
+$emit-subx-var-as-rm32:end:
+    # . restore registers
+    5e/pop-to-esi
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+find-matching-primitive:  # primitives: (addr primitive), stmt: (addr stmt) -> result/eax: (addr primitive)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    51/push-ecx
+    # var curr/ecx: (addr primitive) = primitives
+    8b/-> *(ebp+8) 1/r32/ecx
+    {
+$find-matching-primitive:loop:
+      # if (curr == null) break
+      81 7/subop/compare %ecx 0/imm32
+      74/jump-if-= break/disp8
+      # if match(curr, stmt) return curr
+      {
+        (mu-stmt-matches-primitive? *(ebp+0xc) %ecx)  # => eax
+        3d/compare-eax-and 0/imm32/false
+        74/jump-if-= break/disp8
+        89/<- %eax 1/r32/ecx
+        eb/jump $find-matching-primitive:end/disp8
+      }
+$find-matching-primitive:next-primitive:
+      # curr = curr->next
+      (lookup *(ecx+0x3c) *(ecx+0x40))  # Primitive-next Primitive-next => eax
+      89/<- %ecx 0/r32/eax
+      #
+      e9/jump loop/disp32
+    }
+    # return null
+    b8/copy-to-eax 0/imm32
+$find-matching-primitive:end:
+    # . restore registers
+    59/pop-to-ecx
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+mu-stmt-matches-primitive?:  # stmt: (addr stmt), primitive: (addr primitive) -> result/eax: boolean
+    # A mu stmt matches a primitive if the name matches, all the inout vars
+    # match, and all the output vars match.
+    # Vars match if types match and registers match.
+    # In addition, a stmt output matches a primitive's output if types match
+    # and the primitive has a wildcard register.
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    51/push-ecx
+    52/push-edx
+    53/push-ebx
+    56/push-esi
+    57/push-edi
+    # ecx = stmt
+    8b/-> *(ebp+8) 1/r32/ecx
+    # edx = primitive
+    8b/-> *(ebp+0xc) 2/r32/edx
+    {
+$mu-stmt-matches-primitive?:check-name:
+      # if (primitive->name != stmt->operation) return false
+      # . var esi: (addr array byte) = lookup(stmt->operation)
+      (lookup *(ecx+4) *(ecx+8))  # Stmt1-operation Stmt1-operation => eax
+      89/<- %esi 0/r32/eax
+      # . var edi: (addr array byte) = lookup(primitive->name)
+      (lookup *edx *(edx+4))  # Primitive-name Primitive-name => eax
+#?       (write-buffered Stderr %eax)
+#?       (write-buffered Stderr Newline)
+#?       (flush Stderr)
+      89/<- %edi 0/r32/eax
+      (string-equal? %esi %edi)  # => eax
+      3d/compare-eax-and 0/imm32/false
+      75/jump-if-!= break/disp8
+      b8/copy-to-eax 0/imm32
+      e9/jump $mu-stmt-matches-primitive?:end/disp32
+    }
+    # var curr/esi: (addr stmt-var) = lookup(stmt->inouts)
+    (lookup *(ecx+0xc) *(ecx+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+    89/<- %esi 0/r32/eax
+    # var curr2/edi: (addr list var) = lookup(primitive->inouts)
+    (lookup *(edx+8) *(edx+0xc))  # Primitive-inouts Primitive-inouts => eax
+    89/<- %edi 0/r32/eax
+    {
+$mu-stmt-matches-primitive?:inouts-loop:
+      # if (curr == 0 && curr2 == 0) move on to check outputs
+      {
+$mu-stmt-matches-primitive?:check-both-inouts-null:
+        81 7/subop/compare %esi 0/imm32
+        75/jump-if-!= break/disp8
+$mu-stmt-matches-primitive?:stmt-inout-null:
+        81 7/subop/compare %edi 0/imm32
+        0f 84/jump-if-= $mu-stmt-matches-primitive?:check-outputs/disp32
+$mu-stmt-matches-primitive?:stmt-inout-null-and-prim-inout-not-null:
+        # return false
+        b8/copy-to-eax 0/imm32/false
+        e9/jump $mu-stmt-matches-primitive?:end/disp32
+      }
+      # if (curr2 == 0) return false
+      {
+$mu-stmt-matches-primitive?:check-prim-inout-null:
+        81 7/subop/compare %edi 0/imm32
+        75/jump-if-!= break/disp8
+$mu-stmt-matches-primitive?:prim-inout-null:
+        b8/copy-to-eax 0/imm32/false
+        e9/jump $mu-stmt-matches-primitive?:end/disp32
+      }
+      # if (curr != curr2) return false
+      {
+$mu-stmt-matches-primitive?:check-inouts-match:
+        (lookup *edi *(edi+4))  # List-value List-value => eax
+        (operand-matches-primitive? %esi %eax)  # => eax
+        3d/compare-eax-and 0/imm32/false
+        75/jump-if-!= break/disp8
+$mu-stmt-matches-primitive?:inouts-match:
+        b8/copy-to-eax 0/imm32/false
+        e9/jump $mu-stmt-matches-primitive?:end/disp32
+      }
+$mu-stmt-matches-primitive?:next-inout:
+      # curr = lookup(curr->next)
+      (lookup *(esi+8) *(esi+0xc))  # Stmt-var-next Stmt-var-next => eax
+      89/<- %esi 0/r32/eax
+      # curr2 = lookup(curr2->next)
+      (lookup *(edi+8) *(edi+0xc))  # List-next List-next => eax
+      89/<- %edi 0/r32/eax
+      #
+      e9/jump loop/disp32
+    }
+$mu-stmt-matches-primitive?:check-outputs:
+    # var curr/esi: (addr stmt-var) = lookup(stmt->outputs)
+    (lookup *(ecx+0x14) *(ecx+0x18))  # Stmt1-outputs Stmt1-outputs => eax
+    89/<- %esi 0/r32/eax
+    # var curr2/edi: (addr list var) = lookup(primitive->outputs)
+    (lookup *(edx+0x10) *(edx+0x14))  # Primitive-outputs Primitive-outputs => eax
+    89/<- %edi 0/r32/eax
+    {
+$mu-stmt-matches-primitive?:outputs-loop:
+      # if (curr == 0) return (curr2 == 0)
+      {
+$mu-stmt-matches-primitive?:check-both-outputs-null:
+        81 7/subop/compare %esi 0/imm32
+        75/jump-if-!= break/disp8
+        {
+$mu-stmt-matches-primitive?:stmt-output-null:
+          81 7/subop/compare %edi 0/imm32
+          75/jump-if-!= break/disp8
+$mu-stmt-matches-primitive?:both-outputs-null:
+          # return true
+          b8/copy-to-eax 1/imm32
+          e9/jump $mu-stmt-matches-primitive?:end/disp32
+        }
+$mu-stmt-matches-primitive?:stmt-output-null-and-prim-output-not-null:
+        # return false
+        b8/copy-to-eax 0/imm32
+        e9/jump $mu-stmt-matches-primitive?:end/disp32
+      }
+      # if (curr2 == 0) return false
+      {
+$mu-stmt-matches-primitive?:check-prim-output-null:
+        81 7/subop/compare %edi 0/imm32
+        75/jump-if-!= break/disp8
+$mu-stmt-matches-primitive?:prim-output-is-null:
+        b8/copy-to-eax 0/imm32
+        e9/jump $mu-stmt-matches-primitive?:end/disp32
+      }
+      # if (curr != curr2) return false
+      {
+$mu-stmt-matches-primitive?:check-outputs-match:
+        (lookup *edi *(edi+4))  # List-value List-value => eax
+        (operand-matches-primitive? %esi %eax)  # => eax
+        3d/compare-eax-and 0/imm32/false
+        75/jump-if-!= break/disp8
+$mu-stmt-matches-primitive?:outputs-match:
+        b8/copy-to-eax 0/imm32
+        e9/jump $mu-stmt-matches-primitive?:end/disp32
+      }
+$mu-stmt-matches-primitive?:next-output:
+      # curr = lookup(curr->next)
+      (lookup *(esi+8) *(esi+0xc))  # Stmt-var-next Stmt-var-next => eax
+      89/<- %esi 0/r32/eax
+      # curr2 = lookup(curr2->next)
+      (lookup *(edi+8) *(edi+0xc))  # List-next List-next => eax
+      89/<- %edi 0/r32/eax
+      #
+      e9/jump loop/disp32
+    }
+$mu-stmt-matches-primitive?:return-true:
+    b8/copy-to-eax 1/imm32
+$mu-stmt-matches-primitive?:end:
+    # . restore registers
+    5f/pop-to-edi
+    5e/pop-to-esi
+    5b/pop-to-ebx
+    5a/pop-to-edx
+    59/pop-to-ecx
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+operand-matches-primitive?:  # s: (addr stmt-var), prim-var: (addr var) -> result/eax: boolean
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    51/push-ecx
+    52/push-edx
+    53/push-ebx
+    56/push-esi
+    57/push-edi
+    # ecx = s
+    8b/-> *(ebp+8) 1/r32/ecx
+    # var var/esi: (addr var) = lookup(s->value)
+    (lookup *ecx *(ecx+4))  # Stmt-var-value Stmt-var-value => eax
+    89/<- %esi 0/r32/eax
+    # edi = prim-var
+    8b/-> *(ebp+0xc) 7/r32/edi
+$operand-matches-primitive?:check-type:
+    # if !category-match?(var->type, prim-var->type) return false
+    # . var vtype/ebx: (addr type-tree) = lookup(var->type)
+    (lookup *(esi+8) *(esi+0xc))  # Var-type Var-type => eax
+    89/<- %ebx 0/r32/eax
+    # . if s is deref, vtype = vtype->right
+    {
+      81 7/subop/compare *(ecx+0x10) 0/imm32/false  # Stmt-var-is-deref
+      74/jump-if-= break/disp8
+$operand-matches-primitive?:is-deref:
+      # . var t/eax: (addr type)
+      (lookup *(ebx+0xc) *(ebx+0x10))  # Type-tree-right Type-tree-right => eax
+      # . if !t->is-atom? t = t->left
+      81 7/subop/compare *eax 0/imm32/false
+      {
+        75/jump-if-!= break/disp8
+        (lookup *(eax+4) *(eax+8))  # Type-tree-left Type-tree-left => eax
+      }
+      # .
+      89/<- %ebx 0/r32/eax
+    }
+    # . var ptype/eax: (addr type-tree) = lookup(prim-var->type)
+    (lookup *(edi+8) *(edi+0xc))  # Var-type Var-type => eax
+    (subx-type-category-match? %ebx %eax)  # => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 84/jump-if-= $operand-matches-primitive?:return-false/disp32
+    {
+$operand-matches-primitive?:check-register:
+      # if prim-var is in memory and var is in register but dereference, match
+      {
+        81 7/subop/compare *(edi+0x18) 0/imm32  # Var-register
+        0f 85/jump-if-!= break/disp32
+        81 7/subop/compare *(esi+0x18) 0/imm32  # Var-register
+        74/jump-if-= break/disp8
+        81 7/subop/compare *(ecx+0x10) 0/imm32/false  # Stmt-var-is-deref
+        74/jump-if-= break/disp8
+$operand-matches-primitive?:var-deref-match:
+        e9/jump $operand-matches-primitive?:return-true/disp32
+      }
+      # if prim-var is in register and var is in register but dereference, no match
+      {
+        81 7/subop/compare *(edi+0x18) 0/imm32  # Var-register
+        0f 84/jump-if-= break/disp32
+        81 7/subop/compare *(esi+0x18) 0/imm32  # Var-register
+        0f 84/jump-if-= break/disp32
+        81 7/subop/compare *(ecx+0x10) 0/imm32/false  # Stmt-var-is-deref
+        74/jump-if-= break/disp8
+$operand-matches-primitive?:var-deref-no-match:
+        e9/jump $operand-matches-primitive?:return-false/disp32
+      }
+      # return false if var->register doesn't match prim-var->register
+      {
+        # if register addresses are equal, it's a match
+        # var vreg/ebx: (addr array byte) = lookup(var->register)
+        (lookup *(esi+0x18) *(esi+0x1c))  # Var-register Var-register => eax
+        89/<- %ebx 0/r32/eax
+        # var preg/ecx: (addr array byte) = lookup(prim-var->register)
+        (lookup *(edi+0x18) *(edi+0x1c))  # Var-register Var-register => eax
+        89/<- %ecx 0/r32/eax
+        # if (vreg == preg) break
+        39/compare %ecx 3/r32/ebx
+        74/jump-if-= break/disp8
+$operand-matches-primitive?:var-register-no-match:
+        # if either address is 0, return false
+        81 7/subop/compare %ebx 0/imm32
+        74/jump-if-=  $operand-matches-primitive?:return-false/disp8
+        81 7/subop/compare %ecx 0/imm32
+        74/jump-if-=  $operand-matches-primitive?:return-false/disp8
+        # if prim-var->register is wildcard, it's a match
+        (string-equal? %ecx "*")  # Any-register => eax
+        3d/compare-eax-and 0/imm32/false
+        75/jump-if-!= break/disp8
+$operand-matches-primitive?:wildcard-no-match:
+        # if string contents aren't equal, return false
+        (string-equal? %ecx %ebx)  # => eax
+        3d/compare-eax-and 0/imm32/false
+        74/jump-if-= $operand-matches-primitive?:return-false/disp8
+      }
+    }
+$operand-matches-primitive?:return-true:
+    b8/copy-to-eax 1/imm32/true
+    eb/jump $operand-matches-primitive?:end/disp8
+$operand-matches-primitive?:return-false:
+    b8/copy-to-eax 0/imm32/false
+$operand-matches-primitive?:end:
+    # . restore registers
+    5f/pop-to-edi
+    5e/pop-to-esi
+    5b/pop-to-ebx
+    5a/pop-to-edx
+    59/pop-to-ecx
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+find-matching-function:  # functions: (addr function), stmt: (addr stmt) -> result/eax: (addr function)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    51/push-ecx
+    # var curr/ecx: (handle function) = functions
+    8b/-> *(ebp+8) 1/r32/ecx
+    {
+      # if (curr == null) break
+      81 7/subop/compare %ecx 0/imm32
+      74/jump-if-= break/disp8
+#?       (write-buffered Stderr "iter\n")
+#?       (flush Stderr)
+      # if match(stmt, curr) return curr
+      {
+        (mu-stmt-matches-function? *(ebp+0xc) %ecx)  # => eax
+        3d/compare-eax-and 0/imm32/false
+        74/jump-if-= break/disp8
+        89/<- %eax 1/r32/ecx
+        eb/jump $find-matching-function:end/disp8
+      }
+      # curr = curr->next
+      (lookup *(ecx+0x20) *(ecx+0x24))  # Function-next Function-next => eax
+      89/<- %ecx 0/r32/eax
+      #
+      eb/jump loop/disp8
+    }
+    # return null
+    b8/copy-to-eax 0/imm32
+$find-matching-function:end:
+    # . restore registers
+    59/pop-to-ecx
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+# Just compare names; user-defined functions don't support overloading yet.
+mu-stmt-matches-function?:  # stmt: (addr stmt1), function: (addr function) -> result/eax: boolean
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    51/push-ecx
+    # return function->name == stmt->operation
+    # ecx = lookup(stmt->operation)
+    8b/-> *(ebp+8) 0/r32/eax
+    (lookup *(eax+4) *(eax+8))  # Stmt1-operation Stmt1-operation => eax
+    89/<- %ecx 0/r32/eax
+    # eax = lookup(function->name)
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # Function-name Function-name => eax
+    (string-equal? %eax %ecx)  # => eax
+$mu-stmt-matches-function?:end:
+    # . restore registers
+    59/pop-to-ecx
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+# Type-checking happens elsewhere. This method is for selecting between
+# primitives.
+subx-type-category-match?:  # a: (addr type-tree), b: (addr type-tree) -> result/eax: boolean
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    51/push-ecx
+    # var cata/ecx: int = type-category(a)
+    (type-category *(ebp+8))  # => eax
+    89/<- %ecx 0/r32/eax
+    # var catb/eax: int = type-category(b)
+    (type-category *(ebp+0xc))  # => eax
+    # return cata == catb
+    39/compare %eax 1/r32/ecx
+    0f 94/set-byte-if-= %al
+    81 4/subop/and %eax 0xff/imm32
+$subx-type-category-match?:end:
+    # . restore registers
+    59/pop-to-ecx
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+type-category:  # a: (addr type-tree) -> result/eax: int
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    51/push-ecx
+    # var lit?/ecx: boolean = is-literal-type?(a)
+    (is-simple-mu-type? *(ebp+8) 0)  # literal => eax
+    89/<- %ecx 0/r32/eax
+    # var float?/eax: int = is-float?(a)
+    (is-simple-mu-type? *(ebp+8) 0xf)  # => eax
+    # set bits for lit? and float?
+    c1/shift 4/subop/left %ecx 1/imm8
+    09/or %eax 1/r32/ecx
+$type-category:end:
+    # . restore registers
+    59/pop-to-ecx
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+is-simple-mu-type?:  # a: (addr type-tree), n: type-id -> result/eax: boolean
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    51/push-ecx
+    # ecx = n
+    8b/-> *(ebp+0xc) 1/r32/ecx
+    # return (a->value == n)
+    8b/-> *(ebp+8) 0/r32/eax
+    39/compare *(eax+4) 1/r32/ecx  # Type-tree-value
+    0f 94/set-byte-if-= %al
+    81 4/subop/and %eax 0xff/imm32
+$is-simple-mu-type?:end:
+    # . restore registers
+    59/pop-to-ecx
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+is-mu-addr-type?:  # a: (addr type-tree) -> result/eax: boolean
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # eax = a
+    8b/-> *(ebp+8) 0/r32/eax
+    # if (!a->is-atom?) a = a->left
+    81 7/subop/compare *eax 0/imm32/false  # Type-tree-is-atom
+    {
+      75/jump-if-!= break/disp8
+      (lookup *(eax+4) *(eax+8))  # Type-tree-left Type-tree-left => eax
+    }
+    # return (a->value == addr)
+    81 7/subop/compare *(eax+4) 2/imm32/addr  # Type-tree-value
+    0f 94/set-byte-if-= %al
+    81 4/subop/and %eax 0xff/imm32
+$is-mu-addr-type?:end:
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+is-mu-array-type?:  # a: (addr type-tree) -> result/eax: boolean
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # eax = a
+    8b/-> *(ebp+8) 0/r32/eax
+    # if (!a->is-atom?) a = a->left
+    81 7/subop/compare *eax 0/imm32/false  # Type-tree-is-atom
+    {
+      75/jump-if-!= break/disp8
+      (lookup *(eax+4) *(eax+8))  # Type-tree-left Type-tree-left => eax
+    }
+    # return (a->value == array)
+    81 7/subop/compare *(eax+4) 3/imm32/array  # Type-tree-value
+    0f 94/set-byte-if-= %al
+    81 4/subop/and %eax 0xff/imm32
+$is-mu-array-type?:end:
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+is-mu-string-type?:  # a: (addr type-tree) -> result/eax: boolean
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    56/push-esi
+    # esi = a
+    8b/-> *(ebp+8) 6/r32/esi
+    # if (a->is-atom?) return false
+    81 7/subop/compare *esi 0/imm32/false  # Type-tree-is-atom
+    0f 85/jump-if-!= $is-mu-string-type?:return-false/disp32
+    # if a is not an addr, return false
+    (is-mu-addr-type? %esi)  # => eax
+    3d/compare-eax-with 0/imm32/false
+    0f 84/jump-if-= $is-mu-string-type?:end/disp32  # eax changes var
+    # if a is not an array, return false
+    (lookup *(esi+0xc) *(esi+0x10))  # Type-tree-right Type-tree-right => eax
+    (is-mu-array-type? %eax)  # => eax
+    3d/compare-eax-with 0/imm32/false
+    74/jump-if-= $is-mu-string-type?:end/disp8  # eax changes var
+    # var p/eax: (addr type-tree) = payload of a
+    (lookup *(esi+0xc) *(esi+0x10))  # Type-tree-right Type-tree-right => eax
+    (lookup *(eax+0xc) *(eax+0x10))  # Type-tree-right Type-tree-right => eax
+    # if p is an atom, return false
+    81 7/subop/compare *eax 0/imm32/false  # Type-tree-is-atom
+    75/jump-if-!= $is-mu-string-type?:return-false/disp8
+    # return (p == byte)
+    (lookup *(eax+4) *(eax+8))  # Type-tree-left Type-tree-left => eax
+    (is-simple-mu-type? %eax 8)  # byte => eax
+    eb/jump $is-mu-string-type?:end/disp8
+$is-mu-string-type?:return-false:
+    b8/copy-to-eax 0/imm32/false
+$is-mu-string-type?:end:
+    # . restore registers
+    5e/pop-to-esi
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+is-mu-stream-type?:  # a: (addr type-tree) -> result/eax: boolean
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # eax = a
+    8b/-> *(ebp+8) 0/r32/eax
+    # if (!a->is-atom?) a = a->left
+    81 7/subop/compare *eax 0/imm32/false  # Type-tree-is-atom
+    {
+      75/jump-if-!= break/disp8
+      (lookup *(eax+4) *(eax+8))  # Type-tree-left Type-tree-left => eax
+    }
+    # return (a->value == stream)
+    81 7/subop/compare *(eax+4) 0xb/imm32/stream  # Type-tree-value
+    0f 94/set-byte-if-= %al
+    81 4/subop/and %eax 0xff/imm32
+$is-mu-stream-type?:end:
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-emit-subx-stmt-primitive:
+    # Primitive operation on a variable on the stack.
+    #   increment foo
+    # =>
+    #   ff 0/subop/increment *(ebp-8)
+    #
+    # There's a variable on the var stack as follows:
+    #   name: 'foo'
+    #   type: int
+    #   stack-offset: -8
+    #
+    # There's a primitive with this info:
+    #   name: 'increment'
+    #   inouts: int/mem
+    #   value: 'ff 0/subop/increment'
+    #
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    # simulate allocated payloads starting with an initial fake alloc-id (0x11)
+$test-emit-subx-stmt-primitive:initialize-type:
+    # var type/ecx: (payload type-tree) = int
+    68/push 0/imm32/right:null
+    68/push 0/imm32/right:null
+    68/push 0/imm32/left:unused
+    68/push 1/imm32/value:int
+    68/push 1/imm32/is-atom?:true
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %ecx 4/r32/esp
+$test-emit-subx-stmt-primitive:initialize-var:
+    # var var-foo/ecx: (payload var) = var(type)
+    68/push 0/imm32/no-register
+    68/push 0/imm32/no-register
+    68/push -8/imm32/stack-offset
+    68/push 1/imm32/block-depth
+    51/push-ecx/type
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/name
+    68/push 0/imm32/name
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %ecx 4/r32/esp
+$test-emit-subx-stmt-primitive:initialize-var-name:
+    # var-foo->name = "foo"
+    8d/copy-address *(ecx+4) 0/r32/eax  # Var-name + 4
+    (copy-array Heap "foo" %eax)
+$test-emit-subx-stmt-primitive:initialize-stmt-var:
+    # var operand/ebx: (payload stmt-var) = stmt-var(var-foo)
+    68/push 0/imm32/is-deref:false
+    68/push 0/imm32/next
+    68/push 0/imm32/next
+    51/push-ecx/var-foo
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %ebx 4/r32/esp
+$test-emit-subx-stmt-primitive:initialize-stmt:
+    # var stmt/esi: (addr statement)
+    68/push 0/imm32/no-outputs
+    68/push 0/imm32/no-outputs
+    53/push-ebx/inouts
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/operation
+    68/push 0/imm32/operation
+    68/push 1/imm32/tag
+    89/<- %esi 4/r32/esp
+$test-emit-subx-stmt-primitive:initialize-stmt-operation:
+    # stmt->operation = "increment"
+    8d/copy-address *(esi+4) 0/r32/eax  # Stmt1-operation
+    (copy-array Heap "increment" %eax)
+$test-emit-subx-stmt-primitive:initialize-primitive:
+    # var primitives/ebx: (addr primitive)
+    68/push 0/imm32/next
+    68/push 0/imm32/next
+    68/push 0/imm32/no-x32
+    68/push 0/imm32/no-xm32
+    68/push 0/imm32/no-disp32
+    68/push 0/imm32/no-imm8
+    68/push 0/imm32/no-imm32
+    68/push 0/imm32/no-r32
+    68/push 1/imm32/rm32-is-first-inout
+    68/push 0/imm32/subx-name
+    68/push 0/imm32/subx-name
+    68/push 0/imm32/no-outputs
+    68/push 0/imm32/no-outputs
+    53/push-ebx/inouts  # hack: reuse stmt-var from call stmt as (list var) in function declaration
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/name
+    68/push 0/imm32/name
+    89/<- %ebx 4/r32/esp
+$test-emit-subx-stmt-primitive:initialize-primitive-name:
+    # primitives->name = "increment"
+    (copy-array Heap "increment" %ebx)  # Primitive-name
+$test-emit-subx-stmt-primitive:initialize-primitive-subx-name:
+    # primitives->subx-name = "ff 0/subop/increment"
+    8d/copy-address *(ebx+0x18) 0/r32/eax  # Primitive-subx-name
+    (copy-array Heap "ff 0/subop/increment" %eax)
+    # convert
+    c7 0/subop/copy *Curr-block-depth 0/imm32
+    (emit-subx-stmt _test-output-buffered-file %esi %ebx 0 Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment *(ebp+0xfffffff8)" "F - test-emit-subx-stmt-primitive")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-emit-subx-stmt-primitive-register:
+    # Primitive operation on a variable in a register.
+    #   foo <- increment
+    # =>
+    #   ff 0/subop/increment %eax  # sub-optimal, but should suffice
+    #
+    # There's a variable on the var stack as follows:
+    #   name: 'foo'
+    #   type: int
+    #   register: 'eax'
+    #
+    # There's a primitive with this info:
+    #   name: 'increment'
+    #   out: int/reg
+    #   value: 'ff 0/subop/increment'
+    #
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+$test-emit-subx-stmt-primitive-register:initialize-type:
+    # var type/ecx: (payload type-tree) = int
+    68/push 0/imm32/right:null
+    68/push 0/imm32/right:null
+    68/push 0/imm32/left:unused
+    68/push 1/imm32/value:int
+    68/push 1/imm32/is-atom?:true
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %ecx 4/r32/esp
+$test-emit-subx-stmt-primitive-register:initialize-var:
+    # var var-foo/ecx: (payload var)
+    68/push 0/imm32/register
+    68/push 0/imm32/register
+    68/push 0/imm32/no-stack-offset
+    68/push 1/imm32/block-depth
+    51/push-ecx
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/name
+    68/push 0/imm32/name
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %ecx 4/r32/esp
+$test-emit-subx-stmt-primitive-register:initialize-var-name:
+    # var-foo->name = "foo"
+    8d/copy-address *(ecx+4) 0/r32/eax  # Var-name + 4
+    (copy-array Heap "foo" %eax)
+$test-emit-subx-stmt-primitive-register:initialize-var-register:
+    # var-foo->register = "eax"
+    8d/copy-address *(ecx+0x1c) 0/r32/eax  # Var-register + 4
+    (copy-array Heap "eax" %eax)
+$test-emit-subx-stmt-primitive-register:initialize-stmt-var:
+    # var operand/ebx: (payload stmt-var)
+    68/push 0/imm32/is-deref:false
+    68/push 0/imm32/next
+    68/push 0/imm32/next
+    51/push-ecx/var-foo
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %ebx 4/r32/esp
+$test-emit-subx-stmt-primitive-register:initialize-stmt:
+    # var stmt/esi: (addr statement)
+    53/push-ebx/outputs
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/no-inouts
+    68/push 0/imm32/no-inouts
+    68/push 0/imm32/operation
+    68/push 0/imm32/operation
+    68/push 1/imm32
+    89/<- %esi 4/r32/esp
+$test-emit-subx-stmt-primitive-register:initialize-stmt-operation:
+    # stmt->operation = "increment"
+    8d/copy-address *(esi+4) 0/r32/eax  # Stmt1-operation
+    (copy-array Heap "increment" %eax)
+$test-emit-subx-stmt-primitive-register:initialize-formal-var:
+    # var formal-var/ebx: (payload var)
+    68/push 0/imm32/register
+    68/push 0/imm32/register
+    68/push 0/imm32/no-stack-offset
+    68/push 1/imm32/block-depth
+    ff 6/subop/push *(ecx+0x10)  # Var-type + payload alloc id + handle alloc id
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/name
+    68/push 0/imm32/name
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %ebx 4/r32/esp
+$test-emit-subx-stmt-primitive-register:initialize-formal-var-name:
+    # formal-var->name = "dummy"
+    8d/copy-address *(ebx+4) 0/r32/eax  # Var-name + 4
+    (copy-array Heap "dummy" %eax)
+$test-emit-subx-stmt-primitive-register:initialize-formal-register:
+    # formal-var->register = "*"
+    8d/copy-address *(ebx+0x1c) 0/r32/eax  # Var-register + 4
+    (copy-array Heap "*" %eax)  # Any-register
+$test-emit-subx-stmt-primitive-register:initialize-var-list:
+    # var formal-outputs/ebx: (payload list var)
+    68/push 0/imm32/next
+    68/push 0/imm32/next
+    53/push-ebx/formal-var
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %ebx 4/r32/esp
+$test-emit-subx-stmt-primitive-register:initialize-primitive:
+    # var primitives/ebx: (addr primitive)
+    68/push 0/imm32/next
+    68/push 0/imm32/next
+    68/push 0/imm32/no-x32
+    68/push 0/imm32/no-xm32
+    68/push 0/imm32/no-disp32
+    68/push 0/imm32/no-imm8
+    68/push 0/imm32/no-imm32
+    68/push 0/imm32/no-r32
+    68/push 3/imm32/rm32-is-first-output
+    68/push 0/imm32/subx-name
+    68/push 0/imm32/subx-name
+    53/push-ebx/outputs
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/no-inouts
+    68/push 0/imm32/no-inouts
+    68/push 0/imm32/name
+    68/push 0/imm32/name
+    89/<- %ebx 4/r32/esp
+$test-emit-subx-stmt-primitive-register:initialize-primitive-name:
+    # primitives->name = "increment"
+    (copy-array Heap "increment" %ebx)  # Primitive-name
+$test-emit-subx-stmt-primitive-register:initialize-primitive-subx-name:
+    # primitives->subx-name = "ff 0/subop/increment"
+    8d/copy-address *(ebx+0x18) 0/r32/eax  # Primitive-subx-name
+    (copy-array Heap "ff 0/subop/increment" %eax)
+    # convert
+    c7 0/subop/copy *Curr-block-depth 0/imm32
+    (emit-subx-stmt _test-output-buffered-file %esi %ebx 0 Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment %eax" "F - test-emit-subx-stmt-primitive-register")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-emit-subx-stmt-select-primitive:
+    # Select the right primitive between overloads.
+    #   foo <- increment
+    # =>
+    #   ff 0/subop/increment %eax  # sub-optimal, but should suffice
+    #
+    # There's a variable on the var stack as follows:
+    #   name: 'foo'
+    #   type: int
+    #   register: 'eax'
+    #
+    # There's two primitives, as follows:
+    #   - name: 'increment'
+    #     out: int/reg
+    #     value: 'ff 0/subop/increment'
+    #   - name: 'increment'
+    #     inout: int/mem
+    #     value: 'ff 0/subop/increment'
+    #
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+$test-emit-subx-stmt-select-primitive:initialize-type:
+    # var type/ecx: (payload type-tree) = int
+    68/push 0/imm32/right:null
+    68/push 0/imm32/right:null
+    68/push 0/imm32/left:unused
+    68/push 1/imm32/value:int
+    68/push 1/imm32/is-atom?:true
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %ecx 4/r32/esp
+$test-emit-subx-stmt-select-primitive:initialize-var:
+    # var var-foo/ecx: (payload var)
+    68/push 0/imm32/register
+    68/push 0/imm32/register
+    68/push 0/imm32/no-stack-offset
+    68/push 1/imm32/block-depth
+    51/push-ecx
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/name
+    68/push 0/imm32/name
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %ecx 4/r32/esp
+$test-emit-subx-stmt-select-primitive:initialize-var-name:
+    # var-foo->name = "foo"
+    8d/copy-address *(ecx+4) 0/r32/eax  # Var-name + 4
+    (copy-array Heap "foo" %eax)
+$test-emit-subx-stmt-select-primitive:initialize-var-register:
+    # var-foo->register = "eax"
+    8d/copy-address *(ecx+0x1c) 0/r32/eax  # Var-register + 4
+    (copy-array Heap "eax" %eax)
+$test-emit-subx-stmt-select-primitive:initialize-stmt-var:
+    # var operand/ebx: (payload stmt-var)
+    68/push 0/imm32/is-deref:false
+    68/push 0/imm32/next
+    68/push 0/imm32/next
+    51/push-ecx/var-foo
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %ebx 4/r32/esp
+$test-emit-subx-stmt-select-primitive:initialize-stmt:
+    # var stmt/esi: (addr statement)
+    53/push-ebx/outputs
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/no-inouts
+    68/push 0/imm32/no-inouts
+    68/push 0/imm32/operation
+    68/push 0/imm32/operation
+    68/push 1/imm32
+    89/<- %esi 4/r32/esp
+$test-emit-subx-stmt-select-primitive:initialize-stmt-operation:
+    # stmt->operation = "increment"
+    8d/copy-address *(esi+4) 0/r32/eax  # Stmt1-operation
+    (copy-array Heap "increment" %eax)
+$test-emit-subx-stmt-select-primitive:initialize-formal-var:
+    # var formal-var/ebx: (payload var)
+    68/push 0/imm32/register
+    68/push 0/imm32/register
+    68/push 0/imm32/no-stack-offset
+    68/push 1/imm32/block-depth
+    ff 6/subop/push *(ecx+0x10)  # Var-type + payload alloc id + handle alloc id
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/name
+    68/push 0/imm32/name
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %ebx 4/r32/esp
+$test-emit-subx-stmt-select-primitive:initialize-formal-var-name:
+    # formal-var->name = "dummy"
+    8d/copy-address *(ebx+4) 0/r32/eax  # Var-name + 4
+    (copy-array Heap "dummy" %eax)
+$test-emit-subx-stmt-select-primitive:initialize-formal-register:
+    # formal-var->register = "*"
+    8d/copy-address *(ebx+0x1c) 0/r32/eax  # Var-register + 4
+    (copy-array Heap "*" %eax)  # Any-register
+$test-emit-subx-stmt-select-primitive:initialize-var-list:
+    # var formal-outputs/ebx: (payload list var)
+    68/push 0/imm32/next
+    68/push 0/imm32/next
+    53/push-ebx/formal-var
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %ebx 4/r32/esp
+$test-emit-subx-stmt-select-primitive:initialize-primitive2:
+    # var primitive2/edi: (payload primitive)
+    68/push 0/imm32/next
+    68/push 0/imm32/next
+    68/push 0/imm32/no-x32
+    68/push 0/imm32/no-xm32
+    68/push 0/imm32/no-disp32
+    68/push 0/imm32/no-imm8
+    68/push 0/imm32/no-imm32
+    68/push 0/imm32/no-r32
+    68/push 3/imm32/rm32-is-first-output
+    68/push 0/imm32/subx-name
+    68/push 0/imm32/subx-name
+    53/push-ebx/outputs
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/no-inouts
+    68/push 0/imm32/no-inouts
+    68/push 0/imm32/name
+    68/push 0/imm32/name
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %edi 4/r32/esp
+$test-emit-subx-stmt-select-primitive:initialize-primitive2-name:
+    # primitives->name = "increment"
+    8d/copy-address *(edi+4) 0/r32/eax  # Primitive-name + 4
+    (copy-array Heap "increment" %eax)
+$test-emit-subx-stmt-select-primitive:initialize-primitive2-subx-name:
+    # primitives->subx-name = "ff 0/subop/increment"
+    8d/copy-address *(edi+0x1c) 0/r32/eax  # Primitive-subx-name + 4
+    (copy-array Heap "ff 0/subop/increment" %eax)
+$test-emit-subx-stmt-select-primitive:initialize-primitive:
+    # var primitives/ebx: (addr primitive)
+    57/push-edi
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/no-x32
+    68/push 0/imm32/no-xm32
+    68/push 0/imm32/no-disp32
+    68/push 0/imm32/no-imm8
+    68/push 0/imm32/no-imm32
+    68/push 0/imm32/no-r32
+    68/push 1/imm32/rm32-is-first-inout
+    68/push 0/imm32/subx-name
+    68/push 0/imm32/subx-name
+    68/push 0/imm32/no-outputs
+    68/push 0/imm32/no-outputs
+    53/push-ebx/inouts  # hack: reuse stmt-var from call stmt as (list var) in function declaration
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/name
+    68/push 0/imm32/name
+    89/<- %ebx 4/r32/esp
+$test-emit-subx-stmt-select-primitive:initialize-primitive-name:
+    # primitives->name = "increment"
+    (copy-array Heap "increment" %ebx)  # Primitive-name
+$test-emit-subx-stmt-select-primitive:initialize-primitive-subx-name:
+    # primitives->subx-name = "ff 0/subop/increment"
+    8d/copy-address *(ebx+0x18) 0/r32/eax  # Primitive-subx-name
+    (copy-array Heap "ff 0/subop/increment" %eax)
+    # convert
+    c7 0/subop/copy *Curr-block-depth 0/imm32
+    (emit-subx-stmt _test-output-buffered-file %esi %ebx 0 Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment %eax" "F - test-emit-subx-stmt-select-primitive")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-emit-subx-stmt-select-primitive-2:
+    # Select the right primitive between overloads.
+    #   increment foo
+    # =>
+    #   ff 0/subop/increment %eax  # sub-optimal, but should suffice
+    #
+    # There's a variable on the var stack as follows:
+    #   name: 'foo'
+    #   type: int
+    #   register: 'eax'
+    #
+    # There's two primitives, as follows:
+    #   - name: 'increment'
+    #     out: int/reg
+    #     value: 'ff 0/subop/increment'
+    #   - name: 'increment'
+    #     inout: int/mem
+    #     value: 'ff 0/subop/increment'
+    #
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+$test-emit-subx-stmt-select-primitive-2:initialize-type:
+    # var type/ecx: (payload type-tree) = int
+    68/push 0/imm32/right:null
+    68/push 0/imm32/right:null
+    68/push 0/imm32/left:unused
+    68/push 1/imm32/value:int
+    68/push 1/imm32/is-atom?:true
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %ecx 4/r32/esp
+$test-emit-subx-stmt-select-primitive-2:initialize-var:
+    # var var-foo/ecx: (payload var)
+    68/push 0/imm32/register
+    68/push 0/imm32/register
+    68/push 0/imm32/no-stack-offset
+    68/push 1/imm32/block-depth
+    51/push-ecx
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/name
+    68/push 0/imm32/name
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %ecx 4/r32/esp
+$test-emit-subx-stmt-select-primitive-2:initialize-var-name:
+    # var-foo->name = "foo"
+    8d/copy-address *(ecx+4) 0/r32/eax  # Var-name + 4
+    (copy-array Heap "foo" %eax)
+$test-emit-subx-stmt-select-primitive-2:initialize-var-register:
+    # var-foo->register = "eax"
+    8d/copy-address *(ecx+0x1c) 0/r32/eax  # Var-register + 4
+    (copy-array Heap "eax" %eax)
+$test-emit-subx-stmt-select-primitive-2:initialize-stmt-var:
+    # var operand/ebx: (payload stmt-var)
+    68/push 0/imm32/is-deref:false
+    68/push 0/imm32/next
+    68/push 0/imm32/next
+    51/push-ecx/var-foo
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %ebx 4/r32/esp
+$test-emit-subx-stmt-select-primitive-2:initialize-stmt:
+    # var stmt/esi: (addr statement)
+    68/push 0/imm32/no-outputs
+    68/push 0/imm32/no-outputs
+    53/push-ebx/inouts
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/operation
+    68/push 0/imm32/operation
+    68/push 1/imm32
+    89/<- %esi 4/r32/esp
+$test-emit-subx-stmt-select-primitive-2:initialize-stmt-operation:
+    # stmt->operation = "increment"
+    8d/copy-address *(esi+4) 0/r32/eax  # Stmt1-operation
+    (copy-array Heap "increment" %eax)
+$test-emit-subx-stmt-select-primitive-2:initialize-formal-var:
+    # var formal-var/ebx: (payload var)
+    68/push 0/imm32/register
+    68/push 0/imm32/register
+    68/push 0/imm32/no-stack-offset
+    68/push 1/imm32/block-depth
+    ff 6/subop/push *(ecx+0x10)  # Var-type + payload alloc id + handle alloc id
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/name
+    68/push 0/imm32/name
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %ebx 4/r32/esp
+$test-emit-subx-stmt-select-primitive-2:initialize-formal-var-name:
+    # formal-var->name = "dummy"
+    8d/copy-address *(ebx+4) 0/r32/eax  # Var-name + 4
+    (copy-array Heap "dummy" %eax)
+$test-emit-subx-stmt-select-primitive-2:initialize-formal-register:
+    # formal-var->register = "*"
+    8d/copy-address *(ebx+0x1c) 0/r32/eax  # Var-register + 4
+    (copy-array Heap "*" %eax)  # Any-register
+$test-emit-subx-stmt-select-primitive-2:initialize-var-list:
+    # var formal-outputs/ebx: (payload list stmt-var)
+    68/push 0/imm32/next
+    68/push 0/imm32/next
+    53/push-ebx/formal-var
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %ebx 4/r32/esp
+$test-emit-subx-stmt-select-primitive-2:initialize-primitive2:
+    # var primitive2/edi: (payload primitive)
+    68/push 0/imm32/next
+    68/push 0/imm32/next
+    68/push 0/imm32/no-x32
+    68/push 0/imm32/no-xm32
+    68/push 0/imm32/no-disp32
+    68/push 0/imm32/no-imm8
+    68/push 0/imm32/no-imm32
+    68/push 0/imm32/no-r32
+    68/push 3/imm32/rm32-is-first-output
+    68/push 0/imm32/subx-name
+    68/push 0/imm32/subx-name
+    53/push-ebx/outputs
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/no-inouts
+    68/push 0/imm32/no-inouts
+    68/push 0/imm32/name
+    68/push 0/imm32/name
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %edi 4/r32/esp
+$test-emit-subx-stmt-select-primitive-2:initialize-primitive2-name:
+    # primitives->name = "increment"
+    8d/copy-address *(edi+4) 0/r32/eax  # Primitive-name + 4
+    (copy-array Heap "increment" %eax)
+$test-emit-subx-stmt-select-primitive-2:initialize-primitive2-subx-name:
+    # primitives->subx-name = "ff 0/subop/increment"
+    8d/copy-address *(edi+0x1c) 0/r32/eax  # Primitive-subx-name + 4
+    (copy-array Heap "ff 0/subop/increment" %eax)
+$test-emit-subx-stmt-select-primitive-2:initialize-primitive:
+    # var primitives/ebx: (addr primitive)
+    57/push-edi
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/no-x32
+    68/push 0/imm32/no-xm32
+    68/push 0/imm32/no-disp32
+    68/push 0/imm32/no-imm8
+    68/push 0/imm32/no-imm32
+    68/push 0/imm32/no-r32
+    68/push 1/imm32/rm32-is-first-inout
+    68/push 0/imm32/subx-name
+    68/push 0/imm32/subx-name
+    68/push 0/imm32/no-outputs
+    68/push 0/imm32/no-outputs
+    53/push-ebx/inouts  # hack: reuse stmt-var from call stmt as (list var) in function declaration
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/name
+    68/push 0/imm32/name
+    89/<- %ebx 4/r32/esp
+$test-emit-subx-stmt-select-primitive-2:initialize-primitive-name:
+    # primitives->name = "increment"
+    (copy-array Heap "increment" %ebx)  # Primitive-name
+$test-emit-subx-stmt-select-primitive-2:initialize-primitive-subx-name:
+    # primitives->subx-name = "ff 0/subop/increment"
+    8d/copy-address *(ebx+0x18) 0/r32/eax  # Primitive-subx-name
+    (copy-array Heap "ff 0/subop/increment" %eax)
+    # convert
+    c7 0/subop/copy *Curr-block-depth 0/imm32
+    (emit-subx-stmt _test-output-buffered-file %esi %ebx 0 Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment %eax" "F - test-emit-subx-stmt-select-primitive-2")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-increment-register:
+    # Select the right register between overloads.
+    #   foo <- increment
+    # =>
+    #   50/increment-eax
+    #
+    # There's a variable on the var stack as follows:
+    #   name: 'foo'
+    #   type: int
+    #   register: 'eax'
+    #
+    # Primitives are the global definitions.
+    #
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+$test-increment-register:initialize-type:
+    # var type/ecx: (payload type-tree) = int
+    68/push 0/imm32/right:null
+    68/push 0/imm32/right:null
+    68/push 0/imm32/left:unused
+    68/push 1/imm32/value:int
+    68/push 1/imm32/is-atom?:true
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %ecx 4/r32/esp
+$test-increment-register:initialize-var:
+    # var var-foo/ecx: (payload var)
+    68/push 0/imm32/register
+    68/push 0/imm32/register
+    68/push 0/imm32/no-stack-offset
+    68/push 1/imm32/block-depth
+    51/push-ecx
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/name
+    68/push 0/imm32/name
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %ecx 4/r32/esp
+$test-increment-register:initialize-var-name:
+    # var-foo->name = "foo"
+    8d/copy-address *(ecx+4) 0/r32/eax  # Var-name + 4
+    (copy-array Heap "foo" %eax)
+$test-increment-register:initialize-var-register:
+    # var-foo->register = "eax"
+    8d/copy-address *(ecx+0x1c) 0/r32/eax  # Var-register + 4
+    (copy-array Heap "eax" %eax)
+$test-increment-register:initialize-stmt-var:
+    # var operand/ebx: (payload stmt-var)
+    68/push 0/imm32/is-deref:false
+    68/push 0/imm32/next
+    68/push 0/imm32/next
+    51/push-ecx/var-foo
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %ebx 4/r32/esp
+$test-increment-register:initialize-stmt:
+    # var stmt/esi: (addr statement)
+    53/push-ebx/outputs
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/no-inouts
+    68/push 0/imm32/no-inouts
+    68/push 0/imm32/operation
+    68/push 0/imm32/operation
+    68/push 1/imm32
+    89/<- %esi 4/r32/esp
+$test-increment-register:initialize-stmt-operation:
+    # stmt->operation = "increment"
+    8d/copy-address *(esi+4) 0/r32/eax  # Stmt1-operation
+    (copy-array Heap "increment" %eax)
+    # convert
+    c7 0/subop/copy *Curr-block-depth 0/imm32
+    (emit-subx-stmt _test-output-buffered-file %esi Primitives 0 Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "40/increment-eax" "F - test-increment-register")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-add-reg-to-reg:
+    #   var1/reg <- add var2/reg
+    # =>
+    #   01/add-to %var1 var2
+    #
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+$test-add-reg-to-reg:initialize-type:
+    # var type/ecx: (payload type-tree) = int
+    68/push 0/imm32/right:null
+    68/push 0/imm32/right:null
+    68/push 0/imm32/left:unused
+    68/push 1/imm32/value:int
+    68/push 1/imm32/is-atom?:true
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %ecx 4/r32/esp
+$test-add-reg-to-reg:initialize-var1:
+    # var var1/ecx: (payload var)
+    68/push 0/imm32/register
+    68/push 0/imm32/register
+    68/push 0/imm32/no-stack-offset
+    68/push 1/imm32/block-depth
+    51/push-ecx
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/name
+    68/push 0/imm32/name
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %ecx 4/r32/esp
+$test-add-reg-to-reg:initialize-var1-name:
+    # var1->name = "var1"
+    8d/copy-address *(ecx+4) 0/r32/eax  # Var-name + 4
+    (copy-array Heap "var1" %eax)
+$test-add-reg-to-reg:initialize-var1-register:
+    # var1->register = "eax"
+    8d/copy-address *(ecx+0x1c) 0/r32/eax  # Var-register + 4
+    (copy-array Heap "eax" %eax)
+$test-add-reg-to-reg:initialize-var2:
+    # var var2/edx: (payload var)
+    68/push 0/imm32/register
+    68/push 0/imm32/register
+    68/push 0/imm32/no-stack-offset
+    68/push 1/imm32/block-depth
+    ff 6/subop/push *(ecx+0x10)
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/name
+    68/push 0/imm32/name
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %edx 4/r32/esp
+$test-add-reg-to-reg:initialize-var2-name:
+    # var2->name = "var2"
+    8d/copy-address *(edx+4) 0/r32/eax  # Var-name + 4
+    (copy-array Heap "var2" %eax)
+$test-add-reg-to-reg:initialize-var2-register:
+    # var2->register = "ecx"
+    8d/copy-address *(edx+0x1c) 0/r32/eax  # Var-register + 4
+    (copy-array Heap "ecx" %eax)
+$test-add-reg-to-reg:initialize-inouts:
+    # var inouts/esi: (payload stmt-var) = [var2]
+    68/push 0/imm32/is-deref:false
+    68/push 0/imm32/next
+    68/push 0/imm32/next
+    52/push-edx/var2
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %esi 4/r32/esp
+$test-add-reg-to-reg:initialize-outputs:
+    # var outputs/edi: (payload stmt-var) = [var1]
+    68/push 0/imm32/is-deref:false
+    68/push 0/imm32/next
+    68/push 0/imm32/next
+    51/push-ecx/var1
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %edi 4/r32/esp
+$test-add-reg-to-reg:initialize-stmt:
+    # var stmt/esi: (addr statement)
+    68/push 0/imm32/next
+    68/push 0/imm32/next
+    57/push-edi/outputs
+    68/push 0x11/imm32/alloc-id:fake
+    56/push-esi/inouts
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/operation
+    68/push 0/imm32/operation
+    68/push 1/imm32/tag:stmt1
+    89/<- %esi 4/r32/esp
+$test-add-reg-to-reg:initialize-stmt-operation:
+    # stmt->operation = "add"
+    8d/copy-address *(esi+4) 0/r32/eax  # Stmt1-operation
+    (copy-array Heap "add" %eax)
+    # convert
+    c7 0/subop/copy *Curr-block-depth 0/imm32
+    (emit-subx-stmt _test-output-buffered-file %esi Primitives 0 Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "01/add-to %eax 0x00000001/r32" "F - test-add-reg-to-reg")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-add-reg-to-mem:
+    #   add-to var1 var2/reg
+    # =>
+    #   01/add-to *(ebp+__) var2
+    #
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+$test-add-reg-to-mem:initialize-type:
+    # var type/ecx: (payload type-tree) = int
+    68/push 0/imm32/right:null
+    68/push 0/imm32/right:null
+    68/push 0/imm32/left:unused
+    68/push 1/imm32/value:int
+    68/push 1/imm32/is-atom?:true
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %ecx 4/r32/esp
+$test-add-reg-to-mem:initialize-var1:
+    # var var1/ecx: (payload var)
+    68/push 0/imm32/register
+    68/push 0/imm32/register
+    68/push 8/imm32/stack-offset
+    68/push 1/imm32/block-depth
+    51/push-ecx
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/name
+    68/push 0/imm32/name
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %ecx 4/r32/esp
+$test-add-reg-to-mem:initialize-var1-name:
+    # var1->name = "var1"
+    8d/copy-address *(ecx+4) 0/r32/eax  # Var-name + 4
+    (copy-array Heap "var1" %eax)
+$test-add-reg-to-mem:initialize-var2:
+    # var var2/edx: (payload var)
+    68/push 0/imm32/register
+    68/push 0/imm32/register
+    68/push 0/imm32/no-stack-offset
+    68/push 1/imm32/block-depth
+    ff 6/subop/push *(ecx+0x10)
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/name
+    68/push 0/imm32/name
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %edx 4/r32/esp
+$test-add-reg-to-mem:initialize-var2-name:
+    # var2->name = "var2"
+    8d/copy-address *(edx+4) 0/r32/eax  # Var-name + 4
+    (copy-array Heap "var2" %eax)
+$test-add-reg-to-mem:initialize-var2-register:
+    # var2->register = "ecx"
+    8d/copy-address *(edx+0x1c) 0/r32/eax  # Var-register + 4
+    (copy-array Heap "ecx" %eax)
+$test-add-reg-to-mem:initialize-inouts:
+    # var inouts/esi: (payload stmt-var) = [var2]
+    68/push 0/imm32/is-deref:false
+    68/push 0/imm32/next
+    68/push 0/imm32/next
+    52/push-edx/var2
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %esi 4/r32/esp
+    # inouts = [var1, var2]
+    68/push 0/imm32/is-deref:false
+    56/push-esi/next
+    68/push 0x11/imm32/alloc-id:fake
+    51/push-ecx/var1
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %esi 4/r32/esp
+$test-add-reg-to-mem:initialize-stmt:
+    # var stmt/esi: (addr statement)
+    68/push 0/imm32/next
+    68/push 0/imm32/next
+    68/push 0/imm32/outputs
+    68/push 0/imm32/outputs
+    56/push-esi/inouts
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/operation
+    68/push 0/imm32/operation
+    68/push 1/imm32/tag:stmt1
+    89/<- %esi 4/r32/esp
+$test-add-reg-to-mem:initialize-stmt-operation:
+    # stmt->operation = "add-to"
+    8d/copy-address *(esi+4) 0/r32/eax  # Stmt1-operation
+    (copy-array Heap "add-to" %eax)
+    # convert
+    c7 0/subop/copy *Curr-block-depth 0/imm32
+    (emit-subx-stmt _test-output-buffered-file %esi Primitives 0 Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "01/add-to *(ebp+0x00000008) 0x00000001/r32" "F - test-add-reg-to-mem")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-add-mem-to-reg:
+    #   var1/reg <- add var2
+    # =>
+    #   03/add *(ebp+__) var1
+    #
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+$test-add-mem-to-reg:initialize-type:
+    # var type/ecx: (payload type-tree) = int
+    68/push 0/imm32/right:null
+    68/push 0/imm32/right:null
+    68/push 0/imm32/left:unused
+    68/push 1/imm32/value:int
+    68/push 1/imm32/is-atom?:true
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %ecx 4/r32/esp
+$test-add-mem-to-reg:initialize-var:
+    # var var1/ecx: (payload var)
+    68/push 0/imm32/register
+    68/push 0/imm32/register
+    68/push 0/imm32/no-stack-offset
+    68/push 1/imm32/block-depth
+    51/push-ecx
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/name
+    68/push 0/imm32/name
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %ecx 4/r32/esp
+$test-add-mem-to-reg:initialize-var-name:
+    # var1->name = "foo"
+    8d/copy-address *(ecx+4) 0/r32/eax  # Var-name + 4
+    (copy-array Heap "var1" %eax)
+$test-add-mem-to-reg:initialize-var-register:
+    # var1->register = "eax"
+    8d/copy-address *(ecx+0x1c) 0/r32/eax  # Var-register + 4
+    (copy-array Heap "eax" %eax)
+$test-add-mem-to-reg:initialize-var2:
+    # var var2/edx: (payload var)
+    68/push 0/imm32/register
+    68/push 0/imm32/register
+    68/push 8/imm32/stack-offset
+    68/push 1/imm32/block-depth
+    ff 6/subop/push *(ecx+0x10)
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/name
+    68/push 0/imm32/name
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %edx 4/r32/esp
+$test-add-mem-to-reg:initialize-var2-name:
+    # var2->name = "var2"
+    8d/copy-address *(edx+4) 0/r32/eax  # Var-name + 4
+    (copy-array Heap "var2" %eax)
+$test-add-mem-to-reg:initialize-inouts:
+    # var inouts/esi: (payload stmt-var) = [var2]
+    68/push 0/imm32/is-deref:false
+    68/push 0/imm32/next
+    68/push 0/imm32/next
+    52/push-edx/var2
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %esi 4/r32/esp
+$test-add-mem-to-reg:initialize-outputs:
+    # var outputs/edi: (payload stmt-var) = [var1]
+    68/push 0/imm32/is-deref:false
+    68/push 0/imm32/next
+    68/push 0/imm32/next
+    51/push-ecx/var1
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %edi 4/r32/esp
+$test-add-mem-to-reg:initialize-stmt:
+    # var stmt/esi: (addr statement)
+    68/push 0/imm32/next
+    68/push 0/imm32/next
+    57/push-edi/outputs
+    68/push 0x11/imm32/alloc-id:fake
+    56/push-esi/inouts
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/operation
+    68/push 0/imm32/operation
+    68/push 1/imm32/tag:stmt1
+    89/<- %esi 4/r32/esp
+$test-add-mem-to-reg:initialize-stmt-operation:
+    # stmt->operation = "add"
+    8d/copy-address *(esi+4) 0/r32/eax  # Stmt1-operation
+    (copy-array Heap "add" %eax)
+    # convert
+    c7 0/subop/copy *Curr-block-depth 0/imm32
+    (emit-subx-stmt _test-output-buffered-file %esi Primitives 0 Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "03/add *(ebp+0x00000008) 0x00000000/r32" "F - test-add-mem-to-reg")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-add-literal-to-eax:
+    #   var1/eax <- add 0x34
+    # =>
+    #   05/add-to-eax 0x34/imm32
+    #
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+$test-add-literal-to-eax:initialize-var-type:
+    # var type/ecx: (payload type-tree) = int
+    68/push 0/imm32/right:null
+    68/push 0/imm32/right:null
+    68/push 0/imm32/left:unused
+    68/push 1/imm32/value:int
+    68/push 1/imm32/is-atom?:true
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %ecx 4/r32/esp
+$test-add-literal-to-eax:initialize-var:
+    # var v/ecx: (payload var)
+    68/push 0/imm32/register
+    68/push 0/imm32/register
+    68/push 0/imm32/no-stack-offset
+    68/push 1/imm32/block-depth
+    51/push-ecx
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/name
+    68/push 0/imm32/name
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %ecx 4/r32/esp
+$test-add-literal-to-eax:initialize-var-name:
+    # v->name = "v"
+    8d/copy-address *(ecx+4) 0/r32/eax  # Var-name + 4
+    (copy-array Heap "v" %eax)
+$test-add-literal-to-eax:initialize-var-register:
+    # v->register = "eax"
+    8d/copy-address *(ecx+0x1c) 0/r32/eax  # Var-register + 4
+    (copy-array Heap "eax" %eax)
+$test-add-literal-to-eax:initialize-literal-type:
+    # var type/edx: (payload type-tree) = literal
+    68/push 0/imm32/right:null
+    68/push 0/imm32/right:null
+    68/push 0/imm32/left:unused
+    68/push 0/imm32/value:literal
+    68/push 1/imm32/is-atom?:true
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %edx 4/r32/esp
+$test-add-literal-to-eax:initialize-literal:
+    # var l/edx: (payload var)
+    68/push 0/imm32/register
+    68/push 0/imm32/register
+    68/push 0/imm32/no-stack-offset
+    68/push 1/imm32/block-depth
+    52/push-edx
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/name
+    68/push 0/imm32/name
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %edx 4/r32/esp
+$test-add-literal-to-eax:initialize-literal-value:
+    # l->name = "0x34"
+    8d/copy-address *(edx+4) 0/r32/eax  # Var-name + 4
+    (copy-array Heap "0x34" %eax)
+$test-add-literal-to-eax:initialize-inouts:
+    # var inouts/esi: (payload stmt-var) = [l]
+    68/push 0/imm32/is-deref:false
+    68/push 0/imm32/next
+    68/push 0/imm32/next
+    52/push-edx/l
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %esi 4/r32/esp
+$test-add-literal-to-eax:initialize-outputs:
+    # var outputs/edi: (payload stmt-var) = [v]
+    68/push 0/imm32/is-deref:false
+    68/push 0/imm32/next
+    68/push 0/imm32/next
+    51/push-ecx/v
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %edi 4/r32/esp
+$test-add-literal-to-eax:initialize-stmt:
+    # var stmt/esi: (addr statement)
+    68/push 0/imm32/next
+    68/push 0/imm32/next
+    57/push-edi/outputs
+    68/push 0x11/imm32/alloc-id:fake
+    56/push-esi/inouts
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/operation
+    68/push 0/imm32/operation
+    68/push 1/imm32/tag:stmt1
+    89/<- %esi 4/r32/esp
+$test-add-literal-to-eax:initialize-stmt-operation:
+    # stmt->operation = "add"
+    8d/copy-address *(esi+4) 0/r32/eax  # Stmt1-operation
+    (copy-array Heap "add" %eax)
+    # convert
+    c7 0/subop/copy *Curr-block-depth 0/imm32
+    (emit-subx-stmt _test-output-buffered-file %esi Primitives 0 Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "05/add-to-eax 0x34/imm32" "F - test-add-literal-to-eax")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-add-literal-to-reg:
+    #   var1/ecx <- add 0x34
+    # =>
+    #   81 0/subop/add %ecx 0x34/imm32
+    #
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+$test-add-literal-to-reg:initialize-var-type:
+    # var type/ecx: (payload type-tree) = int
+    68/push 0/imm32/right:null
+    68/push 0/imm32/right:null
+    68/push 0/imm32/left:unused
+    68/push 1/imm32/value:int
+    68/push 1/imm32/is-atom?:true
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %ecx 4/r32/esp
+$test-add-literal-to-reg:initialize-var:
+    # var v/ecx: (payload var)
+    68/push 0/imm32/register
+    68/push 0/imm32/register
+    68/push 0/imm32/no-stack-offset
+    68/push 1/imm32/block-depth
+    51/push-ecx
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/name
+    68/push 0/imm32/name
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %ecx 4/r32/esp
+$test-add-literal-to-reg:initialize-var-name:
+    # v->name = "v"
+    8d/copy-address *(ecx+4) 0/r32/eax  # Var-name + 4
+    (copy-array Heap "v" %eax)
+$test-add-literal-to-reg:initialize-var-register:
+    # v->register = "ecx"
+    8d/copy-address *(ecx+0x1c) 0/r32/eax  # Var-register + 4
+    (copy-array Heap "ecx" %eax)
+$test-add-literal-to-reg:initialize-literal-type:
+    # var type/edx: (payload type-tree) = literal
+    68/push 0/imm32/right:null
+    68/push 0/imm32/right:null
+    68/push 0/imm32/left:unused
+    68/push 0/imm32/value:literal
+    68/push 1/imm32/is-atom?:true
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %edx 4/r32/esp
+$test-add-literal-to-reg:initialize-literal:
+    # var l/edx: (payload var)
+    68/push 0/imm32/register
+    68/push 0/imm32/register
+    68/push 0/imm32/no-stack-offset
+    68/push 1/imm32/block-depth
+    52/push-edx
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/name
+    68/push 0/imm32/name
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %edx 4/r32/esp
+$test-add-literal-to-reg:initialize-literal-value:
+    # l->name = "0x34"
+    8d/copy-address *(edx+4) 0/r32/eax  # Var-name + 4
+    (copy-array Heap "0x34" %eax)
+$test-add-literal-to-reg:initialize-inouts:
+    # var inouts/esi: (payload stmt-var) = [l]
+    68/push 0/imm32/is-deref:false
+    68/push 0/imm32/next
+    68/push 0/imm32/next
+    52/push-edx/l
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %esi 4/r32/esp
+$test-add-literal-to-reg:initialize-outputs:
+    # var outputs/edi: (payload stmt-var) = [v]
+    68/push 0/imm32/is-deref:false
+    68/push 0/imm32/next
+    68/push 0/imm32/next
+    51/push-ecx/v
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %edi 4/r32/esp
+$test-add-literal-to-reg:initialize-stmt:
+    # var stmt/esi: (addr statement)
+    68/push 0/imm32/next
+    68/push 0/imm32/next
+    57/push-edi/outputs
+    68/push 0x11/imm32/alloc-id:fake
+    56/push-esi/inouts
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/operation
+    68/push 0/imm32/operation
+    68/push 1/imm32/tag:stmt1
+    89/<- %esi 4/r32/esp
+$test-add-literal-to-reg:initialize-stmt-operation:
+    # stmt->operation = "add"
+    8d/copy-address *(esi+4) 0/r32/eax  # Stmt1-operation
+    (copy-array Heap "add" %eax)
+    # convert
+    c7 0/subop/copy *Curr-block-depth 0/imm32
+    (emit-subx-stmt _test-output-buffered-file %esi Primitives 0 Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "81 0/subop/add %ecx 0x34/imm32" "F - test-add-literal-to-reg")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-add-literal-to-mem:
+    #   add-to var1, 0x34
+    # =>
+    #   81 0/subop/add %eax 0x34/imm32
+    #
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+$test-add-literal-to-mem:initialize-type:
+    # var type/ecx: (payload type-tree) = int
+    68/push 0/imm32/right:null
+    68/push 0/imm32/right:null
+    68/push 0/imm32/left:unused
+    68/push 1/imm32/value:int
+    68/push 1/imm32/is-atom?:true
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %ecx 4/r32/esp
+$test-add-literal-to-mem:initialize-var1:
+    # var var1/ecx: (payload var)
+    68/push 0/imm32/register
+    68/push 0/imm32/register
+    68/push 8/imm32/stack-offset
+    68/push 1/imm32/block-depth
+    51/push-ecx
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/name
+    68/push 0/imm32/name
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %ecx 4/r32/esp
+$test-add-literal-to-mem:initialize-var1-name:
+    # var1->name = "var1"
+    8d/copy-address *(ecx+4) 0/r32/eax  # Var-name + 4
+    (copy-array Heap "var1" %eax)
+$test-add-literal-to-mem:initialize-literal-type:
+    # var type/edx: (payload type-tree) = literal
+    68/push 0/imm32/right:null
+    68/push 0/imm32/right:null
+    68/push 0/imm32/left:unused
+    68/push 0/imm32/value:literal
+    68/push 1/imm32/is-atom?:true
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %edx 4/r32/esp
+$test-add-literal-to-mem:initialize-literal:
+    # var l/edx: (payload var)
+    68/push 0/imm32/register
+    68/push 0/imm32/register
+    68/push 0/imm32/no-stack-offset
+    68/push 1/imm32/block-depth
+    52/push-edx
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/name
+    68/push 0/imm32/name
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %edx 4/r32/esp
+$test-add-literal-to-mem:initialize-literal-value:
+    # l->name = "0x34"
+    8d/copy-address *(edx+4) 0/r32/eax  # Var-name + 4
+    (copy-array Heap "0x34" %eax)
+$test-add-literal-to-mem:initialize-inouts:
+    # var inouts/esi: (payload stmt-var) = [l]
+    68/push 0/imm32/is-deref:false
+    68/push 0/imm32/next
+    68/push 0/imm32/next
+    52/push-edx/l
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %esi 4/r32/esp
+    # var inouts = (handle stmt-var) = [var1, var2]
+    68/push 0/imm32/is-deref:false
+    56/push-esi/next
+    68/push 0x11/imm32/alloc-id:fake
+    51/push-ecx/var1
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %esi 4/r32/esp
+$test-add-literal-to-mem:initialize-stmt:
+    # var stmt/esi: (addr statement)
+    68/push 0/imm32/next
+    68/push 0/imm32/next
+    68/push 0/imm32/outputs
+    68/push 0/imm32/outputs
+    56/push-esi/inouts
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/operation
+    68/push 0/imm32/operation
+    68/push 1/imm32/tag:stmt1
+    89/<- %esi 4/r32/esp
+$test-add-literal-to-mem:initialize-stmt-operation:
+    # stmt->operation = "add-to"
+    8d/copy-address *(esi+4) 0/r32/eax  # Stmt1-operation
+    (copy-array Heap "add-to" %eax)
+    # convert
+    c7 0/subop/copy *Curr-block-depth 0/imm32
+    (emit-subx-stmt _test-output-buffered-file %esi Primitives 0 Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "81 0/subop/add *(ebp+0x00000008) 0x34/imm32" "F - test-add-literal-to-mem")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-shift-reg-by-literal:
+    #   var1/ecx <- shift-left 2
+    # =>
+    #   c1/shift 4/subop/left %ecx 2/imm8
+    #
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+$test-shift-reg-by-literal:initialize-var-type:
+    # var type/ecx: (payload type-tree) = int
+    68/push 0/imm32/right:null
+    68/push 0/imm32/right:null
+    68/push 0/imm32/left:unused
+    68/push 1/imm32/value:int
+    68/push 1/imm32/is-atom?:true
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %ecx 4/r32/esp
+$test-shift-reg-by-literal:initialize-var:
+    # var v/ecx: (payload var)
+    68/push 0/imm32/register
+    68/push 0/imm32/register
+    68/push 0/imm32/no-stack-offset
+    68/push 1/imm32/block-depth
+    51/push-ecx
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/name
+    68/push 0/imm32/name
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %ecx 4/r32/esp
+$test-shift-reg-by-literal:initialize-var-name:
+    # v->name = "v"
+    8d/copy-address *(ecx+4) 0/r32/eax  # Var-name + 4
+    (copy-array Heap "v" %eax)
+$test-shift-reg-by-literal:initialize-var-register:
+    # v->register = "ecx"
+    8d/copy-address *(ecx+0x1c) 0/r32/eax  # Var-register + 4
+    (copy-array Heap "ecx" %eax)
+$test-shift-reg-by-literal:initialize-literal-type:
+    # var type/edx: (payload type-tree) = literal
+    68/push 0/imm32/right:null
+    68/push 0/imm32/right:null
+    68/push 0/imm32/left:unused
+    68/push 0/imm32/value:literal
+    68/push 1/imm32/is-atom?:true
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %edx 4/r32/esp
+$test-shift-reg-by-literal:initialize-literal:
+    # var l/edx: (payload var)
+    68/push 0/imm32/register
+    68/push 0/imm32/register
+    68/push 0/imm32/no-stack-offset
+    68/push 1/imm32/block-depth
+    52/push-edx
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/name
+    68/push 0/imm32/name
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %edx 4/r32/esp
+$test-shift-reg-by-literal:initialize-literal-value:
+    # l->name = "2"
+    8d/copy-address *(edx+4) 0/r32/eax  # Var-name + 4
+    (copy-array Heap "2" %eax)
+$test-shift-reg-by-literal:initialize-inouts:
+    # var inouts/esi: (payload stmt-var) = [l]
+    68/push 0/imm32/is-deref:false
+    68/push 0/imm32/next
+    68/push 0/imm32/next
+    52/push-edx/l
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %esi 4/r32/esp
+$test-shift-reg-by-literal:initialize-outputs:
+    # var outputs/edi: (payload stmt-var) = [v]
+    68/push 0/imm32/is-deref:false
+    68/push 0/imm32/next
+    68/push 0/imm32/next
+    51/push-ecx/v
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %edi 4/r32/esp
+$test-shift-reg-by-literal:initialize-stmt:
+    # var stmt/esi: (addr statement)
+    68/push 0/imm32/next
+    68/push 0/imm32/next
+    57/push-edi/outputs
+    68/push 0x11/imm32/alloc-id:fake
+    56/push-esi/inouts
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/operation
+    68/push 0/imm32/operation
+    68/push 1/imm32/tag:stmt1
+    89/<- %esi 4/r32/esp
+$test-shift-reg-by-literal:initialize-stmt-operation:
+    # stmt->operation = "shift-left"
+    8d/copy-address *(esi+4) 0/r32/eax  # Stmt1-operation
+    (copy-array Heap "shift-left" %eax)
+    # convert
+    c7 0/subop/copy *Curr-block-depth 0/imm32
+    (emit-subx-stmt _test-output-buffered-file %esi Primitives 0 Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "c1/shift 4/subop/left %ecx 2/imm8" "F - test-shift-reg-by-literal")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-shift-mem-by-literal:
+    #   shift-left var 3
+    # =>
+    #   c1/shift 4/subop/left *(ebp+8) 3/imm8
+    #
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+$test-shift-mem-by-literal:initialize-type:
+    # var type/ecx: (payload type-tree) = int
+    68/push 0/imm32/right:null
+    68/push 0/imm32/right:null
+    68/push 0/imm32/left:unused
+    68/push 1/imm32/value:int
+    68/push 1/imm32/is-atom?:true
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %ecx 4/r32/esp
+$test-shift-mem-by-literal:initialize-var1:
+    # var var1/ecx: (payload var)
+    68/push 0/imm32/register
+    68/push 0/imm32/register
+    68/push 8/imm32/stack-offset
+    68/push 1/imm32/block-depth
+    51/push-ecx
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/name
+    68/push 0/imm32/name
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %ecx 4/r32/esp
+$test-shift-mem-by-literal:initialize-var1-name:
+    # var1->name = "var1"
+    8d/copy-address *(ecx+4) 0/r32/eax  # Var-name + 4
+    (copy-array Heap "var1" %eax)
+$test-shift-mem-by-literal:initialize-literal-type:
+    # var type/edx: (payload type-tree) = literal
+    68/push 0/imm32/right:null
+    68/push 0/imm32/right:null
+    68/push 0/imm32/left:unused
+    68/push 0/imm32/value:literal
+    68/push 1/imm32/is-atom?:true
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %edx 4/r32/esp
+$test-shift-mem-by-literal:initialize-literal:
+    # var l/edx: (payload var)
+    68/push 0/imm32/register
+    68/push 0/imm32/register
+    68/push 0/imm32/no-stack-offset
+    68/push 1/imm32/block-depth
+    52/push-edx
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/name
+    68/push 0/imm32/name
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %edx 4/r32/esp
+$test-shift-mem-by-literal:initialize-literal-value:
+    # l->name = "3"
+    8d/copy-address *(edx+4) 0/r32/eax  # Var-name + 4
+    (copy-array Heap "3" %eax)
+$test-shift-mem-by-literal:initialize-inouts:
+    # var inouts/esi: (payload stmt-var) = [l]
+    68/push 0/imm32/is-deref:false
+    68/push 0/imm32/next
+    68/push 0/imm32/next
+    52/push-edx/l
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %esi 4/r32/esp
+    # var inouts = (handle stmt-var) = [var1, var2]
+    68/push 0/imm32/is-deref:false
+    56/push-esi/next
+    68/push 0x11/imm32/alloc-id:fake
+    51/push-ecx/var1
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %esi 4/r32/esp
+$test-shift-mem-by-literal:initialize-stmt:
+    # var stmt/esi: (addr statement)
+    68/push 0/imm32/next
+    68/push 0/imm32/next
+    68/push 0/imm32/outputs
+    68/push 0/imm32/outputs
+    56/push-esi/inouts
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/operation
+    68/push 0/imm32/operation
+    68/push 1/imm32/tag:stmt1
+    89/<- %esi 4/r32/esp
+$test-shift-mem-by-literal:initialize-stmt-operation:
+    # stmt->operation = "shift-left"
+    8d/copy-address *(esi+4) 0/r32/eax  # Stmt1-operation
+    (copy-array Heap "shift-left" %eax)
+    # convert
+    c7 0/subop/copy *Curr-block-depth 0/imm32
+    (emit-subx-stmt _test-output-buffered-file %esi Primitives 0 Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "c1/shift 4/subop/left *(ebp+0x00000008) 3/imm8" "F - test-shift-mem-by-literal")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-compare-reg-with-reg:
+    #   compare var1/ecx, var2/eax
+    # =>
+    #   39/compare %ecx 0/r32/eax
+    #
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+$test-compare-reg-with-reg:initialize-type:
+    # var type/ecx: (payload type-tree) = int
+    68/push 0/imm32/right:null
+    68/push 0/imm32/right:null
+    68/push 0/imm32/left:unused
+    68/push 1/imm32/value:int
+    68/push 1/imm32/is-atom?:true
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %ecx 4/r32/esp
+$test-compare-reg-with-reg:initialize-var1:
+    # var var1/ecx: (payload var)
+    68/push 0/imm32/register
+    68/push 0/imm32/register
+    68/push 0/imm32/no-stack-offset
+    68/push 1/imm32/block-depth
+    51/push-ecx
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/name
+    68/push 0/imm32/name
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %ecx 4/r32/esp
+$test-compare-reg-with-reg:initialize-var1-name:
+    # var1->name = "var1"
+    8d/copy-address *(ecx+4) 0/r32/eax  # Var-name + 4
+    (copy-array Heap "var1" %eax)
+$test-compare-reg-with-reg:initialize-var1-register:
+    # var1->register = "ecx"
+    8d/copy-address *(ecx+0x1c) 0/r32/eax  # Var-register + 4
+    (copy-array Heap "ecx" %eax)
+$test-compare-reg-with-reg:initialize-var2:
+    # var var2/edx: (payload var)
+    68/push 0/imm32/register
+    68/push 0/imm32/register
+    68/push 0/imm32/no-stack-offset
+    68/push 1/imm32/block-depth
+    ff 6/subop/push *(ecx+0x10)
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/name
+    68/push 0/imm32/name
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %edx 4/r32/esp
+$test-compare-reg-with-reg:initialize-var2-name:
+    # var2->name = "var2"
+    8d/copy-address *(edx+4) 0/r32/eax  # Var-name + 4
+    (copy-array Heap "var2" %eax)
+$test-compare-reg-with-reg:initialize-var2-register:
+    # var2->register = "eax"
+    8d/copy-address *(edx+0x1c) 0/r32/eax  # Var-register + 4
+    (copy-array Heap "eax" %eax)
+$test-compare-reg-with-reg:initialize-inouts:
+    # var inouts/esi: (payload stmt-var) = [var2]
+    68/push 0/imm32/is-deref:false
+    68/push 0/imm32/next
+    68/push 0/imm32/next
+    52/push-edx/var2
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %esi 4/r32/esp
+    # inouts = [var1, var2]
+    68/push 0/imm32/is-deref:false
+    56/push-esi/next
+    68/push 0x11/imm32/alloc-id:fake
+    51/push-ecx/var1
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %esi 4/r32/esp
+$test-compare-reg-with-reg:initialize-stmt:
+    # var stmt/esi: (addr statement)
+    68/push 0/imm32/next
+    68/push 0/imm32/next
+    68/push 0/imm32/outputs
+    68/push 0/imm32/outputs
+    56/push-esi/inouts
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/operation
+    68/push 0/imm32/operation
+    68/push 1/imm32/tag:stmt1
+    89/<- %esi 4/r32/esp
+$test-compare-reg-with-reg:initialize-stmt-operation:
+    # stmt->operation = "compare"
+    8d/copy-address *(esi+4) 0/r32/eax  # Stmt1-operation
+    (copy-array Heap "compare" %eax)
+    # convert
+    c7 0/subop/copy *Curr-block-depth 0/imm32
+    (emit-subx-stmt _test-output-buffered-file %esi Primitives 0 Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "39/compare-> %ecx 0x00000000/r32" "F - test-compare-reg-with-reg")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-compare-mem-with-reg:
+    #   compare var1, var2/eax
+    # =>
+    #   39/compare *(ebp+___) 0/r32/eax
+    #
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+$test-compare-mem-with-reg:initialize-type:
+    # var type/ecx: (payload type-tree) = int
+    68/push 0/imm32/right:null
+    68/push 0/imm32/right:null
+    68/push 0/imm32/left:unused
+    68/push 1/imm32/value:int
+    68/push 1/imm32/is-atom?:true
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %ecx 4/r32/esp
+$test-compare-mem-with-reg:initialize-var1:
+    # var var1/ecx: (payload var)
+    68/push 0/imm32/register
+    68/push 0/imm32/register
+    68/push 8/imm32/stack-offset
+    68/push 1/imm32/block-depth
+    51/push-ecx
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/name
+    68/push 0/imm32/name
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %ecx 4/r32/esp
+$test-compare-mem-with-reg:initialize-var1-name:
+    # var1->name = "var1"
+    8d/copy-address *(ecx+4) 0/r32/eax  # Var-name + 4
+    (copy-array Heap "var1" %eax)
+$test-compare-mem-with-reg:initialize-var2:
+    # var var2/edx: (payload var)
+    68/push 0/imm32/register
+    68/push 0/imm32/register
+    68/push 0/imm32/no-stack-offset
+    68/push 1/imm32/block-depth
+    ff 6/subop/push *(ecx+0x10)
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/name
+    68/push 0/imm32/name
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %edx 4/r32/esp
+$test-compare-mem-with-reg:initialize-var2-name:
+    # var2->name = "var2"
+    8d/copy-address *(edx+4) 0/r32/eax  # Var-name + 4
+    (copy-array Heap "var2" %eax)
+$test-compare-mem-with-reg:initialize-var2-register:
+    # var2->register = "eax"
+    8d/copy-address *(edx+0x1c) 0/r32/eax  # Var-register + 4
+    (copy-array Heap "eax" %eax)
+$test-compare-mem-with-reg:initialize-inouts:
+    # var inouts/esi: (payload stmt-var) = [var2]
+    68/push 0/imm32/is-deref:false
+    68/push 0/imm32/next
+    68/push 0/imm32/next
+    52/push-edx/var2
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %esi 4/r32/esp
+    # inouts = [var1, var2]
+    68/push 0/imm32/is-deref:false
+    56/push-esi/next
+    68/push 0x11/imm32/alloc-id:fake
+    51/push-ecx/var1
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %esi 4/r32/esp
+$test-compare-mem-with-reg:initialize-stmt:
+    # var stmt/esi: (addr statement)
+    68/push 0/imm32/next
+    68/push 0/imm32/next
+    68/push 0/imm32/outputs
+    68/push 0/imm32/outputs
+    56/push-esi/inouts
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/operation
+    68/push 0/imm32/operation
+    68/push 1/imm32/tag:stmt1
+    89/<- %esi 4/r32/esp
+$test-compare-mem-with-reg:initialize-stmt-operation:
+    # stmt->operation = "compare"
+    8d/copy-address *(esi+4) 0/r32/eax  # Stmt1-operation
+    (copy-array Heap "compare" %eax)
+    # convert
+    c7 0/subop/copy *Curr-block-depth 0/imm32
+    (emit-subx-stmt _test-output-buffered-file %esi Primitives 0 Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "39/compare-> *(ebp+0x00000008) 0x00000000/r32" "F - test-compare-mem-with-reg")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-compare-reg-with-mem:
+    #   compare var1/eax, var2
+    # =>
+    #   3b/compare<- *(ebp+___) 0/r32/eax
+    #
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+$test-compare-reg-with-mem:initialize-type:
+    # var type/ecx: (payload type-tree) = int
+    68/push 0/imm32/right:null
+    68/push 0/imm32/right:null
+    68/push 0/imm32/left:unused
+    68/push 1/imm32/value:int
+    68/push 1/imm32/is-atom?:true
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %ecx 4/r32/esp
+$test-compare-reg-with-mem:initialize-var1:
+    # var var1/ecx: (payload var)
+    68/push 0/imm32/register
+    68/push 0/imm32/register
+    68/push 0/imm32/no-stack-offset
+    68/push 1/imm32/block-depth
+    51/push-ecx
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/name
+    68/push 0/imm32/name
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %ecx 4/r32/esp
+$test-compare-reg-with-mem:initialize-var1-name:
+    # var1->name = "var1"
+    8d/copy-address *(ecx+4) 0/r32/eax  # Var-name + 4
+    (copy-array Heap "var1" %eax)
+$test-compare-reg-with-mem:initialize-var1-register:
+    # var1->register = "eax"
+    8d/copy-address *(ecx+0x1c) 0/r32/eax  # Var-register + 4
+    (copy-array Heap "eax" %eax)
+$test-compare-reg-with-mem:initialize-var2:
+    # var var2/edx: (payload var)
+    68/push 0/imm32/register
+    68/push 0/imm32/register
+    68/push 8/imm32/stack-offset
+    68/push 1/imm32/block-depth
+    ff 6/subop/push *(ecx+0x10)
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/name
+    68/push 0/imm32/name
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %edx 4/r32/esp
+$test-compare-reg-with-mem:initialize-var2-name:
+    # var2->name = "var2"
+    8d/copy-address *(edx+4) 0/r32/eax  # Var-name + 4
+    (copy-array Heap "var2" %eax)
+$test-compare-reg-with-mem:initialize-inouts:
+    # var inouts/esi: (payload stmt-var) = [var2]
+    68/push 0/imm32/is-deref:false
+    68/push 0/imm32/next
+    68/push 0/imm32/next
+    52/push-edx/var2
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %esi 4/r32/esp
+    # inouts = [var1, var2]
+    68/push 0/imm32/is-deref:false
+    56/push-esi/next
+    68/push 0x11/imm32/alloc-id:fake
+    51/push-ecx/var1
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %esi 4/r32/esp
+$test-compare-reg-with-mem:initialize-stmt:
+    # var stmt/esi: (addr statement)
+    68/push 0/imm32/next
+    68/push 0/imm32/next
+    68/push 0/imm32/outputs
+    68/push 0/imm32/outputs
+    56/push-esi/inouts
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/operation
+    68/push 0/imm32/operation
+    68/push 1/imm32/tag:stmt1
+    89/<- %esi 4/r32/esp
+$test-compare-reg-with-mem:initialize-stmt-operation:
+    # stmt->operation = "compare"
+    8d/copy-address *(esi+4) 0/r32/eax  # Stmt1-operation
+    (copy-array Heap "compare" %eax)
+    # convert
+    c7 0/subop/copy *Curr-block-depth 0/imm32
+    (emit-subx-stmt _test-output-buffered-file %esi Primitives 0 Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "3b/compare<- *(ebp+0x00000008) 0x00000000/r32" "F - test-compare-reg-with-mem")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-compare-mem-with-literal:
+    #   compare var1, 0x34
+    # =>
+    #   81 7/subop/compare *(ebp+___) 0x34/imm32
+    #
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+$test-compare-mem-with-literal:initialize-type:
+    # var type/ecx: (payload type-tree) = int
+    68/push 0/imm32/right:null
+    68/push 0/imm32/right:null
+    68/push 0/imm32/left:unused
+    68/push 1/imm32/value:int
+    68/push 1/imm32/is-atom?:true
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %ecx 4/r32/esp
+$test-compare-mem-with-literal:initialize-var1:
+    # var var1/ecx: (payload var)
+    68/push 0/imm32/register
+    68/push 0/imm32/register
+    68/push 8/imm32/stack-offset
+    68/push 1/imm32/block-depth
+    51/push-ecx
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/name
+    68/push 0/imm32/name
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %ecx 4/r32/esp
+$test-compare-mem-with-literal:initialize-var1-name:
+    # var1->name = "var1"
+    8d/copy-address *(ecx+4) 0/r32/eax  # Var-name + 4
+    (copy-array Heap "var1" %eax)
+$test-compare-mem-with-literal:initialize-literal-type:
+    # var type/edx: (payload type-tree) = literal
+    68/push 0/imm32/right:null
+    68/push 0/imm32/right:null
+    68/push 0/imm32/left:unused
+    68/push 0/imm32/value:literal
+    68/push 1/imm32/is-atom?:true
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %edx 4/r32/esp
+$test-compare-mem-with-literal:initialize-literal:
+    # var l/edx: (payload var)
+    68/push 0/imm32/register
+    68/push 0/imm32/register
+    68/push 0/imm32/no-stack-offset
+    68/push 1/imm32/block-depth
+    52/push-edx
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/name
+    68/push 0/imm32/name
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %edx 4/r32/esp
+$test-compare-mem-with-literal:initialize-literal-value:
+    # l->name = "0x34"
+    8d/copy-address *(edx+4) 0/r32/eax  # Var-name + 4
+    (copy-array Heap "0x34" %eax)
+$test-compare-mem-with-literal:initialize-inouts:
+    # var inouts/esi: (payload stmt-var) = [l]
+    68/push 0/imm32/is-deref:false
+    68/push 0/imm32/next
+    68/push 0/imm32/next
+    52/push-edx/l
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %esi 4/r32/esp
+    # var inouts = (handle stmt-var) = [var1, var2]
+    68/push 0/imm32/is-deref:false
+    56/push-esi/next
+    68/push 0x11/imm32/alloc-id:fake
+    51/push-ecx/var1
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %esi 4/r32/esp
+$test-compare-mem-with-literal:initialize-stmt:
+    # var stmt/esi: (addr statement)
+    68/push 0/imm32/next
+    68/push 0/imm32/next
+    68/push 0/imm32/outputs
+    68/push 0/imm32/outputs
+    56/push-esi/inouts
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/operation
+    68/push 0/imm32/operation
+    68/push 1/imm32/tag:stmt1
+    89/<- %esi 4/r32/esp
+$test-compare-mem-with-literal:initialize-stmt-operation:
+    # stmt->operation = "compare"
+    8d/copy-address *(esi+4) 0/r32/eax  # Stmt1-operation
+    (copy-array Heap "compare" %eax)
+    # convert
+    c7 0/subop/copy *Curr-block-depth 0/imm32
+    (emit-subx-stmt _test-output-buffered-file %esi Primitives 0 Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "81 7/subop/compare *(ebp+0x00000008) 0x34/imm32" "F - test-compare-mem-with-literal")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-compare-eax-with-literal:
+    #   compare var1/eax 0x34
+    # =>
+    #   3d/compare-eax-with 0x34/imm32
+    #
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+$test-compare-eax-with-literal:initialize-type:
+    # var type/ecx: (payload type-tree) = int
+    68/push 0/imm32/right:null
+    68/push 0/imm32/right:null
+    68/push 0/imm32/left:unused
+    68/push 1/imm32/value:int
+    68/push 1/imm32/is-atom?:true
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %ecx 4/r32/esp
+$test-compare-eax-with-literal:initialize-var1:
+    # var var1/ecx: (payload var)
+    68/push 0/imm32/register
+    68/push 0/imm32/register
+    68/push 0/imm32/no-stack-offset
+    68/push 1/imm32/block-depth
+    51/push-ecx
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/name
+    68/push 0/imm32/name
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %ecx 4/r32/esp
+$test-compare-eax-with-literal:initialize-var1-name:
+    # var1->name = "var1"
+    8d/copy-address *(ecx+4) 0/r32/eax  # Var-name + 4
+    (copy-array Heap "var1" %eax)
+$test-compare-eax-with-literal:initialize-var1-register:
+    # v->register = "eax"
+    8d/copy-address *(ecx+0x1c) 0/r32/eax  # Var-register + 4
+    (copy-array Heap "eax" %eax)
+$test-compare-eax-with-literal:initialize-literal-type:
+    # var type/edx: (payload type-tree) = literal
+    68/push 0/imm32/right:null
+    68/push 0/imm32/right:null
+    68/push 0/imm32/left:unused
+    68/push 0/imm32/value:literal
+    68/push 1/imm32/is-atom?:true
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %edx 4/r32/esp
+$test-compare-eax-with-literal:initialize-literal:
+    # var l/edx: (payload var)
+    68/push 0/imm32/register
+    68/push 0/imm32/register
+    68/push 0/imm32/no-stack-offset
+    68/push 1/imm32/block-depth
+    52/push-edx
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/name
+    68/push 0/imm32/name
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %edx 4/r32/esp
+$test-compare-eax-with-literal:initialize-literal-value:
+    # l->name = "0x34"
+    8d/copy-address *(edx+4) 0/r32/eax  # Var-name + 4
+    (copy-array Heap "0x34" %eax)
+$test-compare-eax-with-literal:initialize-inouts:
+    # var inouts/esi: (payload stmt-var) = [l]
+    68/push 0/imm32/is-deref:false
+    68/push 0/imm32/next
+    68/push 0/imm32/next
+    52/push-edx/l
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %esi 4/r32/esp
+    # var inouts = (handle stmt-var) = [var1, var2]
+    68/push 0/imm32/is-deref:false
+    56/push-esi/next
+    68/push 0x11/imm32/alloc-id:fake
+    51/push-ecx/var1
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %esi 4/r32/esp
+$test-compare-eax-with-literal:initialize-stmt:
+    # var stmt/esi: (addr statement)
+    68/push 0/imm32/next
+    68/push 0/imm32/next
+    68/push 0/imm32/outputs
+    68/push 0/imm32/outputs
+    56/push-esi/inouts
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/operation
+    68/push 0/imm32/operation
+    68/push 1/imm32/tag:stmt1
+    89/<- %esi 4/r32/esp
+$test-compare-eax-with-literal:initialize-stmt-operation:
+    # stmt->operation = "compare"
+    8d/copy-address *(esi+4) 0/r32/eax  # Stmt1-operation
+    (copy-array Heap "compare" %eax)
+    # convert
+    c7 0/subop/copy *Curr-block-depth 0/imm32
+    (emit-subx-stmt _test-output-buffered-file %esi Primitives 0 Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "3d/compare-eax-with 0x34/imm32" "F - test-compare-eax-with-literal")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-compare-reg-with-literal:
+    #   compare var1/ecx 0x34
+    # =>
+    #   81 7/subop/compare %ecx 0x34/imm32
+    #
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+$test-compare-reg-with-literal:initialize-type:
+    # var type/ecx: (payload type-tree) = int
+    68/push 0/imm32/right:null
+    68/push 0/imm32/right:null
+    68/push 0/imm32/left:unused
+    68/push 1/imm32/value:int
+    68/push 1/imm32/is-atom?:true
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %ecx 4/r32/esp
+$test-compare-reg-with-literal:initialize-var1:
+    # var var1/ecx: (payload var)
+    68/push 0/imm32/register
+    68/push 0/imm32/register
+    68/push 0/imm32/no-stack-offset
+    68/push 1/imm32/block-depth
+    51/push-ecx
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/name
+    68/push 0/imm32/name
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %ecx 4/r32/esp
+$test-compare-reg-with-literal:initialize-var1-name:
+    # var1->name = "var1"
+    8d/copy-address *(ecx+4) 0/r32/eax  # Var-name + 4
+    (copy-array Heap "var1" %eax)
+$test-compare-reg-with-literal:initialize-var1-register:
+    # v->register = "ecx"
+    8d/copy-address *(ecx+0x1c) 0/r32/eax  # Var-register + 4
+    (copy-array Heap "ecx" %eax)
+$test-compare-reg-with-literal:initialize-literal-type:
+    # var type/edx: (payload type-tree) = literal
+    68/push 0/imm32/right:null
+    68/push 0/imm32/right:null
+    68/push 0/imm32/left:unused
+    68/push 0/imm32/value:literal
+    68/push 1/imm32/is-atom?:true
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %edx 4/r32/esp
+$test-compare-reg-with-literal:initialize-literal:
+    # var l/edx: (payload var)
+    68/push 0/imm32/register
+    68/push 0/imm32/register
+    68/push 0/imm32/no-stack-offset
+    68/push 1/imm32/block-depth
+    52/push-edx
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/name
+    68/push 0/imm32/name
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %edx 4/r32/esp
+$test-compare-reg-with-literal:initialize-literal-value:
+    # l->name = "0x34"
+    8d/copy-address *(edx+4) 0/r32/eax  # Var-name + 4
+    (copy-array Heap "0x34" %eax)
+$test-compare-reg-with-literal:initialize-inouts:
+    # var inouts/esi: (payload stmt-var) = [l]
+    68/push 0/imm32/is-deref:false
+    68/push 0/imm32/next
+    68/push 0/imm32/next
+    52/push-edx/l
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %esi 4/r32/esp
+    # var inouts = (handle stmt-var) = [var1, var2]
+    68/push 0/imm32/is-deref:false
+    56/push-esi/next
+    68/push 0x11/imm32/alloc-id:fake
+    51/push-ecx/var1
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %esi 4/r32/esp
+$test-compare-reg-with-literal:initialize-stmt:
+    # var stmt/esi: (addr statement)
+    68/push 0/imm32/next
+    68/push 0/imm32/next
+    68/push 0/imm32/outputs
+    68/push 0/imm32/outputs
+    56/push-esi/inouts
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/operation
+    68/push 0/imm32/operation
+    68/push 1/imm32/tag:stmt1
+    89/<- %esi 4/r32/esp
+$test-compare-reg-with-literal:initialize-stmt-operation:
+    # stmt->operation = "compare"
+    8d/copy-address *(esi+4) 0/r32/eax  # Stmt1-operation
+    (copy-array Heap "compare" %eax)
+    # convert
+    c7 0/subop/copy *Curr-block-depth 0/imm32
+    (emit-subx-stmt _test-output-buffered-file %esi Primitives 0 Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "81 7/subop/compare %ecx 0x34/imm32" "F - test-compare-reg-with-literal")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-emit-subx-stmt-function-call:
+    # Call a function on a variable on the stack.
+    #   f foo
+    # =>
+    #   (f *(ebp-8))
+    # (Changing the function name supports overloading in general, but here it
+    # just serves to help disambiguate things.)
+    #
+    # There's a variable on the var stack as follows:
+    #   name: 'foo'
+    #   type: int
+    #   stack-offset: -8
+    #
+    # There's nothing in primitives.
+    #
+    # We don't perform any checking here on the type of 'f'.
+    #
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+$test-emit-subx-function-call:initialize-type:
+    # var type/ecx: (payload type-tree) = int
+    68/push 0/imm32/right:null
+    68/push 0/imm32/right:null
+    68/push 0/imm32/left:unused
+    68/push 1/imm32/value:int
+    68/push 1/imm32/is-atom?:true
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %ecx 4/r32/esp
+$test-emit-subx-function-call:initialize-var:
+    # var var-foo/ecx: (payload var) = var(type)
+    68/push 0/imm32/no-register
+    68/push 0/imm32/no-register
+    68/push -8/imm32/stack-offset
+    68/push 1/imm32/block-depth
+    51/push-ecx/type
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/name
+    68/push 0/imm32/name
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %ecx 4/r32/esp
+$test-emit-subx-function-call:initialize-var-name:
+    # var-foo->name = "foo"
+    8d/copy-address *(ecx+4) 0/r32/eax  # Var-name + 4
+    (copy-array Heap "foo" %eax)
+$test-emit-subx-function-call:initialize-stmt-var:
+    # var operand/ebx: (payload stmt-var) = stmt-var(var-foo)
+    68/push 0/imm32/is-deref:false
+    68/push 0/imm32/next
+    68/push 0/imm32/next
+    51/push-ecx/var-foo
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %ebx 4/r32/esp
+$test-emit-subx-function-call:initialize-stmt:
+    # var stmt/esi: (addr statement)
+    68/push 0/imm32/no-outputs
+    68/push 0/imm32/no-outputs
+    53/push-ebx/inouts
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/operation
+    68/push 0/imm32/operation
+    68/push 1/imm32/tag
+    89/<- %esi 4/r32/esp
+$test-emit-subx-function-call:initialize-stmt-operation:
+    # stmt->operation = "f"
+    8d/copy-address *(esi+4) 0/r32/eax  # Stmt1-operation
+    (copy-array Heap "f" %eax)
+    # convert
+    c7 0/subop/copy *Curr-block-depth 0/imm32
+    (emit-subx-stmt _test-output-buffered-file %esi 0 0 Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "(f *(ebp+0xfffffff8))" "F - test-emit-subx-stmt-function-call")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-emit-subx-stmt-function-call-with-literal-arg:
+    # Call a function on a literal.
+    #   f 0x34
+    # =>
+    #   (f2 0x34)
+    #
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+$test-emit-subx-function-call-with-literal-arg:initialize-type:
+    # var type/ecx: (payload type-tree) = int
+    68/push 0/imm32/right:null
+    68/push 0/imm32/right:null
+    68/push 0/imm32/left:unused
+    68/push 0/imm32/value:literal
+    68/push 1/imm32/is-atom?:true
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %ecx 4/r32/esp
+$test-emit-subx-function-call-with-literal-arg:initialize-var:
+    # var var-foo/ecx: (payload var) = var(lit)
+    68/push 0/imm32/no-register
+    68/push 0/imm32/no-register
+    68/push 0/imm32/no-stack-offset
+    68/push 1/imm32/block-depth
+    51/push-ecx/type
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/name
+    68/push 0/imm32/name
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %ecx 4/r32/esp
+$test-emit-subx-function-call-with-literal-arg:initialize-var-name:
+    # var-foo->name = "0x34"
+    8d/copy-address *(ecx+4) 0/r32/eax  # Var-name + 4
+    (copy-array Heap "0x34" %eax)
+$test-emit-subx-function-call-with-literal-arg:initialize-stmt-var:
+    # var operand/ebx: (payload stmt-var) = stmt-var(var-foo)
+    68/push 0/imm32/is-deref:false
+    68/push 0/imm32/next
+    68/push 0/imm32/next
+    51/push-ecx/var-foo
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0x11/imm32/alloc-id:fake:payload
+    89/<- %ebx 4/r32/esp
+$test-emit-subx-function-call-with-literal-arg:initialize-stmt:
+    # var stmt/esi: (addr statement)
+    68/push 0/imm32/no-outputs
+    68/push 0/imm32/no-outputs
+    53/push-ebx/inouts
+    68/push 0x11/imm32/alloc-id:fake
+    68/push 0/imm32/operation
+    68/push 0/imm32/operation
+    68/push 1/imm32/tag
+    89/<- %esi 4/r32/esp
+$test-emit-subx-function-call-with-literal-arg:initialize-stmt-operation:
+    # stmt->operation = "f"
+    8d/copy-address *(esi+4) 0/r32/eax  # Stmt1-operation
+    (copy-array Heap "f" %eax)
+    # convert
+    c7 0/subop/copy *Curr-block-depth 0/imm32
+    (emit-subx-stmt _test-output-buffered-file %esi 0 %ebx 0 Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "(f 0x34)" "F - test-emit-subx-stmt-function-call-with-literal-arg")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+emit-indent:  # out: (addr buffered-file), n: int
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    # var i/eax: int = n
+    8b/-> *(ebp+0xc) 0/r32/eax
+    {
+      # if (i <= 0) break
+      3d/compare-eax-with 0/imm32
+      7e/jump-if-<= break/disp8
+      (write-buffered *(ebp+8) "  ")
+      48/decrement-eax
+      eb/jump loop/disp8
+    }
+$emit-indent:end:
+    # . restore registers
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+emit-subx-prologue:  # out: (addr buffered-file)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    #
+    (write-buffered *(ebp+8) "  # . prologue\n")
+    (write-buffered *(ebp+8) "  55/push-ebp\n")
+    (write-buffered *(ebp+8) "  89/<- %ebp 4/r32/esp\n")
+$emit-subx-prologue:end:
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+emit-subx-epilogue:  # out: (addr buffered-file)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    #
+    (write-buffered *(ebp+8) "  # . epilogue\n")
+    (write-buffered *(ebp+8) "  89/<- %esp 5/r32/ebp\n")
+    (write-buffered *(ebp+8) "  5d/pop-to-ebp\n")
+    (write-buffered *(ebp+8) "  c3/return\n")
+$emit-subx-epilogue:end:
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return