https://github.com/akkartik/mu/blob/master/apps/mu.subx
    1 # The Mu computer's level-2 language, also called Mu.
    2 # http://akkartik.name/post/mu-2019-2
    3 #
    4 # To run:
    5 #   $ ./translate_subx init.linux [0-9]*.subx apps/mu.subx
    6 
    7 # == Goals
    8 # 1. Be memory safe. It should be impossible to corrupt the heap, or to create
    9 # a bad pointer. (Requires strong type safety.)
   10 # 2. Do as little as possible to achieve goal 1. The translator should be
   11 # implementable in machine code.
   12 #   - minimize impedance mismatch between source language and SubX target
   13 #     (e.g. programmer manages registers manually)
   14 #   - checks over syntax
   15 #     (e.g. programmer's register allocation is checked)
   16 #   - runtime checks to avoid complex static analysis
   17 #     (e.g. array indexing always checks bounds)
   18 
   19 # == Language description
   20 # A program is a sequence of function and type definitions.
   21 #
   22 # Function example:
   23 #   fn foo n: int -> result/eax: int {
   24 #     ...
   25 #   }
   26 #
   27 # Functions consist of a name, optional inputs, optional outputs and a block.
   28 #
   29 # Function inputs and outputs are variables. All variables have a type and
   30 # storage specifier. They can be placed either in memory (on the stack) or in
   31 # one of 6 named registers.
   32 #   eax ecx edx ebx esi edi
   33 # Variables in registers must be primitive 32-bit types.
   34 # Variables not explicitly placed in a register are on the stack.
   35 #
   36 # Function inputs are always passed in memory (on the stack), while outputs
   37 # are always returned in registers.
   38 #
   39 # Blocks mostly consist of statements.
   40 #
   41 # Statements mostly consist of a name, optional inputs and optional outputs.
   42 #
   43 # Statement inputs are variables or literals. Variables need to specify type
   44 # (and storage) the first time they're mentioned but not later.
   45 #
   46 # Statement outputs, like function outputs, must be variables in registers.
   47 #
   48 # Statement names must be either primitives or user-defined functions.
   49 #
   50 # Primitives can write to any register.
   51 # User-defined functions only write to hard-coded registers. Outputs of each
   52 # call must have the same registers as in the function definition.
   53 #
   54 # There are some other statement types:
   55 #   - blocks. Multiple statements surrounded by '{...}' and optionally
   56 #     prefixed with a label name and ':'
   57 #       - {
   58 #           ...
   59 #         }
   60 #       - foo: {
   61 #           ...
   62 #         }
   63 #
   64 #   - variable definitions on the stack. E.g.:
   65 #       - var foo: int
   66 #       - var bar: (array int 3)
   67 #     There's no initializer; variables are automatically initialized.
   68 #     The type of a local variable is either word-length (4 bytes) or starts with 'ref'.
   69 #
   70 #   - variables definitions in a register. E.g.:
   71 #       - var foo/eax: int <- add bar 1
   72 #     The initializer is mandatory and must be a valid instruction that writes
   73 #     a single output to the right register. In practice registers will
   74 #     usually be either initialized by primitives or copied from eax.
   75 #       - var eax: int <- foo bar quux
   76 #         var floo/ecx: int <- copy eax
   77 #
   78 # Still todo:
   79 #   global variables
   80 #   heap allocations (planned name: 'handle')
   81 #   user-defined types: 'type' for structs, 'choice' for unions
   82 #   short-lived 'address' type for efficiently writing inside nested structs
   83 #
   84 # We don't have 'handle' types yet, but we try to distinguish 'ref', 'handle'
   85 # and 'address' in comments. Their definitions are in layer 50, but really you
   86 # can ignore the distinctions on a first reading of this program.
   87 #
   88 # Formal types:
   89 #   A program is a linked list of functions
   90 #   A function contains:
   91 #     name: (handle array byte)
   92 #     inouts: linked list of vars  <-- 'inouts' is more precise than 'inputs'
   93 #       data: (handle var)
   94 #       next: (handle list)
   95 #     outputs: linked list of vars
   96 #       data: (handle var)
   97 #       next: (handle list)
   98 #     body: (handle block)
   99 #   A var-type contains:
  100 #     name: (handle array byte)
  101 #     type: (handle tree type-id)
  102 #
  103 #   A statement can be:
  104 #     tag 0: a block
  105 #     tag 1: a simple statement (stmt1)
  106 #     tag 2: a variable defined on the stack
  107 #     tag 3: a variable defined in a register
  108 #
  109 #   A block contains:
  110 #     tag: 0
  111 #     statements: (handle list stmt)
  112 #     name: (handle array byte) -- starting with '$'
  113 #
  114 #   A regular statement contains:
  115 #     tag: 1
  116 #     operation: (handle array byte)
  117 #     inouts: (handle list operand)
  118 #     outputs: (handle list var)
  119 #
  120 #   A variable defined on the stack contains:
  121 #     tag: 2
  122 #     name: (handle array byte)
  123 #     type: (handle tree type-id)
  124 #
  125 #   A variable defined in a register contains:
  126 #     tag: 3
  127 #     name: (handle array byte)
  128 #     type: (handle tree type-id)
  129 #     reg: (handle array byte)
  130 
  131 # == Translation: managing the stack
  132 # Now that we know what the language looks like in the large, let's think
  133 # about how translation happens from the bottom up. One crucial piece of the
  134 # puzzle is how Mu will clean up variables defined on the stack for you.
  135 #
  136 # Assume that we maintain a 'functions' list while parsing source code. And a
  137 # 'primitives' list is a global constant. Both these contain enough information
  138 # to perform type-checking on function calls or primitive statements, respectively.
  139 #
  140 # Defining variables pushes them on a stack with the current block depth and
  141 # enough information about their location (stack offset or register).
  142 # Starting a block increments the current block id.
  143 # Each statement now has enough information to emit code for it.
  144 # Ending a block is where the magic happens:
  145 #   pop all variables at the current block depth
  146 #   emit code to restore all register variables introduced at the current depth
  147 #   emit code to clean up all stack variables at the current depth (just increment esp)
  148 #   decrement the current block depth
  149 #
  150 # Formal types:
  151 #   live-vars: stack of vars
  152 #   var:
  153 #     name: (handle array byte)
  154 #     type: (handle tree type-id)
  155 #     block: int
  156 #     stack-offset: int  (added to ebp)
  157 #     register: (handle array byte)
  158 #       either usual register names
  159 #       or '*' to indicate any register
  160 #   At most one of stack-offset or register-index must be non-zero.
  161 #   A register of '*' designates a variable _template_. Only legal in formal
  162 #   parameters for primitives.
  163 
  164 # == Translating a single function call
  165 # This one's easy. Assuming we've already checked things, we just drop the
  166 # outputs (which use hard-coded registers) and emit inputs in a standard format.
  167 #
  168 # out1, out2, out3, ... <- name inout1, inout2, inout3, ...
  169 # =>
  170 # (subx-name inout1 inout2 inout3)
  171 #
  172 # Formal types:
  173 #   functions: linked list of info
  174 #     name: (handle array byte)
  175 #     inouts: linked list of vars
  176 #     outputs: linked list of vars
  177 #     body: block (singleton linked list)
  178 #     subx-name: (handle array byte)
  179 
  180 # == Translating a single primitive instruction
  181 # A second crucial piece of the puzzle is how Mu converts fairly regular
  182 # primitives with their uniform syntax to SubX instructions with their gnarly
  183 # x86 details.
  184 #
  185 # Mu instructions have inputs and outputs. Primitives can have up to 2 of
  186 # them.
  187 # SubX instructions have rm32 and r32 operands.
  188 # The translation between them covers almost all the possibilities.
  189 #   Instructions with 1 inout may turn into ones with 1 rm32
  190 #     (e.g. incrementing a var on the stack)
  191 #   Instructions with 1 output may turn into ones with 1 rm32
  192 #     (e.g. incrementing a var in a register)
  193 #   1 inout and 1 output may turn into 1 rm32 and 1 r32
  194 #     (e.g. adding a var to a reg)
  195 #   2 inouts may turn into 1 rm32 and 1 r32
  196 #     (e.g. adding a reg to a var)
  197 #   1 inout and 1 literal may turn into 1 rm32 and 1 imm32
  198 #     (e.g. adding a constant to a var)
  199 #   1 output and 1 literal may turn into 1 rm32 and 1 imm32
  200 #     (e.g. adding a constant to a reg)
  201 #   2 outputs to hardcoded registers and 1 inout may turn into 1 rm32
  202 #     (special-case: divide edx:eax by a var or reg)
  203 # Observations:
  204 #   We always emit rm32. It may be the first inout or the first output.
  205 #   We may emit r32 or imm32 or neither.
  206 #   When we emit r32 it may come from first inout or second inout or first output.
  207 #
  208 # Accordingly, the formal data structure for a primitive looks like this:
  209 #   primitives: linked list of info
  210 #     name: (handle array byte)
  211 #     mu-inouts: linked list of vars to check
  212 #     mu-outputs: linked list of vars to check; at most a singleton
  213 #     subx-name: (handle array byte)
  214 #     subx-rm32: enum arg-location
  215 #     subx-r32: enum arg-location
  216 #     subx-imm32: enum arg-location
  217 #     subx-disp32: enum arg-location
  218 #     output-is-write-only: boolean
  219 #   arg-location: enum
  220 #     0 means none
  221 #     1 means first inout
  222 #     2 means second inout
  223 #     3 means first output
  224 
  225 # == Translating a block
  226 # Emit block name if necessary
  227 # Emit '{'
  228 # When you encounter a statement, emit it as above
  229 # When you encounter a variable declaration
  230 #   emit any code needed for it (bzeros)
  231 #   push it on the var stack
  232 #   update register dict if necessary
  233 # When you encounter '}'
  234 #   While popping variables off the var stack until block id changes
  235 #     Emit code needed to clean up the stack
  236 #       either increment esp
  237 #       or pop into appropriate register
  238 
  239 # The rest is straightforward.
  240 
  241 == data
  242 
  243 Program:
  244 _Program-functions:  # (handle function)
  245   0/imm32
  246 _Program-types:  # (handle typeinfo)
  247   0/imm32
  248 
  249 # Some constants for simulating the data structures described above.
  250 # Many constants here come with a type in a comment.
  251 #
  252 # Sometimes the type is of the value at that offset for the given type. For
  253 # example, if you start at a function record and move forward Function-inouts
  254 # bytes, you'll find a (handle list var).
  255 #
  256 # At other times, the type is of the constant itself. For example, the type of
  257 # the constant Function-size is (addr int). To get the size of a function,
  258 # look in *Function-size.
  259 
  260 Function-name:  # (handle array byte)
  261   0/imm32
  262 _Function-unused:
  263   4/imm32
  264 Function-inouts:  # (handle list var)
  265   8/imm32
  266 Function-outputs:  # (handle list var)
  267   0xc/imm32
  268 Function-body:  # (handle block)
  269   0x10/imm32
  270 Function-next:  # (handle function)
  271   0x14/imm32
  272 Function-size:  # (addr int)
  273   0x18/imm32/24
  274 
  275 Primitive-name:  # (handle array byte)
  276   0/imm32
  277 Primitive-inouts:  # (handle list var)
  278   4/imm32
  279 Primitive-outputs:  # (handle list var)
  280   8/imm32
  281 Primitive-subx-name:  # (handle array byte)
  282   0xc/imm32
  283 Primitive-subx-rm32:  # enum arg-location
  284   0x10/imm32
  285 Primitive-subx-r32:  # enum arg-location
  286   0x14/imm32
  287 Primitive-subx-imm32:  # enum arg-location
  288   0x18/imm32
  289 Primitive-subx-disp32:  # enum arg-location  -- only for branches
  290   0x1c/imm32
  291 Primitive-output-is-write-only:  # boolean
  292   0x20/imm32
  293 Primitive-next:  # (handle function)
  294   0x24/imm32
  295 Primitive-size:  # (addr int)
  296   0x28/imm32/36
  297 
  298 Stmt-tag:  # int
  299   0/imm32
  300 
  301 Block-stmts:  # (handle list stmt)
  302   4/imm32
  303 Block-var:  # (handle var)
  304   8/imm32
  305 
  306 Stmt1-operation:  # (handle array byte)
  307   4/imm32
  308 Stmt1-inouts:  # (handle stmt-var)
  309   8/imm32
  310 Stmt1-outputs:  # (handle stmt-var)
  311   0xc/imm32
  312 
  313 Vardef-var:  # (handle var)
  314   4/imm32
  315 
  316 Regvardef-operation:  # (handle array byte)
  317   4/imm32
  318 Regvardef-inouts:  # (handle stmt-var)
  319   8/imm32
  320 Regvardef-outputs:  # (handle stmt-var)  # will have exactly one element
  321   0xc/imm32
  322 
  323 Stmt-size:  # (addr int)
  324   0x10/imm32
  325 
  326 Var-name:  # (handle array byte)
  327   0/imm32
  328 Var-type:  # (handle tree type-id)
  329   4/imm32
  330 Var-block-depth:  # int -- not available until code-generation time
  331   8/imm32
  332 Var-offset:  # int -- not available until code-generation time
  333   0xc/imm32
  334 Var-register:  # (handle array byte) -- name of a register
  335   0x10/imm32
  336 Var-size:  # (addr int)
  337   0x14/imm32
  338 
  339 Any-register:  # wildcard
  340   # size
  341   1/imm32
  342   # data
  343   2a/asterisk
  344 
  345 List-value:
  346   0/imm32
  347 List-next:  # (handle list _)
  348   4/imm32
  349 List-size:  # (addr int)
  350   8/imm32
  351 
  352 # A stmt-var is like a list of vars with call-site specific metadata
  353 Stmt-var-value:  # (handle var)
  354   0/imm32
  355 Stmt-var-next:  # (handle stmt-var)
  356   4/imm32
  357 Stmt-var-is-deref:  # boolean
  358   8/imm32
  359 Stmt-var-size:  # (addr int)
  360   0xc/imm32
  361 
  362 # Types are expressed as trees (s-expressions) of type-ids (ints).
  363 # However, there's no need for singletons, so we can assume (int) == int
  364 #   - if x->right == nil, x is an atom
  365 #   - x->left contains either a pointer to a pair, or an atomic type-id directly.
  366 
  367 Tree-is-atom:  # boolean
  368   0/imm32
  369 # if left-is-atom?
  370 Tree-value:  # type-id
  371   4/imm32
  372 # unless left-is-atom?
  373 Tree-left:  # (addr tree type-id)
  374   4/imm32
  375 Tree-right:  # (addr tree type-id)
  376   8/imm32
  377 #
  378 Tree-size:  # (addr int)
  379   0xc/imm32
  380 
  381 # Types
  382 
  383 Type-id:  # (stream (address array byte))
  384   0x1c/imm32/write
  385   0/imm32/read
  386   0x100/imm32/size
  387   # data
  388   "literal"/imm32  # 0
  389   "int"/imm32  # 1
  390   "addr"/imm32  # 2
  391   "array"/imm32  # 3
  392   "handle"/imm32  # 4
  393   "boolean"/imm32  # 5
  394   "constant"/imm32  # 6: like a literal, but replaced with its value in Var-offset
  395   "offset"/imm32  # 7: (offset T) is guaranteed to be a 32-bit multiple of size-of(T)
  396   0/imm32
  397   # 0x20
  398   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
  399   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
  400   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
  401   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
  402   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
  403   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
  404   0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
  405 
  406 # == Type definitions
  407 # Program->types contains some typeinfo for each type definition.
  408 # Types contain vars with types, but can't specify registers.
  409 Typeinfo-id:  # type-id
  410   0/imm32
  411 Typeinfo-fields:  # (handle table string (handle typeinfo-entry))
  412   4/imm32
  413 # Total size must be >= 0
  414 # During parsing it may take on two additional values:
  415 #   -2: not yet initialized
  416 #   -1: in process of being computed
  417 # See populate-mu-type-sizes for details.
  418 Typeinfo-total-size-in-bytes:  # int
  419   8/imm32
  420 Typeinfo-next:  # (handle typeinfo)
  421   0xc/imm32
  422 Typeinfo-size:  # (addr int)
  423   0x10/imm32
  424 
  425 # Each entry in the typeinfo->fields table has a pointer to a string and a
  426 # pointer to a typeinfo-entry.
  427 Typeinfo-fields-row-size:  # (addr int)
  428   8/imm32
  429 
  430 # typeinfo-entry objects have information about a field in a single record type
  431 #
  432 # each field of a type is represented using two var's:
  433 #   1. the input var: expected type of the field; convenient for creating using parse-var-with-type
  434 #   2. the output var: a constant containing the byte offset; convenient for code-generation
  435 # computing the output happens after parsing; in the meantime we preserve the
  436 # order of fields in the 'index' field.
  437 Typeinfo-entry-input-var:  # (handle var)
  438   0/imm32
  439 Typeinfo-entry-index:  # int
  440   4/imm32
  441 Typeinfo-entry-output-var:  # (handle var)
  442   8/imm32
  443 Typeinfo-entry-size:  # (addr int)
  444   0xc/imm32
  445 
  446 == code
  447 
  448 Entry:
  449     # . prologue
  450     89/<- %ebp 4/r32/esp
  451     (new-segment *Heap-size Heap)
  452     # if (argv[1] == "test') run-tests()
  453     {
  454       # if (argc <= 1) break
  455       81 7/subop/compare *ebp 1/imm32
  456       7e/jump-if-<= break/disp8
  457       # if (argv[1] != "test") break
  458       (kernel-string-equal? *(ebp+8) "test")  # => eax
  459       3d/compare-eax-and 0/imm32/false
  460       74/jump-if-= break/disp8
  461       #
  462       (run-tests)
  463       # syscall(exit, *Num-test-failures)
  464       8b/-> *Num-test-failures 3/r32/ebx
  465       eb/jump $mu-main:end/disp8
  466     }
  467     # otherwise convert Stdin
  468     (convert-mu Stdin Stdout)
  469     (flush Stdout)
  470     # syscall(exit, 0)
  471     bb/copy-to-ebx 0/imm32
  472 $mu-main:end:
  473     b8/copy-to-eax 1/imm32/exit
  474     cd/syscall 0x80/imm8
  475 
  476 convert-mu:  # in: (addr buffered-file), out: (addr buffered-file)
  477     # . prologue
  478     55/push-ebp
  479     89/<- %ebp 4/r32/esp
  480     # initialize global data structures
  481     c7 0/subop/copy *Next-block-index 1/imm32
  482     c7 0/subop/copy *Type-id 0x1c/imm32  # stream-write
  483     c7 0/subop/copy *_Program-functions 0/imm32
  484     c7 0/subop/copy *_Program-types 0/imm32
  485     #
  486     (parse-mu *(ebp+8))
  487     (populate-mu-type-sizes)
  488     (check-mu-types)
  489     (emit-subx *(ebp+0xc))
  490 $convert-mu:end:
  491     # . epilogue
  492     89/<- %esp 5/r32/ebp
  493     5d/pop-to-ebp
  494     c3/return
  495 
  496 test-convert-empty-input:
  497     # empty input => empty output
  498     # . prologue
  499     55/push-ebp
  500     89/<- %ebp 4/r32/esp
  501     # setup
  502     (clear-stream _test-input-stream)
  503     (clear-stream $_test-input-buffered-file->buffer)
  504     (clear-stream _test-output-stream)
  505     (clear-stream $_test-output-buffered-file->buffer)
  506     #
  507     (convert-mu _test-input-buffered-file _test-output-buffered-file)
  508     (flush _test-output-buffered-file)
  509     (check-stream-equal _test-output-stream "" "F - test-convert-empty-input")
  510     # . epilogue
  511     89/<- %esp 5/r32/ebp
  512     5d/pop-to-ebp
  513     c3/return
  514 
  515 test-convert-function-skeleton:
  516     # . prologue
  517     55/push-ebp
  518     89/<- %ebp 4/r32/esp
  519     # setup
  520     (clear-stream _test-input-stream)
  521     (clear-stream $_test-input-buffered-file->buffer)
  522     (clear-stream _test-output-stream)
  523     (clear-stream $_test-output-buffered-file->buffer)
  524     #
  525     (write _test-input-stream "fn foo {\n")
  526     (write _test-input-stream "}\n")
  527     # convert
  528     (convert-mu _test-input-buffered-file _test-output-buffered-file)
  529     (flush _test-output-buffered-file)
  530 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
  536     # check output
  537     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-skeleton/0")
  538     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-skeleton/1")
  539     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-skeleton/2")
  540     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-skeleton/3")
  541     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-skeleton/4")
  542     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-skeleton/5")
  543     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-skeleton/6")
  544     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-skeleton/7")
  545     # . epilogue
  546     89/<- %esp 5/r32/ebp
  547     5d/pop-to-ebp
  548     c3/return
  549 
  550 test-convert-multiple-function-skeletons:
  551     # . prologue
  552     55/push-ebp
  553     89/<- %ebp 4/r32/esp
  554     # setup
  555     (clear-stream _test-input-stream)
  556     (clear-stream $_test-input-buffered-file->buffer)
  557     (clear-stream _test-output-stream)
  558     (clear-stream $_test-output-buffered-file->buffer)
  559     #
  560     (write _test-input-stream "fn foo {\n")
  561     (write _test-input-stream "}\n")
  562     (write _test-input-stream "fn bar {\n")
  563     (write _test-input-stream "}\n")
  564     # convert
  565     (convert-mu _test-input-buffered-file _test-output-buffered-file)
  566     (flush _test-output-buffered-file)
  567 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
  573     # check first function
  574     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-multiple-function-skeletons/0")
  575     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-multiple-function-skeletons/1")
  576     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-multiple-function-skeletons/2")
  577     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-multiple-function-skeletons/3")
  578     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-multiple-function-skeletons/4")
  579     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-multiple-function-skeletons/5")
  580     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-multiple-function-skeletons/6")
  581     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-multiple-function-skeletons/7")
  582     # check second function
  583     (check-next-stream-line-equal _test-output-stream "bar:"                    "F - test-convert-multiple-function-skeletons/10")
  584     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-multiple-function-skeletons/11")
  585     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-multiple-function-skeletons/12")
  586     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-multiple-function-skeletons/13")
  587     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-multiple-function-skeletons/14")
  588     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-multiple-function-skeletons/15")
  589     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-multiple-function-skeletons/16")
  590     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-multiple-function-skeletons/17")
  591     # . epilogue
  592     89/<- %esp 5/r32/ebp
  593     5d/pop-to-ebp
  594     c3/return
  595 
  596 test-convert-function-with-arg:
  597     # . prologue
  598     55/push-ebp
  599     89/<- %ebp 4/r32/esp
  600     # setup
  601     (clear-stream _test-input-stream)
  602     (clear-stream $_test-input-buffered-file->buffer)
  603     (clear-stream _test-output-stream)
  604     (clear-stream $_test-output-buffered-file->buffer)
  605     #
  606     (write _test-input-stream "fn foo n: int {\n")
  607     (write _test-input-stream "}\n")
  608     # convert
  609     (convert-mu _test-input-buffered-file _test-output-buffered-file)
  610     (flush _test-output-buffered-file)
  611 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
  617     # check output
  618     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-arg/0")
  619     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-arg/1")
  620     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-arg/2")
  621     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-arg/3")
  622     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-arg/4")
  623     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-arg/5")
  624     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-arg/6")
  625     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-arg/7")
  626     # . epilogue
  627     89/<- %esp 5/r32/ebp
  628     5d/pop-to-ebp
  629     c3/return
  630 
  631 test-convert-function-with-arg-and-body:
  632     # . prologue
  633     55/push-ebp
  634     89/<- %ebp 4/r32/esp
  635     # setup
  636     (clear-stream _test-input-stream)
  637     (clear-stream $_test-input-buffered-file->buffer)
  638     (clear-stream _test-output-stream)
  639     (clear-stream $_test-output-buffered-file->buffer)
  640     #
  641     (write _test-input-stream "fn foo n: int {\n")
  642     (write _test-input-stream "  increment n\n")
  643     (write _test-input-stream "}\n")
  644     # convert
  645     (convert-mu _test-input-buffered-file _test-output-buffered-file)
  646     (flush _test-output-buffered-file)
  647 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
  653     # check output
  654     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-arg-and-body/0")
  655     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-arg-and-body/1")
  656     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-arg-and-body/2")
  657     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-arg-and-body/3")
  658     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-arg-and-body/4")
  659     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-arg-and-body/5")
  660     (check-next-stream-line-equal _test-output-stream "    ff 0/subop/increment *(ebp+0x00000008)"  "F - test-convert-function-with-arg-and-body/6")
  661     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-arg-and-body/7")
  662     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-arg-and-body/8")
  663     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-arg-and-body/9")
  664     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-arg-and-body/10")
  665     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-arg-and-body/11")
  666     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-arg-and-body/12")
  667     # . epilogue
  668     89/<- %esp 5/r32/ebp
  669     5d/pop-to-ebp
  670     c3/return
  671 
  672 test-convert-function-distinguishes-args:
  673     # . prologue
  674     55/push-ebp
  675     89/<- %ebp 4/r32/esp
  676     # setup
  677     (clear-stream _test-input-stream)
  678     (clear-stream $_test-input-buffered-file->buffer)
  679     (clear-stream _test-output-stream)
  680     (clear-stream $_test-output-buffered-file->buffer)
  681     #
  682     (write _test-input-stream "fn foo a: int, b: int {\n")
  683     (write _test-input-stream "  increment b\n")
  684     (write _test-input-stream "}\n")
  685     # convert
  686     (convert-mu _test-input-buffered-file _test-output-buffered-file)
  687     (flush _test-output-buffered-file)
  688 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
  694     # check output
  695     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-distinguishes-args/0")
  696     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-distinguishes-args/1")
  697     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-distinguishes-args/2")
  698     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-distinguishes-args/3")
  699     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-distinguishes-args/4")
  700     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-distinguishes-args/5")
  701     (check-next-stream-line-equal _test-output-stream "    ff 0/subop/increment *(ebp+0x0000000c)"  "F - test-convert-function-distinguishes-args/6")
  702     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-distinguishes-args/7")
  703     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-distinguishes-args/8")
  704     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-distinguishes-args/9")
  705     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-distinguishes-args/10")
  706     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-distinguishes-args/11")
  707     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-distinguishes-args/12")
  708     # . epilogue
  709     89/<- %esp 5/r32/ebp
  710     5d/pop-to-ebp
  711     c3/return
  712 
  713 test-convert-function-returns-result:
  714     # . prologue
  715     55/push-ebp
  716     89/<- %ebp 4/r32/esp
  717     # setup
  718     (clear-stream _test-input-stream)
  719     (clear-stream $_test-input-buffered-file->buffer)
  720     (clear-stream _test-output-stream)
  721     (clear-stream $_test-output-buffered-file->buffer)
  722     #
  723     (write _test-input-stream "fn foo a: int, b: int -> result/eax: int {\n")
  724     (write _test-input-stream "  result <- copy a\n")
  725     (write _test-input-stream "  result <- increment\n")
  726     (write _test-input-stream "}\n")
  727     # convert
  728     (convert-mu _test-input-buffered-file _test-output-buffered-file)
  729     (flush _test-output-buffered-file)
  730 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
  736     # check output
  737     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-returns-result/0")
  738     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-returns-result/1")
  739     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-returns-result/2")
  740     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-returns-result/3")
  741     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-returns-result/4")
  742     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-returns-result/5")
  743     (check-next-stream-line-equal _test-output-stream "    8b/-> *(ebp+0x00000008) 0x00000000/r32"  "F - test-convert-function-returns-result/6")
  744     (check-next-stream-line-equal _test-output-stream "    40/increment-eax"    "F - test-convert-function-returns-result/7")
  745     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-returns-result/8")
  746     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-returns-result/9")
  747     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-returns-result/10")
  748     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-returns-result/11")
  749     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-returns-result/12")
  750     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-returns-result/13")
  751     # . epilogue
  752     89/<- %esp 5/r32/ebp
  753     5d/pop-to-ebp
  754     c3/return
  755 
  756 test-convert-function-with-literal-arg:
  757     # . prologue
  758     55/push-ebp
  759     89/<- %ebp 4/r32/esp
  760     # setup
  761     (clear-stream _test-input-stream)
  762     (clear-stream $_test-input-buffered-file->buffer)
  763     (clear-stream _test-output-stream)
  764     (clear-stream $_test-output-buffered-file->buffer)
  765     #
  766     (write _test-input-stream "fn foo a: int, b: int -> result/eax: int {\n")
  767     (write _test-input-stream "  result <- copy a\n")
  768     (write _test-input-stream "  result <- add 1\n")
  769     (write _test-input-stream "}\n")
  770     # convert
  771     (convert-mu _test-input-buffered-file _test-output-buffered-file)
  772     (flush _test-output-buffered-file)
  773 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
  779     # check output
  780     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-literal-arg/0")
  781     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-literal-arg/1")
  782     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-literal-arg/2")
  783     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-literal-arg/3")
  784     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-literal-arg/4")
  785     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-literal-arg/5")
  786     (check-next-stream-line-equal _test-output-stream "    8b/-> *(ebp+0x00000008) 0x00000000/r32"  "F - test-convert-function-with-literal-arg/6")
  787     (check-next-stream-line-equal _test-output-stream "    05/add-to-eax 1/imm32"  "F - test-convert-function-with-literal-arg/7")
  788     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-literal-arg/8")
  789     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-literal-arg/9")
  790     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-literal-arg/10")
  791     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-literal-arg/11")
  792     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-literal-arg/12")
  793     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-literal-arg/13")
  794     # . epilogue
  795     89/<- %esp 5/r32/ebp
  796     5d/pop-to-ebp
  797     c3/return
  798 
  799 test-convert-function-with-literal-arg-2:
  800     # . prologue
  801     55/push-ebp
  802     89/<- %ebp 4/r32/esp
  803     # setup
  804     (clear-stream _test-input-stream)
  805     (clear-stream $_test-input-buffered-file->buffer)
  806     (clear-stream _test-output-stream)
  807     (clear-stream $_test-output-buffered-file->buffer)
  808     #
  809     (write _test-input-stream "fn foo a: int, b: int -> result/ebx: int {\n")
  810     (write _test-input-stream "  result <- copy a\n")
  811     (write _test-input-stream "  result <- add 1\n")
  812     (write _test-input-stream "}\n")
  813     # convert
  814     (convert-mu _test-input-buffered-file _test-output-buffered-file)
  815     (flush _test-output-buffered-file)
  816 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
  822     # check output
  823     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-literal-arg-2/0")
  824     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-literal-arg-2/1")
  825     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-literal-arg-2/2")
  826     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-literal-arg-2/3")
  827     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-literal-arg-2/4")
  828     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-literal-arg-2/5")
  829     (check-next-stream-line-equal _test-output-stream "    8b/-> *(ebp+0x00000008) 0x00000003/r32"  "F - test-convert-function-with-literal-arg-2/6")
  830     (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %ebx 1/imm32"  "F - test-convert-function-with-literal-arg-2/7")
  831     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-literal-arg-2/8")
  832     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-literal-arg-2/9")
  833     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-literal-arg-2/10")
  834     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-literal-arg-2/11")
  835     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-literal-arg-2/12")
  836     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-literal-arg-2/13")
  837     # . epilogue
  838     89/<- %esp 5/r32/ebp
  839     5d/pop-to-ebp
  840     c3/return
  841 
  842 test-convert-function-call-with-literal-arg:
  843     # . prologue
  844     55/push-ebp
  845     89/<- %ebp 4/r32/esp
  846     # setup
  847     (clear-stream _test-input-stream)
  848     (clear-stream $_test-input-buffered-file->buffer)
  849     (clear-stream _test-output-stream)
  850     (clear-stream $_test-output-buffered-file->buffer)
  851     #
  852     (write _test-input-stream "fn main -> result/ebx: int {\n")
  853     (write _test-input-stream "  result <- do-add 3 4\n")
  854     (write _test-input-stream "}\n")
  855     (write _test-input-stream "fn do-add a: int, b: int -> result/ebx: int {\n")
  856     (write _test-input-stream "  result <- copy a\n")
  857     (write _test-input-stream "  result <- add b\n")
  858     (write _test-input-stream "}\n")
  859     # convert
  860     (convert-mu _test-input-buffered-file _test-output-buffered-file)
  861     (flush _test-output-buffered-file)
  862 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
  868     # check output
  869     (check-next-stream-line-equal _test-output-stream "main:"                   "F - test-convert-function-call-with-literal-arg/0")
  870     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-call-with-literal-arg/1")
  871     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-call-with-literal-arg/2")
  872     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-call-with-literal-arg/3")
  873     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-call-with-literal-arg/4")
  874     (check-next-stream-line-equal _test-output-stream "$main:0x00000001:loop:"  "F - test-convert-function-call-with-literal-arg/5")
  875     (check-next-stream-line-equal _test-output-stream "    (do-add 3 4)"        "F - test-convert-function-call-with-literal-arg/6")
  876     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-call-with-literal-arg/7")
  877     (check-next-stream-line-equal _test-output-stream "$main:0x00000001:break:" "F - test-convert-function-call-with-literal-arg/8")
  878     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-call-with-literal-arg/9")
  879     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-call-with-literal-arg/10")
  880     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-call-with-literal-arg/11")
  881     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-call-with-literal-arg/12")
  882     (check-next-stream-line-equal _test-output-stream "do-add:"                 "F - test-convert-function-call-with-literal-arg/13")
  883     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-call-with-literal-arg/14")
  884     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-call-with-literal-arg/15")
  885     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-call-with-literal-arg/16")
  886     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-call-with-literal-arg/17")
  887     (check-next-stream-line-equal _test-output-stream "$do-add:0x00000002:loop:"  "F - test-convert-function-call-with-literal-arg/18")
  888     (check-next-stream-line-equal _test-output-stream "    8b/-> *(ebp+0x00000008) 0x00000003/r32"  "F - test-convert-function-call-with-literal-arg/19")
  889     (check-next-stream-line-equal _test-output-stream "    03/add *(ebp+0x0000000c) 0x00000003/r32"  "F - test-convert-function-call-with-literal-arg/20")
  890     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-call-with-literal-arg/21")
  891     (check-next-stream-line-equal _test-output-stream "$do-add:0x00000002:break:"  "F - test-convert-function-call-with-literal-arg/22")
  892     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-call-with-literal-arg/23")
  893     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-call-with-literal-arg/24")
  894     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-call-with-literal-arg/25")
  895     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-call-with-literal-arg/26")
  896     # . epilogue
  897     89/<- %esp 5/r32/ebp
  898     5d/pop-to-ebp
  899     c3/return
  900 
  901 test-convert-function-with-local-var-in-mem:
  902     # . prologue
  903     55/push-ebp
  904     89/<- %ebp 4/r32/esp
  905     # setup
  906     (clear-stream _test-input-stream)
  907     (clear-stream $_test-input-buffered-file->buffer)
  908     (clear-stream _test-output-stream)
  909     (clear-stream $_test-output-buffered-file->buffer)
  910     #
  911     (write _test-input-stream "fn foo {\n")
  912     (write _test-input-stream "  var x: int\n")
  913     (write _test-input-stream "  increment x\n")
  914     (write _test-input-stream "}\n")
  915     # convert
  916     (convert-mu _test-input-buffered-file _test-output-buffered-file)
  917     (flush _test-output-buffered-file)
  918 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
  924     # check output
  925     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-local-var-in-mem/0")
  926     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-local-var-in-mem/1")
  927     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-local-var-in-mem/2")
  928     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-local-var-in-mem/3")
  929     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-local-var-in-mem/4")
  930     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-local-var-in-mem/5")
  931     (check-next-stream-line-equal _test-output-stream "    68/push 0/imm32"     "F - test-convert-function-with-local-var-in-mem/6")
  932     (check-next-stream-line-equal _test-output-stream "    ff 0/subop/increment *(ebp+0xfffffffc)"  "F - test-convert-function-with-local-var-in-mem/7")
  933     (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")
  934     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-local-var-in-mem/9")
  935     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-local-var-in-mem/10")
  936     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-local-var-in-mem/11")
  937     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-local-var-in-mem/12")
  938     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-local-var-in-mem/13")
  939     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-local-var-in-mem/14")
  940     # . epilogue
  941     89/<- %esp 5/r32/ebp
  942     5d/pop-to-ebp
  943     c3/return
  944 
  945 test-convert-function-with-local-var-with-compound-type-in-mem:
  946     # . prologue
  947     55/push-ebp
  948     89/<- %ebp 4/r32/esp
  949     # setup
  950     (clear-stream _test-input-stream)
  951     (clear-stream $_test-input-buffered-file->buffer)
  952     (clear-stream _test-output-stream)
  953     (clear-stream $_test-output-buffered-file->buffer)
  954     #
  955     (write _test-input-stream "fn foo {\n")
  956     (write _test-input-stream "  var x: (addr int)\n")
  957     (write _test-input-stream "  copy-to x, 0\n")
  958     (write _test-input-stream "}\n")
  959     # convert
  960     (convert-mu _test-input-buffered-file _test-output-buffered-file)
  961     (flush _test-output-buffered-file)
  962 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
  968     # check output
  969     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-local-var-with-compound-type-in-mem/0")
  970     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-local-var-with-compound-type-in-mem/1")
  971     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-local-var-with-compound-type-in-mem/2")
  972     (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")
  973     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-local-var-with-compound-type-in-mem/4")
  974     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-local-var-with-compound-type-in-mem/5")
  975     (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")
  976     (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")
  977     (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")
  978     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-local-var-with-compound-type-in-mem/9")
  979     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-local-var-with-compound-type-in-mem/10")
  980     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-local-var-with-compound-type-in-mem/11")
  981     (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")
  982     (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")
  983     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-local-var-with-compound-type-in-mem/14")
  984     # . epilogue
  985     89/<- %esp 5/r32/ebp
  986     5d/pop-to-ebp
  987     c3/return
  988 
  989 test-convert-function-with-local-var-in-reg:
  990     # . prologue
  991     55/push-ebp
  992     89/<- %ebp 4/r32/esp
  993     # setup
  994     (clear-stream _test-input-stream)
  995     (clear-stream $_test-input-buffered-file->buffer)
  996     (clear-stream _test-output-stream)
  997     (clear-stream $_test-output-buffered-file->buffer)
  998     #
  999     (write _test-input-stream "fn foo {\n")
 1000     (write _test-input-stream "  var x/ecx: int <- copy 3\n")
 1001     (write _test-input-stream "  x <- increment\n")
 1002     (write _test-input-stream "}\n")
 1003     # convert
 1004     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 1005     (flush _test-output-buffered-file)
 1006 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
 1012     # check output
 1013     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-local-var-in-reg/0")
 1014     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-local-var-in-reg/1")
 1015     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-local-var-in-reg/2")
 1016     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-local-var-in-reg/3")
 1017     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-local-var-in-reg/4")
 1018     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-local-var-in-reg/5")
 1019     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-convert-function-with-local-var-in-reg/6")
 1020     (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 3/imm32"  "F - test-convert-function-with-local-var-in-reg/7")
 1021     (check-next-stream-line-equal _test-output-stream "    41/increment-ecx"    "F - test-convert-function-with-local-var-in-reg/8")
 1022     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx" "F - test-convert-function-with-local-var-in-reg/9")
 1023     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-local-var-in-reg/10")
 1024     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-local-var-in-reg/11")
 1025     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-local-var-in-reg/12")
 1026     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-local-var-in-reg/13")
 1027     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-local-var-in-reg/14")
 1028     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-local-var-in-reg/15")
 1029     # . epilogue
 1030     89/<- %esp 5/r32/ebp
 1031     5d/pop-to-ebp
 1032     c3/return
 1033 
 1034 test-convert-function-with-second-local-var-in-same-reg:
 1035     # . prologue
 1036     55/push-ebp
 1037     89/<- %ebp 4/r32/esp
 1038     # setup
 1039     (clear-stream _test-input-stream)
 1040     (clear-stream $_test-input-buffered-file->buffer)
 1041     (clear-stream _test-output-stream)
 1042     (clear-stream $_test-output-buffered-file->buffer)
 1043     #
 1044     (write _test-input-stream "fn foo {\n")
 1045     (write _test-input-stream "  var x/ecx: int <- copy 3\n")
 1046     (write _test-input-stream "  var y/ecx: int <- copy 4\n")
 1047     (write _test-input-stream "  y <- increment\n")
 1048     (write _test-input-stream "}\n")
 1049     # convert
 1050     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 1051     (flush _test-output-buffered-file)
 1052 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
 1058     # check output
 1059     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-second-local-var-in-same-reg/0")
 1060     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-second-local-var-in-same-reg/1")
 1061     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-second-local-var-in-same-reg/2")
 1062     (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")
 1063     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-second-local-var-in-same-reg/4")
 1064     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-second-local-var-in-same-reg/5")
 1065     (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")
 1066     (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")
 1067     (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")
 1068     (check-next-stream-line-equal _test-output-stream "    41/increment-ecx"    "F - test-convert-function-with-second-local-var-in-same-reg/9")
 1069     (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")
 1070     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-second-local-var-in-same-reg/11")
 1071     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-second-local-var-in-same-reg/12")
 1072     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-second-local-var-in-same-reg/13")
 1073     (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")
 1074     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-second-local-var-in-same-reg/15")
 1075     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-second-local-var-in-same-reg/16")
 1076     # . epilogue
 1077     89/<- %esp 5/r32/ebp
 1078     5d/pop-to-ebp
 1079     c3/return
 1080 
 1081 test-convert-function-with-local-var-dereferenced:
 1082     # . prologue
 1083     55/push-ebp
 1084     89/<- %ebp 4/r32/esp
 1085     # setup
 1086     (clear-stream _test-input-stream)
 1087     (clear-stream $_test-input-buffered-file->buffer)
 1088     (clear-stream _test-output-stream)
 1089     (clear-stream $_test-output-buffered-file->buffer)
 1090     #
 1091     (write _test-input-stream "fn foo {\n")
 1092     (write _test-input-stream "  var x/ecx: (addr int) <- copy 0\n")
 1093     (write _test-input-stream "  increment *x\n")
 1094     (write _test-input-stream "}\n")
 1095     # convert
 1096     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 1097     (flush _test-output-buffered-file)
 1098 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
 1104     # check output
 1105     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-local-var-dereferenced/0")
 1106     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-local-var-dereferenced/1")
 1107     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-local-var-dereferenced/2")
 1108     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-local-var-dereferenced/3")
 1109     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-local-var-dereferenced/4")
 1110     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-local-var-dereferenced/5")
 1111     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-convert-function-with-local-var-dereferenced/6")
 1112     (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 0/imm32"  "F - test-convert-function-with-local-var-dereferenced/7")
 1113     (check-next-stream-line-equal _test-output-stream "    ff 0/subop/increment *ecx"  "F - test-convert-function-with-local-var-dereferenced/8")
 1114     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx" "F - test-convert-function-with-local-var-dereferenced/9")
 1115     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-local-var-dereferenced/10")
 1116     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-local-var-dereferenced/11")
 1117     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-local-var-dereferenced/12")
 1118     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-local-var-dereferenced/13")
 1119     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-local-var-dereferenced/14")
 1120     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-local-var-dereferenced/15")
 1121     # . epilogue
 1122     89/<- %esp 5/r32/ebp
 1123     5d/pop-to-ebp
 1124     c3/return
 1125 
 1126 test-convert-compare-register-with-literal:
 1127     # . prologue
 1128     55/push-ebp
 1129     89/<- %ebp 4/r32/esp
 1130     # setup
 1131     (clear-stream _test-input-stream)
 1132     (clear-stream $_test-input-buffered-file->buffer)
 1133     (clear-stream _test-output-stream)
 1134     (clear-stream $_test-output-buffered-file->buffer)
 1135     #
 1136     (write _test-input-stream "fn foo {\n")
 1137     (write _test-input-stream "  var x/ecx: int <- copy 0\n")
 1138     (write _test-input-stream "  compare x, 0\n")
 1139     (write _test-input-stream "}\n")
 1140     # convert
 1141     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 1142     (flush _test-output-buffered-file)
 1143 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
 1149     # check output
 1150     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-compare-register-with-literal/0")
 1151     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-compare-register-with-literal/1")
 1152     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-compare-register-with-literal/2")
 1153     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-compare-register-with-literal/3")
 1154     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-compare-register-with-literal/4")
 1155     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-compare-register-with-literal/5")
 1156     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-convert-compare-register-with-literal/6")
 1157     (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 0/imm32"  "F - test-convert-compare-register-with-literal/7")
 1158     (check-next-stream-line-equal _test-output-stream "    81 7/subop/compare %ecx 0/imm32"  "F - test-convert-compare-register-with-literal/8")
 1159     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx" "F - test-convert-compare-register-with-literal/9")
 1160     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-compare-register-with-literal/10")
 1161     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-compare-register-with-literal/11")
 1162     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-compare-register-with-literal/12")
 1163     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-compare-register-with-literal/13")
 1164     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-compare-register-with-literal/14")
 1165     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-compare-register-with-literal/15")
 1166     # . epilogue
 1167     89/<- %esp 5/r32/ebp
 1168     5d/pop-to-ebp
 1169     c3/return
 1170 
 1171 test-convert-function-with-local-var-in-block:
 1172     # . prologue
 1173     55/push-ebp
 1174     89/<- %ebp 4/r32/esp
 1175     # setup
 1176     (clear-stream _test-input-stream)
 1177     (clear-stream $_test-input-buffered-file->buffer)
 1178     (clear-stream _test-output-stream)
 1179     (clear-stream $_test-output-buffered-file->buffer)
 1180     #
 1181     (write _test-input-stream "fn foo {\n")
 1182     (write _test-input-stream "  {\n")
 1183     (write _test-input-stream "    var x: int\n")
 1184     (write _test-input-stream "    increment x\n")
 1185     (write _test-input-stream "  }\n")
 1186     (write _test-input-stream "}\n")
 1187     # convert
 1188     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 1189     (flush _test-output-buffered-file)
 1190 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
 1196     # check output
 1197     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-local-var-in-block/0")
 1198     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-local-var-in-block/1")
 1199     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-local-var-in-block/2")
 1200     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-local-var-in-block/3")
 1201     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-local-var-in-block/4")
 1202     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-local-var-in-block/5")
 1203     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-local-var-in-block/6")
 1204     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-local-var-in-block/7")
 1205     (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-local-var-in-block/8")
 1206     (check-next-stream-line-equal _test-output-stream "      ff 0/subop/increment *(ebp+0xfffffffc)"  "F - test-convert-function-with-local-var-in-block/9")
 1207     (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")
 1208     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-local-var-in-block/11")
 1209     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-local-var-in-block/12")
 1210     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-local-var-in-block/13")
 1211     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-local-var-in-block/14")
 1212     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-local-var-in-block/15")
 1213     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-local-var-in-block/16")
 1214     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-local-var-in-block/17")
 1215     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-local-var-in-block/18")
 1216     # . epilogue
 1217     89/<- %esp 5/r32/ebp
 1218     5d/pop-to-ebp
 1219     c3/return
 1220 
 1221 test-convert-function-with-local-var-in-named-block:
 1222     # . prologue
 1223     55/push-ebp
 1224     89/<- %ebp 4/r32/esp
 1225     # setup
 1226     (clear-stream _test-input-stream)
 1227     (clear-stream $_test-input-buffered-file->buffer)
 1228     (clear-stream _test-output-stream)
 1229     (clear-stream $_test-output-buffered-file->buffer)
 1230     #
 1231     (write _test-input-stream "fn foo {\n")
 1232     (write _test-input-stream "  $bar: {\n")
 1233     (write _test-input-stream "    var x: int\n")
 1234     (write _test-input-stream "    increment x\n")
 1235     (write _test-input-stream "  }\n")
 1236     (write _test-input-stream "}\n")
 1237     # convert
 1238     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 1239     (flush _test-output-buffered-file)
 1240 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
 1246     # check output
 1247     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-local-var-in-named-block/0")
 1248     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-local-var-in-named-block/1")
 1249     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-local-var-in-named-block/2")
 1250     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-local-var-in-named-block/3")
 1251     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-local-var-in-named-block/4")
 1252     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-local-var-in-named-block/5")
 1253     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-local-var-in-named-block/6")
 1254     (check-next-stream-line-equal _test-output-stream "$bar:loop:"              "F - test-convert-function-with-local-var-in-named-block/7")
 1255     (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-local-var-in-named-block/8")
 1256     (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")
 1257     (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")
 1258     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-local-var-in-named-block/11")
 1259     (check-next-stream-line-equal _test-output-stream "$bar:break:"             "F - test-convert-function-with-local-var-in-named-block/12")
 1260     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-local-var-in-named-block/13")
 1261     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-local-var-in-named-block/14")
 1262     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-local-var-in-named-block/15")
 1263     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-local-var-in-named-block/16")
 1264     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-local-var-in-named-block/17")
 1265     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-local-var-in-named-block/18")
 1266     # . epilogue
 1267     89/<- %esp 5/r32/ebp
 1268     5d/pop-to-ebp
 1269     c3/return
 1270 
 1271 test-always-shadow-outermost-reg-vars-in-function:
 1272     # . prologue
 1273     55/push-ebp
 1274     89/<- %ebp 4/r32/esp
 1275     # setup
 1276     (clear-stream _test-input-stream)
 1277     (clear-stream $_test-input-buffered-file->buffer)
 1278     (clear-stream _test-output-stream)
 1279     (clear-stream $_test-output-buffered-file->buffer)
 1280     #
 1281     (write _test-input-stream "fn foo {\n")
 1282     (write _test-input-stream "  var x/ecx: int <- copy 3\n")
 1283     (write _test-input-stream "}\n")
 1284     # convert
 1285     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 1286     (flush _test-output-buffered-file)
 1287 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
 1293     # check output
 1294     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-always-shadow-outermost-reg-vars-in-function/0")
 1295     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-always-shadow-outermost-reg-vars-in-function/1")
 1296     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-always-shadow-outermost-reg-vars-in-function/2")
 1297     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-always-shadow-outermost-reg-vars-in-function/3")
 1298     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-always-shadow-outermost-reg-vars-in-function/4")
 1299     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-always-shadow-outermost-reg-vars-in-function/5")
 1300     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-convert-compare-register-with-literal/6")
 1301     (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 3/imm32"  "F - test-always-shadow-outermost-reg-vars-in-function/8")
 1302     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx" "F - test-convert-compare-register-with-literal/9")
 1303     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-always-shadow-outermost-reg-vars-in-function/12")
 1304     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-always-shadow-outermost-reg-vars-in-function/13")
 1305     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-always-shadow-outermost-reg-vars-in-function/14")
 1306     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-always-shadow-outermost-reg-vars-in-function/15")
 1307     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-always-shadow-outermost-reg-vars-in-function/16")
 1308     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-always-shadow-outermost-reg-vars-in-function/17")
 1309     # . epilogue
 1310     89/<- %esp 5/r32/ebp
 1311     5d/pop-to-ebp
 1312     c3/return
 1313 
 1314 _pending-test-clobber-dead-local:
 1315     # . prologue
 1316     55/push-ebp
 1317     89/<- %ebp 4/r32/esp
 1318     # setup
 1319     (clear-stream _test-input-stream)
 1320     (clear-stream $_test-input-buffered-file->buffer)
 1321     (clear-stream _test-output-stream)
 1322     (clear-stream $_test-output-buffered-file->buffer)
 1323     #
 1324     (write _test-input-stream "fn foo {\n")
 1325     (write _test-input-stream "  var x/ecx: int <- copy 3\n")
 1326     (write _test-input-stream "  {\n")
 1327     (write _test-input-stream "    var y/ecx: int <- copy 4\n")
 1328     (write _test-input-stream "  }\n")
 1329     (write _test-input-stream "}\n")
 1330     # convert
 1331     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 1332     (flush _test-output-buffered-file)
 1333 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
 1339     # check output
 1340     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-clobber-dead-local/0")
 1341     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-clobber-dead-local/1")
 1342     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-clobber-dead-local/2")
 1343     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-clobber-dead-local/3")
 1344     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-clobber-dead-local/4")
 1345     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-clobber-dead-local/5")
 1346     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-clobber-dead-local/6")
 1347     (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 3/imm32"  "F - test-clobber-dead-local/7")
 1348     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-clobber-dead-local/8")
 1349     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-clobber-dead-local/9")
 1350     (check-next-stream-line-equal _test-output-stream "      b9/copy-to-ecx 4/imm32"  "F - test-clobber-dead-local/10")  # no push/pop here
 1351     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-clobber-dead-local/11")
 1352     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-clobber-dead-local/12")
 1353     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx" "F - test-clobber-dead-local/13")
 1354     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-clobber-dead-local/14")
 1355     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-clobber-dead-local/15")
 1356     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-clobber-dead-local/16")
 1357     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-clobber-dead-local/17")
 1358     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-clobber-dead-local/18")
 1359     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-clobber-dead-local/19")
 1360     # . epilogue
 1361     89/<- %esp 5/r32/ebp
 1362     5d/pop-to-ebp
 1363     c3/return
 1364 
 1365 test-shadow-live-local:
 1366     # . prologue
 1367     55/push-ebp
 1368     89/<- %ebp 4/r32/esp
 1369     # setup
 1370     (clear-stream _test-input-stream)
 1371     (clear-stream $_test-input-buffered-file->buffer)
 1372     (clear-stream _test-output-stream)
 1373     (clear-stream $_test-output-buffered-file->buffer)
 1374     #
 1375     (write _test-input-stream "fn foo {\n")
 1376     (write _test-input-stream "  var x/ecx: int <- copy 3\n")
 1377     (write _test-input-stream "  {\n")
 1378     (write _test-input-stream "    var y/ecx: int <- copy 4\n")
 1379     (write _test-input-stream "  }\n")
 1380     (write _test-input-stream "  x <- increment\n")
 1381     (write _test-input-stream "}\n")
 1382     # convert
 1383     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 1384     (flush _test-output-buffered-file)
 1385 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
 1391     # check output
 1392     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-shadow-live-local/0")
 1393     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-shadow-live-local/1")
 1394     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-shadow-live-local/2")
 1395     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-shadow-live-local/3")
 1396     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-shadow-live-local/4")
 1397     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-shadow-live-local/5")
 1398     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-shadow-live-local/6")
 1399     (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 3/imm32"  "F - test-shadow-live-local/7")
 1400     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-shadow-live-local/8")
 1401     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-shadow-live-local/9")
 1402     (check-next-stream-line-equal _test-output-stream "      ff 6/subop/push %ecx"  "F - test-shadow-live-local/10")
 1403     (check-next-stream-line-equal _test-output-stream "      b9/copy-to-ecx 4/imm32"  "F - test-shadow-live-local/11")
 1404     (check-next-stream-line-equal _test-output-stream "      8f 0/subop/pop %ecx" "F - test-shadow-live-local/12")
 1405     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-shadow-live-local/13")
 1406     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-shadow-live-local/14")
 1407     (check-next-stream-line-equal _test-output-stream "    41/increment-ecx"    "F - test-shadow-live-local/15")
 1408     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx" "F - test-shadow-live-local/16")
 1409     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-shadow-live-local/17")
 1410     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-shadow-live-local/18")
 1411     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-shadow-live-local/19")
 1412     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-shadow-live-local/20")
 1413     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-shadow-live-local/21")
 1414     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-shadow-live-local/21")
 1415     # . epilogue
 1416     89/<- %esp 5/r32/ebp
 1417     5d/pop-to-ebp
 1418     c3/return
 1419 
 1420 test-shadow-live-output:
 1421     # . prologue
 1422     55/push-ebp
 1423     89/<- %ebp 4/r32/esp
 1424     # setup
 1425     (clear-stream _test-input-stream)
 1426     (clear-stream $_test-input-buffered-file->buffer)
 1427     (clear-stream _test-output-stream)
 1428     (clear-stream $_test-output-buffered-file->buffer)
 1429     #
 1430     (write _test-input-stream "fn foo -> x/ecx: int {\n")
 1431     (write _test-input-stream "  x <- copy 3\n")
 1432     (write _test-input-stream "  {\n")
 1433     (write _test-input-stream "    var y/ecx: int <- copy 4\n")
 1434     (write _test-input-stream "  }\n")
 1435     (write _test-input-stream "  x <- increment\n")
 1436     (write _test-input-stream "}\n")
 1437     # convert
 1438     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 1439     (flush _test-output-buffered-file)
 1440 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
 1446     # check output
 1447     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-shadow-live-output/0")
 1448     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-shadow-live-output/1")
 1449     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-shadow-live-output/2")
 1450     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-shadow-live-output/3")
 1451     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-shadow-live-output/4")
 1452     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-shadow-live-output/5")
 1453     (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 3/imm32"  "F - test-shadow-live-output/7")  # no push because it's an output reg
 1454     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-shadow-live-output/8")
 1455     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-shadow-live-output/9")
 1456     (check-next-stream-line-equal _test-output-stream "      ff 6/subop/push %ecx"  "F - test-shadow-live-output/10")
 1457     (check-next-stream-line-equal _test-output-stream "      b9/copy-to-ecx 4/imm32"  "F - test-shadow-live-output/11")
 1458     (check-next-stream-line-equal _test-output-stream "      8f 0/subop/pop %ecx" "F - test-shadow-live-output/12")
 1459     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-shadow-live-output/13")
 1460     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-shadow-live-output/14")
 1461     (check-next-stream-line-equal _test-output-stream "    41/increment-ecx"    "F - test-shadow-live-output/15")
 1462     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-shadow-live-output/17")
 1463     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-shadow-live-output/18")
 1464     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-shadow-live-output/19")
 1465     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-shadow-live-output/20")
 1466     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-shadow-live-output/21")
 1467     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-shadow-live-output/21")
 1468     # . epilogue
 1469     89/<- %esp 5/r32/ebp
 1470     5d/pop-to-ebp
 1471     c3/return
 1472 
 1473 _pending-test-local-clobbered-by-output:
 1474     # also doesn't spill
 1475     # . prologue
 1476     55/push-ebp
 1477     89/<- %ebp 4/r32/esp
 1478     # setup
 1479     (clear-stream _test-input-stream)
 1480     (clear-stream $_test-input-buffered-file->buffer)
 1481     (clear-stream _test-output-stream)
 1482     (clear-stream $_test-output-buffered-file->buffer)
 1483     #
 1484     (write _test-input-stream "fn foo -> x/ecx: int {\n")
 1485     (write _test-input-stream "  var y/ecx: int <- copy 4\n")
 1486     (write _test-input-stream "  x <- copy y\n")
 1487     (write _test-input-stream "}\n")
 1488     # convert
 1489     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 1490     (flush _test-output-buffered-file)
 1491 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
 1497     # check output
 1498     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-local-clobbered-by-output/0")
 1499     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-local-clobbered-by-output/1")
 1500     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-local-clobbered-by-output/2")
 1501     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-local-clobbered-by-output/3")
 1502     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-local-clobbered-by-output/4")
 1503     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-local-clobbered-by-output/5")
 1504     (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 4/imm32"  "F - test-local-clobbered-by-output/6")
 1505     (check-next-stream-line-equal _test-output-stream "    89/<- %ecx 0x00000001/r32"  "F - test-local-clobbered-by-output/7")
 1506     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-local-clobbered-by-output/8")
 1507     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-local-clobbered-by-output/9")
 1508     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-local-clobbered-by-output/10")
 1509     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-local-clobbered-by-output/11")
 1510     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-local-clobbered-by-output/12")
 1511     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-local-clobbered-by-output/13")
 1512     # . epilogue
 1513     89/<- %esp 5/r32/ebp
 1514     5d/pop-to-ebp
 1515     c3/return
 1516 
 1517 test-convert-function-with-branches-in-block:
 1518     # . prologue
 1519     55/push-ebp
 1520     89/<- %ebp 4/r32/esp
 1521     # setup
 1522     (clear-stream _test-input-stream)
 1523     (clear-stream $_test-input-buffered-file->buffer)
 1524     (clear-stream _test-output-stream)
 1525     (clear-stream $_test-output-buffered-file->buffer)
 1526     #
 1527     (write _test-input-stream "fn foo x: int {\n")
 1528     (write _test-input-stream "  {\n")
 1529     (write _test-input-stream "    break-if->=\n")
 1530     (write _test-input-stream "    loop-if-addr<\n")
 1531     (write _test-input-stream "    increment x\n")
 1532     (write _test-input-stream "    loop\n")
 1533     (write _test-input-stream "  }\n")
 1534     (write _test-input-stream "}\n")
 1535     # convert
 1536     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 1537     (flush _test-output-buffered-file)
 1538 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
 1544     # check output
 1545     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-branches-in-block/0")
 1546     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-branches-in-block/1")
 1547     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-branches-in-block/2")
 1548     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-branches-in-block/3")
 1549     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-branches-in-block/4")
 1550     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-branches-in-block/5")
 1551     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-branches-in-block/6")
 1552     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-branches-in-block/7")
 1553     (check-next-stream-line-equal _test-output-stream "      0f 8d/jump-if->= break/disp32"  "F - test-convert-function-with-branches-in-block/8")
 1554     (check-next-stream-line-equal _test-output-stream "      0f 82/jump-if-addr< loop/disp32"  "F - test-convert-function-with-branches-in-block/9")
 1555     (check-next-stream-line-equal _test-output-stream "      ff 0/subop/increment *(ebp+0x00000008)"  "F - test-convert-function-with-branches-in-block/10")
 1556     (check-next-stream-line-equal _test-output-stream "      e9/jump loop/disp32"  "F - test-convert-function-with-branches-in-block/11")
 1557     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-branches-in-block/12")
 1558     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-branches-in-block/13")
 1559     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-branches-in-block/14")
 1560     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-branches-in-block/15")
 1561     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-branches-in-block/16")
 1562     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-branches-in-block/17")
 1563     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-branches-in-block/18")
 1564     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-branches-in-block/19")
 1565     # . epilogue
 1566     89/<- %esp 5/r32/ebp
 1567     5d/pop-to-ebp
 1568     c3/return
 1569 
 1570 test-convert-function-with-branches-in-named-block:
 1571     # . prologue
 1572     55/push-ebp
 1573     89/<- %ebp 4/r32/esp
 1574     # setup
 1575     (clear-stream _test-input-stream)
 1576     (clear-stream $_test-input-buffered-file->buffer)
 1577     (clear-stream _test-output-stream)
 1578     (clear-stream $_test-output-buffered-file->buffer)
 1579     #
 1580     (write _test-input-stream "fn foo x: int {\n")
 1581     (write _test-input-stream "  $bar: {\n")
 1582     (write _test-input-stream "    break-if->= $bar\n")
 1583     (write _test-input-stream "    loop-if-addr< $bar\n")
 1584     (write _test-input-stream "    increment x\n")
 1585     (write _test-input-stream "    loop\n")
 1586     (write _test-input-stream "  }\n")
 1587     (write _test-input-stream "}\n")
 1588     # convert
 1589     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 1590     (flush _test-output-buffered-file)
 1591 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
 1597     # check output
 1598     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-branches-in-named-block/0")
 1599     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-branches-in-named-block/1")
 1600     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-branches-in-named-block/2")
 1601     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-branches-in-named-block/3")
 1602     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-branches-in-named-block/4")
 1603     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-branches-in-named-block/5")
 1604     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-branches-in-named-block/6")
 1605     (check-next-stream-line-equal _test-output-stream "$bar:loop:"              "F - test-convert-function-with-branches-in-named-block/7")
 1606     (check-next-stream-line-equal _test-output-stream "      0f 8d/jump-if->= $bar:break/disp32"  "F - test-convert-function-with-branches-in-named-block/8")
 1607     (check-next-stream-line-equal _test-output-stream "      0f 82/jump-if-addr< $bar:loop/disp32"  "F - test-convert-function-with-branches-in-named-block/9")
 1608     (check-next-stream-line-equal _test-output-stream "      ff 0/subop/increment *(ebp+0x00000008)"  "F - test-convert-function-with-branches-in-named-block/10")
 1609     (check-next-stream-line-equal _test-output-stream "      e9/jump loop/disp32"   "F - test-convert-function-with-branches-in-named-block/11")
 1610     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-branches-in-named-block/12")
 1611     (check-next-stream-line-equal _test-output-stream "$bar:break:"             "F - test-convert-function-with-branches-in-named-block/13")
 1612     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-branches-in-named-block/14")
 1613     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-branches-in-named-block/15")
 1614     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-branches-in-named-block/16")
 1615     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-branches-in-named-block/17")
 1616     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-branches-in-named-block/18")
 1617     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-branches-in-named-block/19")
 1618     # . epilogue
 1619     89/<- %esp 5/r32/ebp
 1620     5d/pop-to-ebp
 1621     c3/return
 1622 
 1623 test-convert-function-with-var-in-nested-block:
 1624     # . prologue
 1625     55/push-ebp
 1626     89/<- %ebp 4/r32/esp
 1627     # setup
 1628     (clear-stream _test-input-stream)
 1629     (clear-stream $_test-input-buffered-file->buffer)
 1630     (clear-stream _test-output-stream)
 1631     (clear-stream $_test-output-buffered-file->buffer)
 1632     #
 1633     (write _test-input-stream "fn foo x: int {\n")
 1634     (write _test-input-stream "  {\n")
 1635     (write _test-input-stream "    {\n")
 1636     (write _test-input-stream "      var x: int\n")
 1637     (write _test-input-stream "      increment x\n")
 1638     (write _test-input-stream "    }\n")
 1639     (write _test-input-stream "  }\n")
 1640     (write _test-input-stream "}\n")
 1641     # convert
 1642     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 1643     (flush _test-output-buffered-file)
 1644 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
 1650     # check output
 1651     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-var-in-nested-block/0")
 1652     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-var-in-nested-block/1")
 1653     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-var-in-nested-block/2")
 1654     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-var-in-nested-block/3")
 1655     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-var-in-nested-block/4")
 1656     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-var-in-nested-block/5")
 1657     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-var-in-nested-block/6")
 1658     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-var-in-nested-block/7")
 1659     (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-var-in-nested-block/8")
 1660     (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:loop:"   "F - test-convert-function-with-var-in-nested-block/9")
 1661     (check-next-stream-line-equal _test-output-stream "        68/push 0/imm32" "F - test-convert-function-with-var-in-nested-block/10")
 1662     (check-next-stream-line-equal _test-output-stream "        ff 0/subop/increment *(ebp+0xfffffffc)"  "F - test-convert-function-with-var-in-nested-block/11")
 1663     (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")
 1664     (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-var-in-nested-block/13")
 1665     (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:break:"  "F - test-convert-function-with-var-in-nested-block/14")
 1666     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-var-in-nested-block/15")
 1667     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-var-in-nested-block/16")
 1668     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-var-in-nested-block/17")
 1669     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-var-in-nested-block/18")
 1670     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-var-in-nested-block/19")
 1671     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-var-in-nested-block/20")
 1672     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-var-in-nested-block/21")
 1673     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-var-in-nested-block/22")
 1674     # . epilogue
 1675     89/<- %esp 5/r32/ebp
 1676     5d/pop-to-ebp
 1677     c3/return
 1678 
 1679 test-convert-function-with-multiple-vars-in-nested-blocks:
 1680     # . prologue
 1681     55/push-ebp
 1682     89/<- %ebp 4/r32/esp
 1683     # setup
 1684     (clear-stream _test-input-stream)
 1685     (clear-stream $_test-input-buffered-file->buffer)
 1686     (clear-stream _test-output-stream)
 1687     (clear-stream $_test-output-buffered-file->buffer)
 1688     #
 1689     (write _test-input-stream "fn foo x: int {\n")
 1690     (write _test-input-stream "  {\n")
 1691     (write _test-input-stream "    var x/eax: int <- copy 0\n")
 1692     (write _test-input-stream "    {\n")
 1693     (write _test-input-stream "      var y: int\n")
 1694     (write _test-input-stream "      x <- add y\n")
 1695     (write _test-input-stream "    }\n")
 1696     (write _test-input-stream "  }\n")
 1697     (write _test-input-stream "}\n")
 1698     # convert
 1699     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 1700     (flush _test-output-buffered-file)
 1701 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
 1707     # check output
 1708     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-multiple-vars-in-nested-blocks/0")
 1709     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-multiple-vars-in-nested-blocks/1")
 1710     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-multiple-vars-in-nested-blocks/2")
 1711     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-multiple-vars-in-nested-blocks/3")
 1712     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-multiple-vars-in-nested-blocks/4")
 1713     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-multiple-vars-in-nested-blocks/5")
 1714     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-multiple-vars-in-nested-blocks/6")
 1715     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-multiple-vars-in-nested-blocks/7")
 1716     (check-next-stream-line-equal _test-output-stream "      ff 6/subop/push %eax"  "F - test-convert-function-with-multiple-vars-in-nested-blocks/8")
 1717     (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")
 1718     (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-multiple-vars-in-nested-blocks/10")
 1719     (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:loop:"   "F - test-convert-function-with-multiple-vars-in-nested-blocks/11")
 1720     (check-next-stream-line-equal _test-output-stream "        68/push 0/imm32" "F - test-convert-function-with-multiple-vars-in-nested-blocks/12")
 1721     (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")
 1722     (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")
 1723     (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-multiple-vars-in-nested-blocks/15")
 1724     (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:break:"  "F - test-convert-function-with-multiple-vars-in-nested-blocks/16")
 1725     (check-next-stream-line-equal _test-output-stream "      8f 0/subop/pop %eax"   "F - test-convert-function-with-multiple-vars-in-nested-blocks/17")
 1726     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-multiple-vars-in-nested-blocks/18")
 1727     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-multiple-vars-in-nested-blocks/19")
 1728     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-multiple-vars-in-nested-blocks/20")
 1729     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-multiple-vars-in-nested-blocks/21")
 1730     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-multiple-vars-in-nested-blocks/22")
 1731     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-multiple-vars-in-nested-blocks/23")
 1732     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-multiple-vars-in-nested-blocks/24")
 1733     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-multiple-vars-in-nested-blocks/25")
 1734     # . epilogue
 1735     89/<- %esp 5/r32/ebp
 1736     5d/pop-to-ebp
 1737     c3/return
 1738 
 1739 test-convert-function-with-branches-and-local-vars:
 1740     # A conditional 'break' after a 'var' in a block is converted into a
 1741     # nested block that performs all necessary cleanup before jumping. This
 1742     # results in some ugly code duplication.
 1743     # . prologue
 1744     55/push-ebp
 1745     89/<- %ebp 4/r32/esp
 1746     # setup
 1747     (clear-stream _test-input-stream)
 1748     (clear-stream $_test-input-buffered-file->buffer)
 1749     (clear-stream _test-output-stream)
 1750     (clear-stream $_test-output-buffered-file->buffer)
 1751     #
 1752     (write _test-input-stream "fn foo {\n")
 1753     (write _test-input-stream "  {\n")
 1754     (write _test-input-stream "    var x: int\n")
 1755     (write _test-input-stream "    break-if->=\n")
 1756     (write _test-input-stream "    increment x\n")
 1757     (write _test-input-stream "  }\n")
 1758     (write _test-input-stream "}\n")
 1759     # convert
 1760     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 1761     (flush _test-output-buffered-file)
 1762 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
 1768     # check output
 1769     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-branches-and-local-vars/0")
 1770     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-branches-and-local-vars/1")
 1771     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-branches-and-local-vars/2")
 1772     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-branches-and-local-vars/3")
 1773     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-branches-and-local-vars/4")
 1774     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-branches-and-local-vars/5")
 1775     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-branches-and-local-vars/6")
 1776     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-branches-and-local-vars/7")
 1777     (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-branches-and-local-vars/8")
 1778     (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-branches-and-local-vars/9")
 1779     (check-next-stream-line-equal _test-output-stream "        0f 8c/jump-if-< break/disp32"  "F - test-convert-function-with-branches-and-local-vars/10")
 1780     (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")
 1781     (check-next-stream-line-equal _test-output-stream "        e9/jump $foo:0x00000002:break/disp32"  "F - test-convert-function-with-branches-and-local-vars/12")
 1782     (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-branches-and-local-vars/13")
 1783     (check-next-stream-line-equal _test-output-stream "      ff 0/subop/increment *(ebp+0xfffffffc)"  "F - test-convert-function-with-branches-and-local-vars/14")
 1784     (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")
 1785     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-branches-and-local-vars/16")
 1786     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-branches-and-local-vars/17")
 1787     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-branches-and-local-vars/18")
 1788     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-branches-and-local-vars/19")
 1789     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-branches-and-local-vars/20")
 1790     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-branches-and-local-vars/21")
 1791     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-branches-and-local-vars/22")
 1792     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-branches-and-local-vars/23")
 1793     # . epilogue
 1794     89/<- %esp 5/r32/ebp
 1795     5d/pop-to-ebp
 1796     c3/return
 1797 
 1798 test-convert-function-with-conditional-loops-and-local-vars:
 1799     # A conditional 'loop' after a 'var' in a block is converted into a nested
 1800     # block that performs all necessary cleanup before jumping. This results
 1801     # in some ugly code duplication.
 1802     # . prologue
 1803     55/push-ebp
 1804     89/<- %ebp 4/r32/esp
 1805     # setup
 1806     (clear-stream _test-input-stream)
 1807     (clear-stream $_test-input-buffered-file->buffer)
 1808     (clear-stream _test-output-stream)
 1809     (clear-stream $_test-output-buffered-file->buffer)
 1810     #
 1811     (write _test-input-stream "fn foo {\n")
 1812     (write _test-input-stream "  {\n")
 1813     (write _test-input-stream "    var x: int\n")
 1814     (write _test-input-stream "    loop-if->=\n")
 1815     (write _test-input-stream "    increment x\n")
 1816     (write _test-input-stream "  }\n")
 1817     (write _test-input-stream "}\n")
 1818     # convert
 1819     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 1820     (flush _test-output-buffered-file)
 1821 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
 1827     # check output
 1828     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-conditional-loops-and-local-vars/0")
 1829     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-conditional-loops-and-local-vars/1")
 1830     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-conditional-loops-and-local-vars/2")
 1831     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-conditional-loops-and-local-vars/3")
 1832     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-conditional-loops-and-local-vars/4")
 1833     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-conditional-loops-and-local-vars/5")
 1834     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-conditional-loops-and-local-vars/6")
 1835     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-conditional-loops-and-local-vars/7")
 1836     (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-conditional-loops-and-local-vars/8")
 1837     (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-conditional-loops-and-local-vars/9")
 1838     (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")
 1839     (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")
 1840     (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")
 1841     (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-conditional-loops-and-local-vars/13")
 1842     (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")
 1843     (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")
 1844     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-conditional-loops-and-local-vars/16")
 1845     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-conditional-loops-and-local-vars/17")
 1846     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-conditional-loops-and-local-vars/18")
 1847     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-conditional-loops-and-local-vars/19")
 1848     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-conditional-loops-and-local-vars/20")
 1849     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-conditional-loops-and-local-vars/21")
 1850     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-conditional-loops-and-local-vars/22")
 1851     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-conditional-loops-and-local-vars/23")
 1852     # . epilogue
 1853     89/<- %esp 5/r32/ebp
 1854     5d/pop-to-ebp
 1855     c3/return
 1856 
 1857 test-convert-function-with-unconditional-loops-and-local-vars:
 1858     # An unconditional 'loop' after a 'var' in a block is emitted _after_ the
 1859     # regular block cleanup. Any instructions after 'loop' are dead and
 1860     # therefore skipped.
 1861     # . prologue
 1862     55/push-ebp
 1863     89/<- %ebp 4/r32/esp
 1864     # setup
 1865     (clear-stream _test-input-stream)
 1866     (clear-stream $_test-input-buffered-file->buffer)
 1867     (clear-stream _test-output-stream)
 1868     (clear-stream $_test-output-buffered-file->buffer)
 1869     #
 1870     (write _test-input-stream "fn foo {\n")
 1871     (write _test-input-stream "  {\n")
 1872     (write _test-input-stream "    var x: int\n")
 1873     (write _test-input-stream "    loop\n")
 1874     (write _test-input-stream "    increment x\n")
 1875     (write _test-input-stream "  }\n")
 1876     (write _test-input-stream "}\n")
 1877     # convert
 1878     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 1879     (flush _test-output-buffered-file)
 1880 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
 1886     # check output
 1887     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-unconditional-loops-and-local-vars/0")
 1888     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-unconditional-loops-and-local-vars/1")
 1889     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-unconditional-loops-and-local-vars/2")
 1890     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-unconditional-loops-and-local-vars/3")
 1891     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-unconditional-loops-and-local-vars/4")
 1892     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-unconditional-loops-and-local-vars/5")
 1893     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-unconditional-loops-and-local-vars/6")
 1894     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-unconditional-loops-and-local-vars/7")
 1895     (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-unconditional-loops-and-local-vars/8")
 1896     (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")
 1897     (check-next-stream-line-equal _test-output-stream "      e9/jump loop/disp32"  "F - test-convert-function-with-unconditional-loops-and-local-vars/10")
 1898     # not emitted:                                           ff 0/subop/increment *(ebp+0xfffffffc)
 1899     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-unconditional-loops-and-local-vars/11")
 1900     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-unconditional-loops-and-local-vars/12")
 1901     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-unconditional-loops-and-local-vars/13")
 1902     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-unconditional-loops-and-local-vars/14")
 1903     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-unconditional-loops-and-local-vars/15")
 1904     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-unconditional-loops-and-local-vars/16")
 1905     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-unconditional-loops-and-local-vars/17")
 1906     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-unconditional-loops-and-local-vars/18")
 1907     # . epilogue
 1908     89/<- %esp 5/r32/ebp
 1909     5d/pop-to-ebp
 1910     c3/return
 1911 
 1912 test-convert-function-with-branches-and-loops-and-local-vars:
 1913     # . prologue
 1914     55/push-ebp
 1915     89/<- %ebp 4/r32/esp
 1916     # setup
 1917     (clear-stream _test-input-stream)
 1918     (clear-stream $_test-input-buffered-file->buffer)
 1919     (clear-stream _test-output-stream)
 1920     (clear-stream $_test-output-buffered-file->buffer)
 1921     #
 1922     (write _test-input-stream "fn foo {\n")
 1923     (write _test-input-stream "  {\n")
 1924     (write _test-input-stream "    var x: int\n")
 1925     (write _test-input-stream "    break-if->=\n")
 1926     (write _test-input-stream "    increment x\n")
 1927     (write _test-input-stream "    loop\n")
 1928     (write _test-input-stream "  }\n")
 1929     (write _test-input-stream "}\n")
 1930     # convert
 1931     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 1932     (flush _test-output-buffered-file)
 1933 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
 1939     # check output
 1940     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-branches-and-loops-and-local-vars/0")
 1941     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-branches-and-loops-and-local-vars/1")
 1942     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-branches-and-loops-and-local-vars/2")
 1943     (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")
 1944     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-branches-and-loops-and-local-vars/4")
 1945     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-branches-and-loops-and-local-vars/5")
 1946     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-branches-and-loops-and-local-vars/6")
 1947     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-branches-and-loops-and-local-vars/7")
 1948     (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-branches-and-loops-and-local-vars/8")
 1949     (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-branches-and-loops-and-local-vars/9")
 1950     (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")
 1951     (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")
 1952     (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")
 1953     (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-branches-and-loops-and-local-vars/13")
 1954     (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")
 1955     (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")
 1956     (check-next-stream-line-equal _test-output-stream "      e9/jump loop/disp32"  "F - test-convert-function-with-branches-and-loops-and-local-vars/16")
 1957     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-branches-and-loops-and-local-vars/17")
 1958     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-branches-and-loops-and-local-vars/18")
 1959     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-branches-and-loops-and-local-vars/19")
 1960     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-branches-and-loops-and-local-vars/20")
 1961     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-branches-and-loops-and-local-vars/21")
 1962     (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")
 1963     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-branches-and-loops-and-local-vars/23")
 1964     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-branches-and-loops-and-local-vars/24")
 1965     # . epilogue
 1966     89/<- %esp 5/r32/ebp
 1967     5d/pop-to-ebp
 1968     c3/return
 1969 
 1970 test-convert-function-with-nonlocal-branches-and-loops-and-local-vars:
 1971     # . prologue
 1972     55/push-ebp
 1973     89/<- %ebp 4/r32/esp
 1974     # setup
 1975     (clear-stream _test-input-stream)
 1976     (clear-stream $_test-input-buffered-file->buffer)
 1977     (clear-stream _test-output-stream)
 1978     (clear-stream $_test-output-buffered-file->buffer)
 1979     #
 1980     (write _test-input-stream "fn foo {\n")
 1981     (write _test-input-stream "  a: {\n")
 1982     (write _test-input-stream "    var x: int\n")
 1983     (write _test-input-stream "    {\n")
 1984     (write _test-input-stream "      var y: int\n")
 1985     (write _test-input-stream "      break-if->= a\n")
 1986     (write _test-input-stream "      increment x\n")
 1987     (write _test-input-stream "      loop\n")
 1988     (write _test-input-stream "    }\n")
 1989     (write _test-input-stream "  }\n")
 1990     (write _test-input-stream "}\n")
 1991     # convert
 1992     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 1993     (flush _test-output-buffered-file)
 1994 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
 2000     # check output
 2001     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/0")
 2002     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/1")
 2003     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/2")
 2004     (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")
 2005     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/4")
 2006     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/5")
 2007     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/6")
 2008     (check-next-stream-line-equal _test-output-stream "a:loop:"                 "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/7")
 2009     (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")
 2010     (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/9")
 2011     (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:loop:"   "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/10")
 2012     (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")
 2013     (check-next-stream-line-equal _test-output-stream "        {"               "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/12")
 2014     (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")
 2015     (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")
 2016     (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")
 2017     (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")
 2018     (check-next-stream-line-equal _test-output-stream "        }"               "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/17")
 2019     (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")
 2020     (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")
 2021     (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")
 2022     (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/21")
 2023     (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:break:"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/22")
 2024     (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")
 2025     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/24")
 2026     (check-next-stream-line-equal _test-output-stream "a:break:"                "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/25")
 2027     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/26")
 2028     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/27")
 2029     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/28")
 2030     (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")
 2031     (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")
 2032     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/31")
 2033     # . epilogue
 2034     89/<- %esp 5/r32/ebp
 2035     5d/pop-to-ebp
 2036     c3/return
 2037 
 2038 test-convert-function-with-nonlocal-unconditional-break-and-local-vars:
 2039     # . prologue
 2040     55/push-ebp
 2041     89/<- %ebp 4/r32/esp
 2042     # setup
 2043     (clear-stream _test-input-stream)
 2044     (clear-stream $_test-input-buffered-file->buffer)
 2045     (clear-stream _test-output-stream)
 2046     (clear-stream $_test-output-buffered-file->buffer)
 2047     #
 2048     (write _test-input-stream "fn foo {\n")
 2049     (write _test-input-stream "  a: {\n")
 2050     (write _test-input-stream "    var x: int\n")
 2051     (write _test-input-stream "    {\n")
 2052     (write _test-input-stream "      var y: int\n")
 2053     (write _test-input-stream "      break a\n")
 2054     (write _test-input-stream "      increment x\n")
 2055     (write _test-input-stream "    }\n")
 2056     (write _test-input-stream "  }\n")
 2057     (write _test-input-stream "}\n")
 2058     # convert
 2059     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 2060     (flush _test-output-buffered-file)
 2061 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
 2067     # check output
 2068     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/0")
 2069     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/1")
 2070     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/2")
 2071     (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")
 2072     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/4")
 2073     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/5")
 2074     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/6")
 2075     (check-next-stream-line-equal _test-output-stream "a:loop:"                 "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/7")
 2076     (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/8")
 2077     (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/9")
 2078     (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:loop:"   "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/10")
 2079     (check-next-stream-line-equal _test-output-stream "        68/push 0/imm32" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/11")
 2080     (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")
 2081     (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")
 2082     (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")
 2083     (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/15")
 2084     (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:break:"  "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/16")
 2085     (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")
 2086     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/18")
 2087     (check-next-stream-line-equal _test-output-stream "a:break:"                "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/19")
 2088     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/20")
 2089     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/21")
 2090     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/22")
 2091     (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")
 2092     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/24")
 2093     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/25")
 2094     # . epilogue
 2095     89/<- %esp 5/r32/ebp
 2096     5d/pop-to-ebp
 2097     c3/return
 2098 
 2099 test-convert-function-with-unconditional-break-and-local-vars:
 2100     # . prologue
 2101     55/push-ebp
 2102     89/<- %ebp 4/r32/esp
 2103     # setup
 2104     (clear-stream _test-input-stream)
 2105     (clear-stream $_test-input-buffered-file->buffer)
 2106     (clear-stream _test-output-stream)
 2107     (clear-stream $_test-output-buffered-file->buffer)
 2108     #
 2109     (write _test-input-stream "fn foo {\n")
 2110     (write _test-input-stream "  {\n")
 2111     (write _test-input-stream "    var x: int\n")
 2112     (write _test-input-stream "    {\n")
 2113     (write _test-input-stream "      var y: int\n")
 2114     (write _test-input-stream "      break\n")
 2115     (write _test-input-stream "      increment x\n")
 2116     (write _test-input-stream "    }\n")
 2117     (write _test-input-stream "  }\n")
 2118     (write _test-input-stream "}\n")
 2119     # convert
 2120     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 2121     (flush _test-output-buffered-file)
 2122 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
 2128     # check output
 2129     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-unconditional-break-and-local-vars/0")
 2130     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-unconditional-break-and-local-vars/1")
 2131     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-unconditional-break-and-local-vars/2")
 2132     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-unconditional-break-and-local-vars/3")
 2133     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-unconditional-break-and-local-vars/4")
 2134     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-unconditional-break-and-local-vars/5")
 2135     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-unconditional-break-and-local-vars/6")
 2136     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-unconditional-break-and-local-vars/7")
 2137     (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-unconditional-break-and-local-vars/8")
 2138     (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-unconditional-break-and-local-vars/9")
 2139     (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:loop:"   "F - test-convert-function-with-unconditional-break-and-local-vars/10")
 2140     (check-next-stream-line-equal _test-output-stream "        68/push 0/imm32" "F - test-convert-function-with-unconditional-break-and-local-vars/11")
 2141     (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")
 2142     (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-unconditional-break-and-local-vars/13")
 2143     (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:break:"  "F - test-convert-function-with-unconditional-break-and-local-vars/14")
 2144     (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")
 2145     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-unconditional-break-and-local-vars/16")
 2146     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-unconditional-break-and-local-vars/17")
 2147     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-unconditional-break-and-local-vars/18")
 2148     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-unconditional-break-and-local-vars/19")
 2149     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-unconditional-break-and-local-vars/20")
 2150     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-unconditional-break-and-local-vars/21")
 2151     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-unconditional-break-and-local-vars/22")
 2152     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-unconditional-break-and-local-vars/23")
 2153     # . epilogue
 2154     89/<- %esp 5/r32/ebp
 2155     5d/pop-to-ebp
 2156     c3/return
 2157 
 2158 test-convert-function-with-nonlocal-unconditional-loop-and-local-vars:
 2159     # . prologue
 2160     55/push-ebp
 2161     89/<- %ebp 4/r32/esp
 2162     # setup
 2163     (clear-stream _test-input-stream)
 2164     (clear-stream $_test-input-buffered-file->buffer)
 2165     (clear-stream _test-output-stream)
 2166     (clear-stream $_test-output-buffered-file->buffer)
 2167     #
 2168     (write _test-input-stream "fn foo {\n")
 2169     (write _test-input-stream "  a: {\n")
 2170     (write _test-input-stream "    var x: int\n")
 2171     (write _test-input-stream "    {\n")
 2172     (write _test-input-stream "      var y: int\n")
 2173     (write _test-input-stream "      loop a\n")
 2174     (write _test-input-stream "      increment x\n")
 2175     (write _test-input-stream "    }\n")
 2176     (write _test-input-stream "  }\n")
 2177     (write _test-input-stream "}\n")
 2178     # convert
 2179     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 2180     (flush _test-output-buffered-file)
 2181 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
 2187     # check output
 2188     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/0")
 2189     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/1")
 2190     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/2")
 2191     (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")
 2192     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/4")
 2193     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/5")
 2194     (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/6")
 2195     (check-next-stream-line-equal _test-output-stream "a:loop:"                 "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/7")
 2196     (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/8")
 2197     (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/9")
 2198     (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:loop:"   "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/10")
 2199     (check-next-stream-line-equal _test-output-stream "        68/push 0/imm32" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/11")
 2200     (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")
 2201     (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")
 2202     (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")
 2203     (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/15")
 2204     (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:break:"  "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/16")
 2205     (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")
 2206     (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/18")
 2207     (check-next-stream-line-equal _test-output-stream "a:break:"                "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/19")
 2208     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/20")
 2209     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/21")
 2210     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/22")
 2211     (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")
 2212     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/24")
 2213     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/25")
 2214     # . epilogue
 2215     89/<- %esp 5/r32/ebp
 2216     5d/pop-to-ebp
 2217     c3/return
 2218 
 2219 test-convert-function-with-local-array-var-in-mem:
 2220     # . prologue
 2221     55/push-ebp
 2222     89/<- %ebp 4/r32/esp
 2223     # setup
 2224     (clear-stream _test-input-stream)
 2225     (clear-stream $_test-input-buffered-file->buffer)
 2226     (clear-stream _test-output-stream)
 2227     (clear-stream $_test-output-buffered-file->buffer)
 2228     #
 2229     (write _test-input-stream "fn foo {\n")
 2230     (write _test-input-stream "  var x: (array int 3)\n")
 2231     (write _test-input-stream "}\n")
 2232     # convert
 2233     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 2234     (flush _test-output-buffered-file)
 2235 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
 2241     # check output
 2242     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-local-array-var-in-mem/0")
 2243     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-local-array-var-in-mem/1")
 2244     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-local-array-var-in-mem/2")
 2245     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-local-array-var-in-mem/3")
 2246     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-local-array-var-in-mem/4")
 2247     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-local-array-var-in-mem/5")
 2248     # define x
 2249     (check-next-stream-line-equal _test-output-stream "    (push-n-zero-bytes 0x0000000c)"  "F - test-convert-function-with-local-array-var-in-mem/7")
 2250     (check-next-stream-line-equal _test-output-stream "    68/push 0x0000000c/imm32"  "F - test-convert-function-with-local-array-var-in-mem/8")
 2251     # reclaim x
 2252     (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")
 2253     #
 2254     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-local-array-var-in-mem/10")
 2255     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-local-array-var-in-mem/11")
 2256     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-local-array-var-in-mem/12")
 2257     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-local-array-var-in-mem/13")
 2258     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-local-array-var-in-mem/14")
 2259     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-local-array-var-in-mem/15")
 2260     # . epilogue
 2261     89/<- %esp 5/r32/ebp
 2262     5d/pop-to-ebp
 2263     c3/return
 2264 
 2265 test-convert-address:
 2266     # . prologue
 2267     55/push-ebp
 2268     89/<- %ebp 4/r32/esp
 2269     # setup
 2270     (clear-stream _test-input-stream)
 2271     (clear-stream $_test-input-buffered-file->buffer)
 2272     (clear-stream _test-output-stream)
 2273     (clear-stream $_test-output-buffered-file->buffer)
 2274     #
 2275     (write _test-input-stream "fn foo {\n")
 2276     (write _test-input-stream "  var a: int\n")
 2277     (write _test-input-stream "  var b/eax: (addr int) <- address a\n")
 2278     (write _test-input-stream "}\n")
 2279     # convert
 2280     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 2281     (flush _test-output-buffered-file)
 2282 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
 2288     # check output
 2289     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-address/0")
 2290     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-address/1")
 2291     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-address/2")
 2292     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-address/3")
 2293     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-address/4")
 2294     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-address/5")
 2295     (check-next-stream-line-equal _test-output-stream "    68/push 0/imm32"     "F - test-convert-address/6")
 2296     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"  "F - test-convert-address/7")
 2297     (check-next-stream-line-equal _test-output-stream "    8d/copy-address *(ebp+0xfffffffc) 0x00000000/r32"  "F - test-convert-address/8")
 2298     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"  "F - test-convert-address/9")
 2299     (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-address/10")
 2300     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-address/11")
 2301     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-address/12")
 2302     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-address/13")
 2303     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-address/14")
 2304     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-address/15")
 2305     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-address/16")
 2306     # . epilogue
 2307     89/<- %esp 5/r32/ebp
 2308     5d/pop-to-ebp
 2309     c3/return
 2310 
 2311 test-convert-length-of-array:
 2312     # . prologue
 2313     55/push-ebp
 2314     89/<- %ebp 4/r32/esp
 2315     # setup
 2316     (clear-stream _test-input-stream)
 2317     (clear-stream $_test-input-buffered-file->buffer)
 2318     (clear-stream _test-output-stream)
 2319     (clear-stream $_test-output-buffered-file->buffer)
 2320     #
 2321     (write _test-input-stream "fn foo a: (addr array int) {\n")
 2322     (write _test-input-stream "  var b/eax: (addr array int) <- copy a\n")
 2323     (write _test-input-stream "  var c/eax: int <- length b\n")
 2324     (write _test-input-stream "}\n")
 2325     # convert
 2326     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 2327     (flush _test-output-buffered-file)
 2328 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
 2334     # check output
 2335     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-length-of-array/0")
 2336     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-length-of-array/1")
 2337     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-length-of-array/2")
 2338     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-length-of-array/3")
 2339     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-length-of-array/4")
 2340     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-length-of-array/5")
 2341     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"  "F - test-convert-length-of-array/6")
 2342     (check-next-stream-line-equal _test-output-stream "    8b/-> *(ebp+0x00000008) 0x00000000/r32"  "F - test-convert-length-of-array/7")
 2343     (check-next-stream-line-equal _test-output-stream "    8b/-> *eax 0x00000000/r32"  "F - test-convert-length-of-array/9")
 2344     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"  "F - test-convert-length-of-array/11")
 2345     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-length-of-array/12")
 2346     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-length-of-array/13")
 2347     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-length-of-array/14")
 2348     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-length-of-array/15")
 2349     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-length-of-array/16")
 2350     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-length-of-array/17")
 2351     # . epilogue
 2352     89/<- %esp 5/r32/ebp
 2353     5d/pop-to-ebp
 2354     c3/return
 2355 
 2356 test-convert-length-of-array-on-stack:
 2357     # . prologue
 2358     55/push-ebp
 2359     89/<- %ebp 4/r32/esp
 2360     # setup
 2361     (clear-stream _test-input-stream)
 2362     (clear-stream $_test-input-buffered-file->buffer)
 2363     (clear-stream _test-output-stream)
 2364     (clear-stream $_test-output-buffered-file->buffer)
 2365     #
 2366     (write _test-input-stream "fn foo {\n")
 2367     (write _test-input-stream "  var a: (array int 3)\n")
 2368     (write _test-input-stream "  var b/eax: int <- length a\n")
 2369     (write _test-input-stream "}\n")
 2370     # convert
 2371     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 2372     (flush _test-output-buffered-file)
 2373 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
 2379     # check output
 2380     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-length-of-array-on-stack/0")
 2381     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-length-of-array-on-stack/1")
 2382     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-length-of-array-on-stack/2")
 2383     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-length-of-array-on-stack/3")
 2384     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-length-of-array-on-stack/4")
 2385     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-length-of-array-on-stack/5")
 2386     # define x
 2387     (check-next-stream-line-equal _test-output-stream "    (push-n-zero-bytes 0x0000000c)"  "F - test-convert-length-of-array-on-stack/6")
 2388     (check-next-stream-line-equal _test-output-stream "    68/push 0x0000000c/imm32"  "F - test-convert-length-of-array-on-stack/7")
 2389     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"  "F - test-convert-length-of-array-on-stack/8")
 2390     (check-next-stream-line-equal _test-output-stream "    8b/-> *(ebp+0xfffffff0) 0x00000000/r32"  "F - test-convert-length-of-array-on-stack/9")
 2391     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"  "F - test-convert-length-of-array-on-stack/10")
 2392     (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 0x00000010/imm32"  "F - test-convert-length-of-array-on-stack/11")
 2393     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-length-of-array-on-stack/12")
 2394     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-length-of-array-on-stack/13")
 2395     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-length-of-array-on-stack/14")
 2396     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-length-of-array-on-stack/15")
 2397     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-length-of-array-on-stack/16")
 2398     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-length-of-array-on-stack/17")
 2399     # . epilogue
 2400     89/<- %esp 5/r32/ebp
 2401     5d/pop-to-ebp
 2402     c3/return
 2403 
 2404 test-convert-index-into-array:
 2405     # . prologue
 2406     55/push-ebp
 2407     89/<- %ebp 4/r32/esp
 2408     # setup
 2409     (clear-stream _test-input-stream)
 2410     (clear-stream $_test-input-buffered-file->buffer)
 2411     (clear-stream _test-output-stream)
 2412     (clear-stream $_test-output-buffered-file->buffer)
 2413     #
 2414     (write _test-input-stream "fn foo {\n")
 2415     (write _test-input-stream "  var arr/eax: (addr array int) <- copy 0\n")
 2416     (write _test-input-stream "  var idx/ecx: int <- copy 3\n")
 2417     (write _test-input-stream "  var x/eax: (addr int) <- index arr, idx\n")
 2418     (write _test-input-stream "}\n")
 2419     # convert
 2420     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 2421     (flush _test-output-buffered-file)
 2422 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
 2428     # check output
 2429     (check-next-stream-line-equal _test-output-stream "foo:"                                        "F - test-convert-index-into-array/0")
 2430     (check-next-stream-line-equal _test-output-stream "  # . prologue"                              "F - test-convert-index-into-array/1")
 2431     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"                               "F - test-convert-index-into-array/2")
 2432     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"                      "F - test-convert-index-into-array/3")
 2433     (check-next-stream-line-equal _test-output-stream "  {"                                         "F - test-convert-index-into-array/4")
 2434     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"                       "F - test-convert-index-into-array/5")
 2435     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"                    "F - test-convert-index-into-array/6")
 2436     (check-next-stream-line-equal _test-output-stream "    b8/copy-to-eax 0/imm32"                  "F - test-convert-index-into-array/7")
 2437     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"                    "F - test-convert-index-into-array/8")
 2438     (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 3/imm32"                  "F - test-convert-index-into-array/9")
 2439     (check-next-stream-line-equal _test-output-stream "    8d/copy-address *(eax + ecx<<0x00000002 + 4) 0x00000000/r32"  "F - test-convert-index-into-array/11")
 2440     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx"                     "F - test-convert-index-into-array/13")
 2441     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"                     "F - test-convert-index-into-array/14")
 2442     (check-next-stream-line-equal _test-output-stream "  }"                                         "F - test-convert-index-into-array/15")
 2443     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"                      "F - test-convert-index-into-array/16")
 2444     (check-next-stream-line-equal _test-output-stream "  # . epilogue"                              "F - test-convert-index-into-array/17")
 2445     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"                      "F - test-convert-index-into-array/18")
 2446     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"                             "F - test-convert-index-into-array/19")
 2447     (check-next-stream-line-equal _test-output-stream "  c3/return"                                 "F - test-convert-index-into-array/20")
 2448     # . epilogue
 2449     89/<- %esp 5/r32/ebp
 2450     5d/pop-to-ebp
 2451     c3/return
 2452 
 2453 test-convert-index-into-array-with-literal:
 2454     # . prologue
 2455     55/push-ebp
 2456     89/<- %ebp 4/r32/esp
 2457     # setup
 2458     (clear-stream _test-input-stream)
 2459     (clear-stream $_test-input-buffered-file->buffer)
 2460     (clear-stream _test-output-stream)
 2461     (clear-stream $_test-output-buffered-file->buffer)
 2462     #
 2463     (write _test-input-stream "fn foo {\n")
 2464     (write _test-input-stream "  var arr/eax: (addr array int) <- copy 0\n")
 2465     (write _test-input-stream "  var x/eax: (addr int) <- index arr, 2\n")
 2466     (write _test-input-stream "}\n")
 2467     # convert
 2468     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 2469     (flush _test-output-buffered-file)
 2470 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
 2476     # check output
 2477     (check-next-stream-line-equal _test-output-stream "foo:"                                        "F - test-convert-index-into-array-with-literal/0")
 2478     (check-next-stream-line-equal _test-output-stream "  # . prologue"                              "F - test-convert-index-into-array-with-literal/1")
 2479     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"                               "F - test-convert-index-into-array-with-literal/2")
 2480     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"                      "F - test-convert-index-into-array-with-literal/3")
 2481     (check-next-stream-line-equal _test-output-stream "  {"                                         "F - test-convert-index-into-array-with-literal/4")
 2482     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"                       "F - test-convert-index-into-array-with-literal/5")
 2483     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"                    "F - test-convert-index-into-array-with-literal/6")
 2484     (check-next-stream-line-equal _test-output-stream "    b8/copy-to-eax 0/imm32"                  "F - test-convert-index-into-array-with-literal/7")
 2485                                                                                  # 2 * 4 bytes/elem + 4 bytes for size = offset 12
 2486     (check-next-stream-line-equal _test-output-stream "    8d/copy-address *(eax + 0x0000000c) 0x00000000/r32"  "F - test-convert-index-into-array-with-literal/8")
 2487     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"                     "F - test-convert-index-into-array-with-literal/9")
 2488     (check-next-stream-line-equal _test-output-stream "  }"                                         "F - test-convert-index-into-array-with-literal/10")
 2489     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"                      "F - test-convert-index-into-array-with-literal/11")
 2490     (check-next-stream-line-equal _test-output-stream "  # . epilogue"                              "F - test-convert-index-into-array-with-literal/12")
 2491     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"                      "F - test-convert-index-into-array-with-literal/13")
 2492     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"                             "F - test-convert-index-into-array-with-literal/14")
 2493     (check-next-stream-line-equal _test-output-stream "  c3/return"                                 "F - test-convert-index-into-array-with-literal/15")
 2494     # . epilogue
 2495     89/<- %esp 5/r32/ebp
 2496     5d/pop-to-ebp
 2497     c3/return
 2498 
 2499 test-convert-index-into-array-on-stack:
 2500     # . prologue
 2501     55/push-ebp
 2502     89/<- %ebp 4/r32/esp
 2503     # setup
 2504     (clear-stream _test-input-stream)
 2505     (clear-stream $_test-input-buffered-file->buffer)
 2506     (clear-stream _test-output-stream)
 2507     (clear-stream $_test-output-buffered-file->buffer)
 2508     #
 2509     (write _test-input-stream "fn foo {\n")
 2510     (write _test-input-stream "  var arr: (array int 3)\n")
 2511     (write _test-input-stream "  var idx/eax: int <- copy 2\n")
 2512     (write _test-input-stream "  var x/eax: (addr int) <- index arr, idx\n")
 2513     (write _test-input-stream "}\n")
 2514     # convert
 2515     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 2516     (flush _test-output-buffered-file)
 2517 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
 2523     # check output
 2524     (check-next-stream-line-equal _test-output-stream "foo:"                                        "F - test-convert-index-into-array-on-stack/0")
 2525     (check-next-stream-line-equal _test-output-stream "  # . prologue"                              "F - test-convert-index-into-array-on-stack/1")
 2526     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"                               "F - test-convert-index-into-array-on-stack/2")
 2527     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"                      "F - test-convert-index-into-array-on-stack/3")
 2528     (check-next-stream-line-equal _test-output-stream "  {"                                         "F - test-convert-index-into-array-on-stack/4")
 2529     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"                       "F - test-convert-index-into-array-on-stack/5")
 2530     # var arr
 2531     (check-next-stream-line-equal _test-output-stream "    (push-n-zero-bytes 0x0000000c)"          "F - test-convert-index-into-array-on-stack/6")
 2532     (check-next-stream-line-equal _test-output-stream "    68/push 0x0000000c/imm32"                "F - test-convert-index-into-array-on-stack/7")
 2533     # var idx
 2534     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"                    "F - test-convert-index-into-array-on-stack/8")
 2535     (check-next-stream-line-equal _test-output-stream "    b8/copy-to-eax 2/imm32"                  "F - test-convert-index-into-array-on-stack/9")
 2536     # var x is at (ebp-0x10) + idx<<2 + 4 = ebp + idx<<2 - 0xc
 2537     (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/10")
 2538     # reclaim idx
 2539     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"                     "F - test-convert-index-into-array-on-stack/11")
 2540     # reclaim arr
 2541     (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 0x00000010/imm32"    "F - test-convert-index-into-array-on-stack/12")
 2542     #
 2543     (check-next-stream-line-equal _test-output-stream "  }"                                         "F - test-convert-index-into-array-on-stack/13")
 2544     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"                      "F - test-convert-index-into-array-on-stack/14")
 2545     (check-next-stream-line-equal _test-output-stream "  # . epilogue"                              "F - test-convert-index-into-array-on-stack/15")
 2546     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"                      "F - test-convert-index-into-array-on-stack/16")
 2547     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"                             "F - test-convert-index-into-array-on-stack/17")
 2548     (check-next-stream-line-equal _test-output-stream "  c3/return"                                 "F - test-convert-index-into-array-on-stack/18")
 2549     # . epilogue
 2550     89/<- %esp 5/r32/ebp
 2551     5d/pop-to-ebp
 2552     c3/return
 2553 
 2554 test-convert-index-into-array-on-stack-with-literal:
 2555     # . prologue
 2556     55/push-ebp
 2557     89/<- %ebp 4/r32/esp
 2558     # setup
 2559     (clear-stream _test-input-stream)
 2560     (clear-stream $_test-input-buffered-file->buffer)
 2561     (clear-stream _test-output-stream)
 2562     (clear-stream $_test-output-buffered-file->buffer)
 2563     #
 2564     (write _test-input-stream "fn foo {\n")
 2565     (write _test-input-stream "  var arr: (array int 3)\n")
 2566     (write _test-input-stream "  var x/eax: (addr int) <- index arr, 2\n")
 2567     (write _test-input-stream "}\n")
 2568     # convert
 2569     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 2570     (flush _test-output-buffered-file)
 2571 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
 2577     # check output
 2578     (check-next-stream-line-equal _test-output-stream "foo:"                                        "F - test-convert-index-into-array-on-stack-with-literal/0")
 2579     (check-next-stream-line-equal _test-output-stream "  # . prologue"                              "F - test-convert-index-into-array-on-stack-with-literal/1")
 2580     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"                               "F - test-convert-index-into-array-on-stack-with-literal/2")
 2581     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"                      "F - test-convert-index-into-array-on-stack-with-literal/3")
 2582     (check-next-stream-line-equal _test-output-stream "  {"                                         "F - test-convert-index-into-array-on-stack-with-literal/4")
 2583     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"                       "F - test-convert-index-into-array-on-stack-with-literal/5")
 2584     # var arr
 2585     (check-next-stream-line-equal _test-output-stream "    (push-n-zero-bytes 0x0000000c)"          "F - test-convert-index-into-array-on-stack-with-literal/6")
 2586     (check-next-stream-line-equal _test-output-stream "    68/push 0x0000000c/imm32"                "F - test-convert-index-into-array-on-stack-with-literal/7")
 2587     # var x
 2588     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"                    "F - test-convert-index-into-array-on-stack-with-literal/8")
 2589     # x is at (ebp-0x10) + 4 + 2*4 = ebp-4
 2590     (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/9")
 2591     # reclaim x
 2592     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"                     "F - test-convert-index-into-array-on-stack-with-literal/10")
 2593     # reclaim arr
 2594     (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/11")
 2595     #
 2596     (check-next-stream-line-equal _test-output-stream "  }"                                         "F - test-convert-index-into-array-on-stack-with-literal/12")
 2597     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"                      "F - test-convert-index-into-array-on-stack-with-literal/13")
 2598     (check-next-stream-line-equal _test-output-stream "  # . epilogue"                              "F - test-convert-index-into-array-on-stack-with-literal/14")
 2599     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"                      "F - test-convert-index-into-array-on-stack-with-literal/15")
 2600     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"                             "F - test-convert-index-into-array-on-stack-with-literal/16")
 2601     (check-next-stream-line-equal _test-output-stream "  c3/return"                                 "F - test-convert-index-into-array-on-stack-with-literal/17")
 2602     # . epilogue
 2603     89/<- %esp 5/r32/ebp
 2604     5d/pop-to-ebp
 2605     c3/return
 2606 
 2607 test-convert-index-into-array-using-offset:
 2608     # . prologue
 2609     55/push-ebp
 2610     89/<- %ebp 4/r32/esp
 2611     # setup
 2612     (clear-stream _test-input-stream)
 2613     (clear-stream $_test-input-buffered-file->buffer)
 2614     (clear-stream _test-output-stream)
 2615     (clear-stream $_test-output-buffered-file->buffer)
 2616     #
 2617     (write _test-input-stream "fn foo {\n")
 2618     (write _test-input-stream "  var arr/eax: (addr array int) <- copy 0\n")
 2619     (write _test-input-stream "  var idx/ecx: int <- copy 3\n")
 2620     (write _test-input-stream "  var off/ecx: (offset int) <- compute-offset arr, idx\n")
 2621     (write _test-input-stream "  var x/eax: (addr int) <- index arr, off\n")
 2622     (write _test-input-stream "}\n")
 2623     # convert
 2624     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 2625     (flush _test-output-buffered-file)
 2626 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
 2632     # check output
 2633     (check-next-stream-line-equal _test-output-stream "foo:"                                        "F - test-convert-index-into-array-using-offset/0")
 2634     (check-next-stream-line-equal _test-output-stream "  # . prologue"                              "F - test-convert-index-into-array-using-offset/1")
 2635     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"                               "F - test-convert-index-into-array-using-offset/2")
 2636     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"                      "F - test-convert-index-into-array-using-offset/3")
 2637     (check-next-stream-line-equal _test-output-stream "  {"                                         "F - test-convert-index-into-array-using-offset/4")
 2638     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"                       "F - test-convert-index-into-array-using-offset/5")
 2639     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"                    "F - test-convert-index-into-array-using-offset/6")
 2640     (check-next-stream-line-equal _test-output-stream "    b8/copy-to-eax 0/imm32"                  "F - test-convert-index-into-array-using-offset/7")
 2641     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"                    "F - test-convert-index-into-array-using-offset/8")
 2642     (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 3/imm32"                  "F - test-convert-index-into-array-using-offset/9")
 2643     (check-next-stream-line-equal _test-output-stream "    69/multiply %ecx 0x00000004/imm32 0x00000001/r32"  "F - test-convert-index-into-array-using-offset/10")
 2644     (check-next-stream-line-equal _test-output-stream "    8d/copy-address *(eax + ecx + 4) 0x00000000/r32"  "F - test-convert-index-into-array-using-offset/11")
 2645     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx"                     "F - test-convert-index-into-array-using-offset/12")
 2646     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"                     "F - test-convert-index-into-array-using-offset/13")
 2647     (check-next-stream-line-equal _test-output-stream "  }"                                         "F - test-convert-index-into-array-using-offset/14")
 2648     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"                      "F - test-convert-index-into-array-using-offset/15")
 2649     (check-next-stream-line-equal _test-output-stream "  # . epilogue"                              "F - test-convert-index-into-array-using-offset/16")
 2650     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"                      "F - test-convert-index-into-array-using-offset/17")
 2651     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"                             "F - test-convert-index-into-array-using-offset/18")
 2652     (check-next-stream-line-equal _test-output-stream "  c3/return"                                 "F - test-convert-index-into-array-using-offset/19")
 2653     # . epilogue
 2654     89/<- %esp 5/r32/ebp
 2655     5d/pop-to-ebp
 2656     c3/return
 2657 
 2658 test-convert-index-into-array-using-offset-on-stack:
 2659     # . prologue
 2660     55/push-ebp
 2661     89/<- %ebp 4/r32/esp
 2662     # setup
 2663     (clear-stream _test-input-stream)
 2664     (clear-stream $_test-input-buffered-file->buffer)
 2665     (clear-stream _test-output-stream)
 2666     (clear-stream $_test-output-buffered-file->buffer)
 2667     #
 2668     (write _test-input-stream "fn foo {\n")
 2669     (write _test-input-stream "  var arr/eax: (addr array int) <- copy 0\n")
 2670     (write _test-input-stream "  var idx: int\n")
 2671     (write _test-input-stream "  var off/ecx: (offset int) <- compute-offset arr, idx\n")
 2672     (write _test-input-stream "  var x/eax: (addr int) <- index arr, off\n")
 2673     (write _test-input-stream "}\n")
 2674     # convert
 2675     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 2676     (flush _test-output-buffered-file)
 2677 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
 2683     # check output
 2684     (check-next-stream-line-equal _test-output-stream "foo:"                                        "F - test-convert-index-into-array-using-offset-on-stack/0")
 2685     (check-next-stream-line-equal _test-output-stream "  # . prologue"                              "F - test-convert-index-into-array-using-offset-on-stack/1")
 2686     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"                               "F - test-convert-index-into-array-using-offset-on-stack/2")
 2687     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"                      "F - test-convert-index-into-array-using-offset-on-stack/3")
 2688     (check-next-stream-line-equal _test-output-stream "  {"                                         "F - test-convert-index-into-array-using-offset-on-stack/4")
 2689     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"                       "F - test-convert-index-into-array-using-offset-on-stack/5")
 2690     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"                    "F - test-convert-index-into-array-using-offset-on-stack/6")
 2691     (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")
 2692     (check-next-stream-line-equal _test-output-stream "    68/push 0/imm32"                         "F - test-convert-index-into-array-using-offset-on-stack/8")
 2693     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"                    "F - test-convert-index-into-array-using-offset-on-stack/9")
 2694     (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")
 2695     (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/11")
 2696     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx"                     "F - test-convert-index-into-array-using-offset-on-stack/12")
 2697     (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/13")
 2698     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"                     "F - test-convert-index-into-array-using-offset-on-stack/14")
 2699     (check-next-stream-line-equal _test-output-stream "  }"                                         "F - test-convert-index-into-array-using-offset-on-stack/15")
 2700     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"                      "F - test-convert-index-into-array-using-offset-on-stack/16")
 2701     (check-next-stream-line-equal _test-output-stream "  # . epilogue"                              "F - test-convert-index-into-array-using-offset-on-stack/17")
 2702     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"                      "F - test-convert-index-into-array-using-offset-on-stack/18")
 2703     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"                             "F - test-convert-index-into-array-using-offset-on-stack/19")
 2704     (check-next-stream-line-equal _test-output-stream "  c3/return"                                 "F - test-convert-index-into-array-using-offset-on-stack/20")
 2705     # . epilogue
 2706     89/<- %esp 5/r32/ebp
 2707     5d/pop-to-ebp
 2708     c3/return
 2709 
 2710 test-convert-function-and-type-definition:
 2711     # . prologue
 2712     55/push-ebp
 2713     89/<- %ebp 4/r32/esp
 2714     # setup
 2715     (clear-stream _test-input-stream)
 2716     (clear-stream $_test-input-buffered-file->buffer)
 2717     (clear-stream _test-output-stream)
 2718     (clear-stream $_test-output-buffered-file->buffer)
 2719     #
 2720     (write _test-input-stream "fn foo a: (addr t) {\n")
 2721     (write _test-input-stream "  var _a/eax: (addr t) <- copy a\n")
 2722     (write _test-input-stream "  var b/ecx: (addr int) <- get _a, x\n")
 2723     (write _test-input-stream "  var c/ecx: (addr int) <- get _a, y\n")
 2724     (write _test-input-stream "}\n")
 2725     (write _test-input-stream "type t {\n")
 2726     (write _test-input-stream "  x: int\n")
 2727     (write _test-input-stream "  y: int\n")
 2728     (write _test-input-stream "}\n")
 2729     # convert
 2730     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 2731     (flush _test-output-buffered-file)
 2732 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
 2738     # check output
 2739     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-and-type-definition/0")
 2740     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-and-type-definition/1")
 2741     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-and-type-definition/2")
 2742     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-and-type-definition/3")
 2743     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-and-type-definition/4")
 2744     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-and-type-definition/5")
 2745     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"  "F - test-convert-function-and-type-definition/6")
 2746     (check-next-stream-line-equal _test-output-stream "    8b/-> *(ebp+0x00000008) 0x00000000/r32"  "F - test-convert-function-and-type-definition/7")
 2747     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-convert-function-and-type-definition/8")
 2748     (check-next-stream-line-equal _test-output-stream "    8d/copy-address *(eax + 0x00000000) 0x00000001/r32"  "F - test-convert-function-and-type-definition/9")
 2749     (check-next-stream-line-equal _test-output-stream "    8d/copy-address *(eax + 0x00000004) 0x00000001/r32"  "F - test-convert-function-and-type-definition/11")
 2750     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx" "F - test-convert-function-and-type-definition/13")
 2751     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax" "F - test-convert-function-and-type-definition/14")
 2752     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-and-type-definition/15")
 2753     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-and-type-definition/16")
 2754     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-and-type-definition/17")
 2755     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-and-type-definition/18")
 2756     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-and-type-definition/19")
 2757     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-and-type-definition/20")
 2758     # . epilogue
 2759     89/<- %esp 5/r32/ebp
 2760     5d/pop-to-ebp
 2761     c3/return
 2762 
 2763 test-convert-function-with-local-var-with-user-defined-type:
 2764     # . prologue
 2765     55/push-ebp
 2766     89/<- %ebp 4/r32/esp
 2767     # setup
 2768     (clear-stream _test-input-stream)
 2769     (clear-stream $_test-input-buffered-file->buffer)
 2770     (clear-stream _test-output-stream)
 2771     (clear-stream $_test-output-buffered-file->buffer)
 2772     #
 2773     (write _test-input-stream "fn foo {\n")
 2774     (write _test-input-stream "  var a: t\n")
 2775     (write _test-input-stream "}\n")
 2776     (write _test-input-stream "type t {\n")
 2777     (write _test-input-stream "  x: int\n")
 2778     (write _test-input-stream "  y: int\n")
 2779     (write _test-input-stream "}\n")
 2780     # convert
 2781     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 2782     (flush _test-output-buffered-file)
 2783 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
 2789     # check output
 2790     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-local-var-with-user-defined-type/0")
 2791     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-local-var-with-user-defined-type/1")
 2792     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-local-var-with-user-defined-type/2")
 2793     (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")
 2794     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-local-var-with-user-defined-type/4")
 2795     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-local-var-with-user-defined-type/5")
 2796     (check-next-stream-line-equal _test-output-stream "    68/push 0/imm32"     "F - test-convert-function-with-local-var-with-user-defined-type/6")
 2797     (check-next-stream-line-equal _test-output-stream "    68/push 0/imm32"     "F - test-convert-function-with-local-var-with-user-defined-type/7")
 2798     (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")
 2799     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-local-var-with-user-defined-type/9")
 2800     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-local-var-with-user-defined-type/10")
 2801     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-local-var-with-user-defined-type/11")
 2802     (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")
 2803     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-local-var-with-user-defined-type/13")
 2804     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-local-var-with-user-defined-type/14")
 2805     # . epilogue
 2806     89/<- %esp 5/r32/ebp
 2807     5d/pop-to-ebp
 2808     c3/return
 2809 
 2810 test-convert-function-call-with-arg-of-user-defined-type:
 2811     # . prologue
 2812     55/push-ebp
 2813     89/<- %ebp 4/r32/esp
 2814     # setup
 2815     (clear-stream _test-input-stream)
 2816     (clear-stream $_test-input-buffered-file->buffer)
 2817     (clear-stream _test-output-stream)
 2818     (clear-stream $_test-output-buffered-file->buffer)
 2819     #
 2820     (write _test-input-stream "fn f {\n")
 2821     (write _test-input-stream "  var a: t\n")
 2822     (write _test-input-stream "  foo a\n")
 2823     (write _test-input-stream "}\n")
 2824     (write _test-input-stream "fn foo x: t {\n")
 2825     (write _test-input-stream "}\n")
 2826     (write _test-input-stream "type t {\n")
 2827     (write _test-input-stream "  x: int\n")
 2828     (write _test-input-stream "  y: int\n")
 2829     (write _test-input-stream "}\n")
 2830     # convert
 2831     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 2832     (flush _test-output-buffered-file)
 2833 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
 2839     # check output
 2840     (check-next-stream-line-equal _test-output-stream "f:"                      "F - test-convert-function-call-with-arg-of-user-defined-type/0")
 2841     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-call-with-arg-of-user-defined-type/1")
 2842     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-call-with-arg-of-user-defined-type/2")
 2843     (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")
 2844     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-call-with-arg-of-user-defined-type/4")
 2845     (check-next-stream-line-equal _test-output-stream "$f:0x00000001:loop:"     "F - test-convert-function-call-with-arg-of-user-defined-type/5")
 2846     # var a: t
 2847     (check-next-stream-line-equal _test-output-stream "    68/push 0/imm32"     "F - test-convert-function-call-with-arg-of-user-defined-type/6")
 2848     (check-next-stream-line-equal _test-output-stream "    68/push 0/imm32"     "F - test-convert-function-call-with-arg-of-user-defined-type/7")
 2849     # foo a
 2850     (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")
 2851     #
 2852     (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")
 2853     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-call-with-arg-of-user-defined-type/10")
 2854     (check-next-stream-line-equal _test-output-stream "$f:0x00000001:break:"    "F - test-convert-function-call-with-arg-of-user-defined-type/11")
 2855     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-call-with-arg-of-user-defined-type/12")
 2856     (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")
 2857     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-call-with-arg-of-user-defined-type/14")
 2858     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-call-with-arg-of-user-defined-type/15")
 2859     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-call-with-arg-of-user-defined-type/16")
 2860     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-call-with-arg-of-user-defined-type/17")
 2861     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-call-with-arg-of-user-defined-type/18")
 2862     (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")
 2863     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-call-with-arg-of-user-defined-type/20")
 2864     (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")
 2865     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-call-with-arg-of-user-defined-type/22")
 2866     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-call-with-arg-of-user-defined-type/23")
 2867     # . epilogue
 2868     89/<- %esp 5/r32/ebp
 2869     5d/pop-to-ebp
 2870     c3/return
 2871 
 2872 test-convert-function-call-with-arg-of-user-defined-type-register-indirect:
 2873     # . prologue
 2874     55/push-ebp
 2875     89/<- %ebp 4/r32/esp
 2876     # setup
 2877     (clear-stream _test-input-stream)
 2878     (clear-stream $_test-input-buffered-file->buffer)
 2879     (clear-stream _test-output-stream)
 2880     (clear-stream $_test-output-buffered-file->buffer)
 2881     #
 2882     (write _test-input-stream "fn f {\n")
 2883     (write _test-input-stream "  var a/eax: (addr t) <- copy 0\n")
 2884     (write _test-input-stream "  foo *a\n")
 2885     (write _test-input-stream "}\n")
 2886     (write _test-input-stream "fn foo x: t {\n")
 2887     (write _test-input-stream "}\n")
 2888     (write _test-input-stream "type t {\n")
 2889     (write _test-input-stream "  x: int\n")
 2890     (write _test-input-stream "  y: int\n")
 2891     (write _test-input-stream "}\n")
 2892     # convert
 2893     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 2894     (flush _test-output-buffered-file)
 2895 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
 2901     # check output
 2902     (check-next-stream-line-equal _test-output-stream "f:"                      "F - test-convert-function-call-with-arg-of-user-defined-type/0")
 2903     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-call-with-arg-of-user-defined-type/1")
 2904     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-call-with-arg-of-user-defined-type/2")
 2905     (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")
 2906     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-call-with-arg-of-user-defined-type/4")
 2907     (check-next-stream-line-equal _test-output-stream "$f:0x00000001:loop:"     "F - test-convert-function-call-with-arg-of-user-defined-type/5")
 2908     # var a
 2909     (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")
 2910     (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")
 2911     # foo a
 2912     (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")
 2913     #
 2914     (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")
 2915     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-call-with-arg-of-user-defined-type/10")
 2916     (check-next-stream-line-equal _test-output-stream "$f:0x00000001:break:"    "F - test-convert-function-call-with-arg-of-user-defined-type/11")
 2917     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-call-with-arg-of-user-defined-type/12")
 2918     (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")
 2919     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-call-with-arg-of-user-defined-type/14")
 2920     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-call-with-arg-of-user-defined-type/15")
 2921     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-call-with-arg-of-user-defined-type/16")
 2922     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-call-with-arg-of-user-defined-type/17")
 2923     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-call-with-arg-of-user-defined-type/18")
 2924     (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")
 2925     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-call-with-arg-of-user-defined-type/20")
 2926     (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")
 2927     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-call-with-arg-of-user-defined-type/22")
 2928     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-call-with-arg-of-user-defined-type/23")
 2929     # . epilogue
 2930     89/<- %esp 5/r32/ebp
 2931     5d/pop-to-ebp
 2932     c3/return
 2933 
 2934 # we don't have special support for call-by-reference; just explicitly create
 2935 # a new variable with the address of the arg
 2936 test-convert-function-call-with-arg-of-user-defined-type-by-reference:
 2937     # . prologue
 2938     55/push-ebp
 2939     89/<- %ebp 4/r32/esp
 2940     # setup
 2941     (clear-stream _test-input-stream)
 2942     (clear-stream $_test-input-buffered-file->buffer)
 2943     (clear-stream _test-output-stream)
 2944     (clear-stream $_test-output-buffered-file->buffer)
 2945     #
 2946     (write _test-input-stream "fn f {\n")
 2947     (write _test-input-stream "  var a: t\n")
 2948     (write _test-input-stream "  var b/eax: (addr t) <- address a\n")
 2949     (write _test-input-stream "  foo b\n")
 2950     (write _test-input-stream "}\n")
 2951     (write _test-input-stream "fn foo x: (addr t) {\n")
 2952     (write _test-input-stream "  var x/ecx: (addr int) <- copy x\n")
 2953     (write _test-input-stream "  increment *x\n")
 2954     (write _test-input-stream "}\n")
 2955     (write _test-input-stream "type t {\n")
 2956     (write _test-input-stream "  x: int\n")
 2957     (write _test-input-stream "  y: int\n")
 2958     (write _test-input-stream "}\n")
 2959     # convert
 2960     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 2961     (flush _test-output-buffered-file)
 2962 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
 2968     # check output
 2969     (check-next-stream-line-equal _test-output-stream "f:"                      "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/0")
 2970     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/1")
 2971     (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")
 2972     (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")
 2973     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/4")
 2974     (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")
 2975     # var a: t
 2976     (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")
 2977     (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")
 2978     # var b/eax: (addr t)
 2979     (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")
 2980     (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")
 2981     # foo a
 2982     (check-next-stream-line-equal _test-output-stream "    (foo %eax)"          "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/10")
 2983     #
 2984     (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")
 2985     (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")
 2986     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/13")
 2987     (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")
 2988     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/15")
 2989     (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")
 2990     (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")
 2991     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/18")
 2992     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/19")
 2993     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/20")
 2994     (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")
 2995     (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")
 2996     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/23")
 2997     (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")
 2998     (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")
 2999     (check-next-stream-line-equal _test-output-stream "    89/<- %ecx 0x00000001/r32"  "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/26")
 3000     (check-next-stream-line-equal _test-output-stream "    ff 0/subop/increment *ecx"  "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/27")
 3001     (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/28")
 3002     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/29")
 3003     (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/30")
 3004     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/31")
 3005     (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/32")
 3006     (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/33")
 3007     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/34")
 3008     # . epilogue
 3009     89/<- %esp 5/r32/ebp
 3010     5d/pop-to-ebp
 3011     c3/return
 3012 
 3013 test-convert-get-of-type-on-stack:
 3014     # . prologue
 3015     55/push-ebp
 3016     89/<- %ebp 4/r32/esp
 3017     # setup
 3018     (clear-stream _test-input-stream)
 3019     (clear-stream $_test-input-buffered-file->buffer)
 3020     (clear-stream _test-output-stream)
 3021     (clear-stream $_test-output-buffered-file->buffer)
 3022     #
 3023     (write _test-input-stream "fn foo {\n")
 3024     (write _test-input-stream "  var a: t\n")
 3025     (write _test-input-stream "  var c/ecx: (addr int) <- get a, y\n")
 3026     (write _test-input-stream "}\n")
 3027     (write _test-input-stream "type t {\n")
 3028     (write _test-input-stream "  x: int\n")
 3029     (write _test-input-stream "  y: int\n")
 3030     (write _test-input-stream "}\n")
 3031     # convert
 3032     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 3033     (flush _test-output-buffered-file)
 3034 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
 3040     # check output
 3041     (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-get-of-type-on-stack/0")
 3042     (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-get-of-type-on-stack/1")
 3043     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-get-of-type-on-stack/2")
 3044     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-get-of-type-on-stack/3")
 3045     (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-get-of-type-on-stack/4")
 3046     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-get-of-type-on-stack/5")
 3047     # var a
 3048     (check-next-stream-line-equal _test-output-stream "    68/push 0/imm32"     "F - test-convert-get-of-type-on-stack/6")
 3049     (check-next-stream-line-equal _test-output-stream "    68/push 0/imm32"     "F - test-convert-get-of-type-on-stack/7")
 3050     # var c
 3051     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-convert-get-of-type-on-stack/8")
 3052     # get
 3053     (check-next-stream-line-equal _test-output-stream "    8d/copy-address *(ebp+0xfffffffc) 0x00000001/r32"  "F - test-convert-get-of-type-on-stack/9")
 3054     # reclaim c
 3055     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx" "F - test-convert-get-of-type-on-stack/10")
 3056     # reclaim a
 3057     (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 0x00000008/imm32"  "F - test-convert-get-of-type-on-stack/11")
 3058     (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-get-of-type-on-stack/12")
 3059     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-get-of-type-on-stack/13")
 3060     (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-get-of-type-on-stack/14")
 3061     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-get-of-type-on-stack/15")
 3062     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-get-of-type-on-stack/16")
 3063     (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-get-of-type-on-stack/17")
 3064     # . epilogue
 3065     89/<- %esp 5/r32/ebp
 3066     5d/pop-to-ebp
 3067     c3/return
 3068 
 3069 test-convert-array-of-user-defined-types:
 3070     # . prologue
 3071     55/push-ebp
 3072     89/<- %ebp 4/r32/esp
 3073     # setup
 3074     (clear-stream _test-input-stream)
 3075     (clear-stream $_test-input-buffered-file->buffer)
 3076     (clear-stream _test-output-stream)
 3077     (clear-stream $_test-output-buffered-file->buffer)
 3078     #
 3079     (write _test-input-stream "type t {\n")  # each t is 8 bytes, which is a power of 2
 3080     (write _test-input-stream "  x: int\n")
 3081     (write _test-input-stream "  y: int\n")
 3082     (write _test-input-stream "}\n")
 3083     (write _test-input-stream "fn foo {\n")
 3084     (write _test-input-stream "  var arr/eax: (addr array t) <- copy 0\n")
 3085     (write _test-input-stream "  var idx/ecx: int <- copy 3\n")
 3086     (write _test-input-stream "  var x/eax: (addr int) <- index arr, idx\n")
 3087     (write _test-input-stream "}\n")
 3088     # convert
 3089     (convert-mu _test-input-buffered-file _test-output-buffered-file)
 3090     (flush _test-output-buffered-file)
 3091 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
 3097     # check output
 3098     (check-next-stream-line-equal _test-output-stream "foo:"                                        "F - test-convert-array-of-user-defined-types/0")
 3099     (check-next-stream-line-equal _test-output-stream "  # . prologue"                              "F - test-convert-array-of-user-defined-types/1")
 3100     (check-next-stream-line-equal _test-output-stream "  55/push-ebp"                               "F - test-convert-array-of-user-defined-types/2")
 3101     (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"                      "F - test-convert-array-of-user-defined-types/3")
 3102     (check-next-stream-line-equal _test-output-stream "  {"                                         "F - test-convert-array-of-user-defined-types/4")
 3103     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"                       "F - test-convert-array-of-user-defined-types/5")
 3104     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"                    "F - test-convert-array-of-user-defined-types/6")
 3105     (check-next-stream-line-equal _test-output-stream "    b8/copy-to-eax 0/imm32"                  "F - test-convert-array-of-user-defined-types/7")
 3106     (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"                    "F - test-convert-array-of-user-defined-types/8")
 3107     (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 3/imm32"                  "F - test-convert-array-of-user-defined-types/9")
 3108     (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")
 3109     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx"                     "F - test-convert-array-of-user-defined-types/13")
 3110     (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"                     "F - test-convert-array-of-user-defined-types/14")
 3111     (check-next-stream-line-equal _test-output-stream "  }"                                         "F - test-convert-array-of-user-defined-types/15")
 3112     (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"                      "F - test-convert-array-of-user-defined-types/16")
 3113     (check-next-stream-line-equal _test-output-stream "  # . epilogue"                              "F - test-convert-array-of-user-defined-types/17")
 3114     (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"                      "F - test-convert-array-of-user-defined-types/18")
 3115     (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"                             "F - test-convert-array-of-user-defined-types/19")
 3116     (check-next-stream-line-equal _test-output-stream "  c3/return"                                 "F - test-convert-array-of-user-defined-types/20")
 3117     # . epilogue
 3118     89/<- %esp 5/r32/ebp
 3119     5d/pop-to-ebp
 3120     c3/return
 3121 
 3122 #######################################################
 3123 # Parsing
 3124 #######################################################
 3125 
 3126 parse-mu:  # in: (addr buffered-file)
 3127     # pseudocode
 3128     #   var curr-function: (addr (handle function)) = Program->functions
 3129     #   var curr-type: (addr (handle typeinfo)) = Program->types
 3130     #   var line: (stream byte 512)
 3131     #   var word-slice: slice
 3132     #   while true                                  # line loop
 3133     #     clear-stream(line)
 3134     #     read-line-buffered(in, line)
 3135     #     if (line->write == 0) break               # end of file
 3136     #     word-slice = next-mu-token(line)
 3137     #     if slice-empty?(word-slice)               # end of line
 3138     #       continue
 3139     #     else if slice-starts-with?(word-slice, "#")  # comment
 3140     #       continue                                # end of line
 3141     #     else if slice-equal?(word-slice, "fn")
 3142     #       var new-function: (handle function) = allocate(function)
 3143     #       var vars: (stack (addr var) 256)
 3144     #       populate-mu-function-header(in, new-function, vars)
 3145     #       populate-mu-function-body(in, new-function, vars)
 3146     #       assert(vars->top == 0)
 3147     #       *curr-function = new-function
 3148     #       curr-function = &new-function->next
 3149     #     else if slice-equal?(word-slice, "type")
 3150     #       word-slice = next-mu-token(line)
 3151     #       type-id = pos-or-insert-slice(Type-id, word-slice)
 3152     #       var new-type: (handle typeinfo) = find-or-create-typeinfo(type-id)
 3153     #       assert(next-word(line) == "{")
 3154     #       populate-mu-type(in, new-type)
 3155     #     else
 3156     #       abort()
 3157     #
 3158     # . prologue
 3159     55/push-ebp
 3160     89/<- %ebp 4/r32/esp
 3161     # . save registers
 3162     50/push-eax
 3163     51/push-ecx
 3164     52/push-edx
 3165     53/push-ebx
 3166     56/push-esi
 3167     57/push-edi
 3168     # var line/ecx: (stream byte 512)
 3169     81 5/subop/subtract %esp 0x200/imm32
 3170     68/push 0x200/imm32/size
 3171     68/push 0/imm32/read
 3172     68/push 0/imm32/write
 3173     89/<- %ecx 4/r32/esp
 3174     # var word-slice/edx: slice
 3175     68/push 0/imm32/end
 3176     68/push 0/imm32/start
 3177     89/<- %edx 4/r32/esp
 3178     # var curr-function/edi: (addr (handle function))
 3179     bf/copy-to-edi _Program-functions/imm32
 3180     # var curr-type/esi: (addr (handle typeinfo))
 3181     be/copy-to-esi _Program-types/imm32
 3182     # var vars/ebx: (stack (addr var) 256)
 3183     81 5/subop/subtract %esp 0x400/imm32
 3184     68/push 0x400/imm32/size
 3185     68/push 0/imm32/top
 3186     89/<- %ebx 4/r32/esp
 3187     {
 3188 $parse-mu:line-loop:
 3189       (clear-stream %ecx)
 3190       (read-line-buffered *(ebp+8) %ecx)
 3191       # if (line->write == 0) break
 3192       81 7/subop/compare *ecx 0/imm32
 3193       0f 84/jump-if-= break/disp32
 3194 +--  6 lines: #?       # dump line ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 3200       (next-mu-token %ecx %edx)
 3201       # if slice-empty?(word-slice) continue
 3202       (slice-empty? %edx)
 3203       3d/compare-eax-and 0/imm32/false
 3204       0f 85/jump-if-!= loop/disp32
 3205       # if (*word-slice->start == "#") continue
 3206       # . eax = *word-slice->start
 3207       8b/-> *edx 0/r32/eax
 3208       8a/copy-byte *eax 0/r32/AL
 3209       81 4/subop/and %eax 0xff/imm32
 3210       # . if (eax == '#') continue
 3211       3d/compare-eax-and 0x23/imm32/hash
 3212       0f 84/jump-if-= loop/disp32
 3213       # if (slice-equal?(word-slice, "fn")) parse a function
 3214       {
 3215 $parse-mu:fn:
 3216         (slice-equal? %edx "fn")
 3217         3d/compare-eax-and 0/imm32/false
 3218         0f 84/jump-if-= break/disp32
 3219         # var new-function/eax: (handle function) = populate-mu-function(line, in, vars)
 3220         (allocate Heap *Function-size)  # => eax
 3221         (clear-stack %ebx)
 3222         (populate-mu-function-header %ecx %eax %ebx)
 3223         (populate-mu-function-body *(ebp+8) %eax %ebx)
 3224         # *curr-function = new-function
 3225         89/<- *edi 0/r32/eax
 3226         # curr-function = &new-function->next
 3227         8d/address-> *(eax+0x14) 7/r32/edi  # Function-next
 3228         e9/jump $parse-mu:line-loop/disp32
 3229       }
 3230       # if (slice-equal?(word-slice, "type")) parse a type (struct/record) definition
 3231       {
 3232 $parse-mu:type:
 3233         (slice-equal? %edx "type")
 3234         3d/compare-eax-and 0/imm32
 3235         0f 84/jump-if-= break/disp32
 3236         (next-mu-token %ecx %edx)
 3237         # var type-id/eax: int
 3238         (pos-or-insert-slice Type-id %edx)  # => eax
 3239         # var new-type/eax: (handle typeinfo)
 3240         (find-or-create-typeinfo %eax)  # => eax
 3241         # TODO: ensure that 'line' has nothing else but '{'
 3242         (populate-mu-type *(ebp+8) %eax)  # => eax
 3243         e9/jump $parse-mu:line-loop/disp32
 3244       }
 3245       # otherwise abort
 3246       e9/jump $parse-mu:error1/disp32
 3247     } # end line loop
 3248 $parse-mu:end:
 3249     # . reclaim locals
 3250     81 0/subop/add %esp 0x630/imm32
 3251     # . restore registers
 3252     5f/pop-to-edi
 3253     5e/pop-to-esi
 3254     5b/pop-to-ebx
 3255     5a/pop-to-edx
 3256     59/pop-to-ecx
 3257     58/pop-to-eax
 3258     # . epilogue
 3259     89/<- %esp 5/r32/ebp
 3260     5d/pop-to-ebp
 3261     c3/return
 3262 
 3263 $parse-mu:error1:
 3264     # error("unexpected top-level command: " word-slice "\n")
 3265     (write-buffered Stderr "unexpected top-level command: ")
 3266     (write-slice-buffered Stderr %edx)
 3267     (write-buffered Stderr "\n")
 3268     (flush Stderr)
 3269     # . syscall(exit, 1)
 3270     bb/copy-to-ebx  1/imm32
 3271     b8/copy-to-eax  1/imm32/exit
 3272     cd/syscall  0x80/imm8
 3273     # never gets here
 3274 
 3275 $parse-mu:error2:
 3276     # error(vars->top " vars not reclaimed after fn '" new-function->name "'\n")
 3277     (print-int32-buffered Stderr *ebx)
 3278     (write-buffered Stderr " vars not reclaimed after fn '")
 3279     (write-slice-buffered Stderr *eax)  # Function-name
 3280     (write-buffered Stderr "'\n")
 3281     (flush Stderr)
 3282     # . syscall(exit, 1)
 3283     bb/copy-to-ebx  1/imm32
 3284     b8/copy-to-eax  1/imm32/exit
 3285     cd/syscall  0x80/imm8
 3286     # never gets here
 3287 
 3288 # scenarios considered:
 3289 # ✗ fn foo  # no block
 3290 # ✓ fn foo {
 3291 # ✗ fn foo { {
 3292 # ✗ fn foo { }
 3293 # ✗ fn foo { } {
 3294 # ✗ fn foo x {
 3295 # ✗ fn foo x: {
 3296 # ✓ fn foo x: int {
 3297 # ✓ fn foo x: int {
 3298 # ✓ fn foo x: int -> y/eax: int {
 3299 populate-mu-function-header:  # first-line: (addr stream byte), out: (handle function), vars: (addr stack (handle var))
 3300     # pseudocode:
 3301     #   var name: slice
 3302     #   next-mu-token(first-line, name)
 3303     #   assert(name not in '{' '}' '->')
 3304     #   out->name = slice-to-string(name)
 3305     #   ## inouts
 3306     #   while true
 3307     #     ## name
 3308     #     name = next-mu-token(first-line)
 3309     #     if (name == '{') goto done
 3310     #     if (name == '->') break
 3311     #     assert(name != '}')
 3312     #     var v: (handle var) = parse-var-with-type(name, first-line)
 3313     #     assert(v->register == null)
 3314     #     # v->block-depth is implicitly 0
 3315     #     out->inouts = append(out->inouts, v)
 3316     #     push(vars, v)
 3317     #   ## outputs
 3318     #   while true
 3319     #     ## name
 3320     #     name = next-mu-token(first-line)
 3321     #     assert(name not in '{' '}' '->')
 3322     #     var v: (handle var) = parse-var-with-type(name, first-line)
 3323     #     assert(v->register != null)
 3324     #     out->outputs = append(out->outputs, v)
 3325     #   done:
 3326     #
 3327     # . prologue
 3328     55/push-ebp
 3329     89/<- %ebp 4/r32/esp
 3330     # . save registers
 3331     50/push-eax
 3332     51/push-ecx
 3333     52/push-edx
 3334     53/push-ebx
 3335     57/push-edi
 3336     # edi = out
 3337     8b/-> *(ebp+0xc) 7/r32/edi
 3338     # var word-slice/ecx: slice
 3339     68/push 0/imm32/end
 3340     68/push 0/imm32/start
 3341     89/<- %ecx 4/r32/esp
 3342     # read function name
 3343     (next-mu-token *(ebp+8) %ecx)
 3344     # error checking
 3345     # TODO: error if name starts with 'break' or 'loop'
 3346     # if (word-slice == '{') abort
 3347     (slice-equal? %ecx "{")   # => eax
 3348     3d/compare-eax-and 0/imm32/false
 3349     0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
 3350     # if (word-slice == '->') abort
 3351     (slice-equal? %ecx "->")   # => eax
 3352     3d/compare-eax-and 0/imm32/false
 3353     0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
 3354     # if (word-slice == '}') abort
 3355     (slice-equal? %ecx "}")   # => eax
 3356     3d/compare-eax-and 0/imm32/false
 3357     0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
 3358     # save function name
 3359     (slice-to-string Heap %ecx)  # => eax
 3360     89/<- *edi 0/r32/eax  # Function-name
 3361     # save function inouts
 3362     {
 3363 $populate-mu-function-header:check-for-inout:
 3364       (next-mu-token *(ebp+8) %ecx)
 3365       # if (word-slice == '{') goto done
 3366       (slice-equal? %ecx "{")   # => eax
 3367       3d/compare-eax-and 0/imm32/false
 3368       0f 85/jump-if-!= $populate-mu-function-header:done/disp32
 3369       # if (word-slice == '->') break
 3370       (slice-equal? %ecx "->")   # => eax
 3371       3d/compare-eax-and 0/imm32/false
 3372       0f 85/jump-if-!= break/disp32
 3373       # if (word-slice == '}') abort
 3374       (slice-equal? %ecx "}")   # => eax
 3375       3d/compare-eax-and 0/imm32/false
 3376       0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
 3377       # var v/ebx: (handle var) = parse-var-with-type(word-slice, first-line)
 3378       (parse-var-with-type %ecx *(ebp+8))  # => eax
 3379       89/<- %ebx 0/r32/eax
 3380       # assert(v->register == null)
 3381       81 7/subop/compare *(ebx+0x10) 0/imm32  # Var-register
 3382       0f 85/jump-if-!= $populate-mu-function-header:error2/disp32
 3383       # v->block-depth is implicitly 0
 3384       #
 3385       # out->inouts = append(out->inouts, v)
 3386       (append-list Heap %ebx *(edi+8))  # Function-inouts => eax
 3387       89/<- *(edi+8) 0/r32/eax  # Function-inouts
 3388       # push(vars, v)
 3389       (push *(ebp+0x10) %ebx)
 3390       #
 3391       e9/jump loop/disp32
 3392     }
 3393     # save function outputs
 3394     {
 3395 $populate-mu-function-header:check-for-out:
 3396       (next-mu-token *(ebp+8) %ecx)
 3397       # if (word-slice == '{') break
 3398       (slice-equal? %ecx "{")   # => eax
 3399       3d/compare-eax-and 0/imm32/false
 3400       0f 85/jump-if-!= break/disp32
 3401       # if (word-slice == '->') abort
 3402       (slice-equal? %ecx "->")   # => eax
 3403       3d/compare-eax-and 0/imm32/false
 3404       0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
 3405       # if (word-slice == '}') abort
 3406       (slice-equal? %ecx "}")   # => eax
 3407       3d/compare-eax-and 0/imm32/false
 3408       0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
 3409       #
 3410       (parse-var-with-type %ecx *(ebp+8))  # => eax
 3411       89/<- %ebx 0/r32/eax
 3412       # assert(var->register != null)
 3413       81 7/subop/compare *(ebx+0x10) 0/imm32  # Var-register
 3414       0f 84/jump-if-= $populate-mu-function-header:error3/disp32
 3415       (append-list Heap %ebx *(edi+0xc))  # Function-outputs => eax
 3416       89/<- *(edi+0xc) 0/r32/eax  # Function-outputs
 3417       e9/jump loop/disp32
 3418     }
 3419 $populate-mu-function-header:done:
 3420     (check-no-tokens-left *(ebp+8))
 3421 $populate-mu-function-header:end:
 3422     # . reclaim locals
 3423     81 0/subop/add %esp 8/imm32
 3424     # . restore registers
 3425     5f/pop-to-edi
 3426     5b/pop-to-ebx
 3427     5a/pop-to-edx
 3428     59/pop-to-ecx
 3429     58/pop-to-eax
 3430     # . epilogue
 3431     89/<- %esp 5/r32/ebp
 3432     5d/pop-to-ebp
 3433     c3/return
 3434 
 3435 $populate-mu-function-header:error1:
 3436     # error("function header not in form 'fn <name> {'")
 3437     (write-buffered Stderr "function header not in form 'fn <name> [inouts] [-> outputs] {' -- '")
 3438     (flush Stderr)
 3439     (rewind-stream *(ebp+8))
 3440     (write-stream 2 *(ebp+8))
 3441     (write-buffered Stderr "'\n")
 3442     (flush Stderr)
 3443     # . syscall(exit, 1)
 3444     bb/copy-to-ebx  1/imm32
 3445     b8/copy-to-eax  1/imm32/exit
 3446     cd/syscall  0x80/imm8
 3447     # never gets here
 3448 
 3449 $populate-mu-function-header:error2:
 3450     # error("function input '" var "' cannot be in a register")
 3451     (write-buffered Stderr "function input '")
 3452     (write-buffered Stderr *ebx)  # Var-name
 3453     (write-buffered Stderr "' cannot be in a register")
 3454     (flush Stderr)
 3455     # . syscall(exit, 1)
 3456     bb/copy-to-ebx  1/imm32
 3457     b8/copy-to-eax  1/imm32/exit
 3458     cd/syscall  0x80/imm8
 3459     # never gets here
 3460 
 3461 $populate-mu-function-header:error3:
 3462     # error("function input '" var "' must be in a register")
 3463     (write-buffered Stderr "function input '")
 3464     (write-buffered Stderr *eax)  # Var-name
 3465     (write-buffered Stderr " must be in a register'")
 3466     (flush Stderr)
 3467     (rewind-stream *(ebp+8))
 3468     (write-stream 2 *(ebp+8))
 3469     (write-buffered Stderr "'\n")
 3470     (flush Stderr)
 3471     # . syscall(exit, 1)
 3472     bb/copy-to-ebx  1/imm32
 3473     b8/copy-to-eax  1/imm32/exit
 3474     cd/syscall  0x80/imm8
 3475     # never gets here
 3476 
 3477 test-function-header-with-arg:
 3478     # . prologue
 3479     55/push-ebp
 3480     89/<- %ebp 4/r32/esp
 3481     # setup
 3482     (clear-stream _test-input-stream)
 3483     (write _test-input-stream "foo n: int {\n")
 3484     # var result/ecx: function
 3485     2b/subtract *Function-size 4/r32/esp
 3486     89/<- %ecx 4/r32/esp
 3487     (zero-out %ecx *Function-size)
 3488     # var vars/ebx: (stack (addr var) 16)
 3489     81 5/subop/subtract %esp 0x10/imm32
 3490     68/push 0x10/imm32/size
 3491     68/push 0/imm32/top
 3492     89/<- %ebx 4/r32/esp
 3493     # convert
 3494     (populate-mu-function-header _test-input-stream %ecx %ebx)
 3495     # check result
 3496     (check-strings-equal *ecx "foo" "F - test-function-header-with-arg/name")  # Function-name
 3497     # edx: (handle list var) = result->inouts
 3498     8b/-> *(ecx+8) 2/r32/edx  # Function-inouts
 3499     # ebx: (handle var) = result->inouts->value
 3500     8b/-> *edx 3/r32/ebx  # List-value
 3501     (check-strings-equal *ebx "n" "F - test-function-header-with-arg/inout:0")  # Var-name
 3502     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
 3503     (check-ints-equal *ebx 1 "F - test-function-header-with-arg/inout:0/type:0")  # Tree-is-atom
 3504     (check-ints-equal *(ebx+4) 1 "F - test-function-header-with-arg/inout:0/type:1")  # Tree-value
 3505     (check-ints-equal *(ebx+8) 0 "F - test-function-header-with-arg/inout:0/type:2")  # Tree-right
 3506     # . epilogue
 3507     89/<- %esp 5/r32/ebp
 3508     5d/pop-to-ebp
 3509     c3/return
 3510 
 3511 test-function-header-with-multiple-args:
 3512     # . prologue
 3513     55/push-ebp
 3514     89/<- %ebp 4/r32/esp
 3515     # setup
 3516     (clear-stream _test-input-stream)
 3517     (write _test-input-stream "foo a: int, b: int c: int {\n")
 3518     # result/ecx: (handle function)
 3519     2b/subtract *Function-size 4/r32/esp
 3520     89/<- %ecx 4/r32/esp
 3521     (zero-out %ecx *Function-size)
 3522     # var vars/ebx: (stack (addr var) 16)
 3523     81 5/subop/subtract %esp 0x10/imm32
 3524     68/push 0x10/imm32/size
 3525     68/push 0/imm32/top
 3526     89/<- %ebx 4/r32/esp
 3527     # convert
 3528     (populate-mu-function-header _test-input-stream %ecx %ebx)
 3529     # check result
 3530     (check-strings-equal *ecx "foo")  # Function-name
 3531     # edx: (handle list var) = result->inouts
 3532     8b/-> *(ecx+8) 2/r32/edx  # Function-inouts
 3533 $test-function-header-with-multiple-args:inout0:
 3534     # ebx: (handle var) = result->inouts->value
 3535     8b/-> *edx 3/r32/ebx  # List-value
 3536     (check-strings-equal *ebx "a" "F - test-function-header-with-multiple-args/inout:0")  # Var-name
 3537     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
 3538     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args/inout:0/type:0")  # Tree-is-atom
 3539     (check-ints-equal *(ebx+4) 1 "F - test-function-header-with-multiple-args/inout:0/type:1")  # Tree-value
 3540     (check-ints-equal *(ebx+8) 0 "F - test-function-header-with-multiple-args/inout:0/type:2")  # Tree-right
 3541     # edx = result->inouts->next
 3542     8b/-> *(edx+4) 2/r32/edx  # List-next
 3543 $test-function-header-with-multiple-args:inout1:
 3544     # ebx = result->inouts->next->value
 3545     8b/-> *edx 3/r32/ebx  # List-value
 3546     (check-strings-equal *ebx "b" "F - test-function-header-with-multiple-args/inout:1")  # Var-name
 3547     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
 3548     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args/inout:1/type:0")  # Tree-is-atom
 3549     (check-ints-equal *(ebx+4) 1 "F - test-function-header-with-multiple-args/inout:1/type:1")  # Tree-value
 3550     (check-ints-equal *(ebx+8) 0 "F - test-function-header-with-multiple-args/inout:1/type:2")  # Tree-right
 3551     # edx = result->inouts->next->next
 3552     8b/-> *(edx+4) 2/r32/edx  # List-next
 3553 $test-function-header-with-multiple-args:inout2:
 3554     # ebx = result->inouts->next->next->value
 3555     8b/-> *edx 3/r32/ebx  # List-value
 3556     (check-strings-equal *ebx "c" "F - test-function-header-with-multiple-args/inout:2")  # Var-name
 3557     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
 3558     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args/inout:2/type:0")  # Tree-is-atom
 3559     (check-ints-equal *(ebx+4) 1 "F - test-function-header-with-multiple-args/inout:2/type:1")  # Tree-value
 3560     (check-ints-equal *(ebx+8) 0 "F - test-function-header-with-multiple-args/inout:2/type:2")  # Tree-right
 3561     # . epilogue
 3562     89/<- %esp 5/r32/ebp
 3563     5d/pop-to-ebp
 3564     c3/return
 3565 
 3566 test-function-with-multiple-args-and-outputs:
 3567     # . prologue
 3568     55/push-ebp
 3569     89/<- %ebp 4/r32/esp
 3570     # setup
 3571     (clear-stream _test-input-stream)
 3572     (write _test-input-stream "foo a: int, b: int, c: int -> x/ecx: int y/edx: int {\n")
 3573     # result/ecx: (handle function)
 3574     2b/subtract *Function-size 4/r32/esp
 3575     89/<- %ecx 4/r32/esp
 3576     (zero-out %ecx *Function-size)
 3577     # var vars/ebx: (stack (addr var) 16)
 3578     81 5/subop/subtract %esp 0x10/imm32
 3579     68/push 0x10/imm32/size
 3580     68/push 0/imm32/top
 3581     89/<- %ebx 4/r32/esp
 3582     # convert
 3583     (populate-mu-function-header _test-input-stream %ecx %ebx)
 3584     # check result
 3585     (check-strings-equal *ecx "foo")  # Function-name
 3586     # edx: (handle list var) = result->inouts
 3587     8b/-> *(ecx+8) 2/r32/edx  # Function-inouts
 3588     # ebx: (handle var) = result->inouts->value
 3589     8b/-> *edx 3/r32/ebx  # List-value
 3590     (check-strings-equal *ebx "a" "F - test-function-header-with-multiple-args-and-outputs/inout:0")  # Var-name
 3591     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
 3592     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args-and-outputs/inout:0/type:0")  # Tree-is-atom
 3593     (check-ints-equal *(ebx+4) 1 "F - test-function-header-with-multiple-args-and-outputs/inout:0/type:1")  # Tree-value
 3594     (check-ints-equal *(ebx+8) 0 "F - test-function-header-with-multiple-args-and-outputs/inout:0/type:2")  # Tree-right
 3595     # edx = result->inouts->next
 3596     8b/-> *(edx+4) 2/r32/edx  # List-next
 3597     # ebx = result->inouts->next->value
 3598     8b/-> *edx 3/r32/ebx  # List-value
 3599     (check-strings-equal *ebx "b" "F - test-function-header-with-multiple-args-and-outputs/inout:1")  # Var-name
 3600     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
 3601     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args-and-outputs/inout:1/type:0")  # Tree-is-atom
 3602     (check-ints-equal *(ebx+4) 1 "F - test-function-header-with-multiple-args-and-outputs/inout:1/type:1")  # Tree-value
 3603     (check-ints-equal *(ebx+8) 0 "F - test-function-header-with-multiple-args-and-outputs/inout:1/type:2")  # Tree-right
 3604     # edx = result->inouts->next->next
 3605     8b/-> *(edx+4) 2/r32/edx  # List-next
 3606     # ebx = result->inouts->next->next->value
 3607     8b/-> *edx 3/r32/ebx  # List-value
 3608     (check-strings-equal *ebx "c" "F - test-function-header-with-multiple-args-and-outputs/inout:2")  # Var-name
 3609     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
 3610     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args-and-outputs/inout:2/type:0")  # Tree-is-atom
 3611     (check-ints-equal *(ebx+4) 1 "F - test-function-header-with-multiple-args-and-outputs/inout:2/type:1")  # Tree-value
 3612     (check-ints-equal *(ebx+8) 0 "F - test-function-header-with-multiple-args-and-outputs/inout:2/type:2")  # Tree-right
 3613     # edx: (handle list var) = result->outputs
 3614     8b/-> *(ecx+0xc) 2/r32/edx  # Function-outputs
 3615     # ebx: (handle var) = result->outputs->value
 3616     8b/-> *edx 3/r32/ebx  # List-value
 3617     (check-strings-equal *ebx "x" "F - test-function-header-with-multiple-args-and-outputs/output:0")  # Var-name
 3618     (check-strings-equal *(ebx+0x10) "ecx" "F - test-function-header-with-multiple-args-and-outputs/output:0/register")  # Var-register
 3619     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
 3620     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args-and-outputs/output:0/type:0")  # Tree-is-atom
 3621     (check-ints-equal *(ebx+4) 1 "F - test-function-header-with-multiple-args-and-outputs/output:0/type:1")  # Tree-value
 3622     (check-ints-equal *(ebx+8) 0 "F - test-function-header-with-multiple-args-and-outputs/output:0/type:2")  # Tree-right
 3623     # edx = result->outputs->next
 3624     8b/-> *(edx+4) 2/r32/edx  # List-next
 3625     # ebx = result->outputs->next->value
 3626     8b/-> *edx 3/r32/ebx  # List-value
 3627     (check-strings-equal *ebx "y" "F - test-function-header-with-multiple-args-and-outputs/output:1")  # Var-name
 3628     (check-strings-equal *(ebx+0x10) "edx" "F - test-function-header-with-multiple-args-and-outputs/output:0/register")  # Var-register
 3629     8b/-> *(ebx+4) 3/r32/ebx  # Var-type
 3630     (check-ints-equal *ebx 1 "F - test-function-header-with-multiple-args-and-outputs/output:1/type:0")  # Tree-is-atom
 3631     (check-ints-equal *(ebx+4) 1 "F - test-function-header-with-multiple-args-and-outputs/output:1/type:1")  # Tree-value
 3632     (check-ints-equal *(ebx+8) 0 "F - test-function-header-with-multiple-args-and-outputs/output:1/type:2")  # Tree-right
 3633     # . epilogue
 3634     89/<- %esp 5/r32/ebp
 3635     5d/pop-to-ebp
 3636     c3/return
 3637 
 3638 # format for variables with types
 3639 #   x: int
 3640 #   x: int,
 3641 #   x/eax: int
 3642 #   x/eax: int,
 3643 # ignores at most one trailing comma
 3644 # WARNING: modifies name
 3645 parse-var-with-type:  # name: (addr slice), first-line: (addr stream byte) -> result/eax: (handle var)
 3646     # pseudocode:
 3647     #   var s: slice
 3648     #   if (!slice-ends-with(name, ":"))
 3649     #     abort
 3650     #   --name->end to skip ':'
 3651     #   next-token-from-slice(name->start, name->end, '/', s)
 3652     #   result = new-var-from-slice(s)
 3653     #   ## register
 3654     #   next-token-from-slice(s->end, name->end, '/', s)
 3655     #   if (!slice-empty?(s))
 3656     #     v->register = slice-to-string(s)
 3657     #   ## type
 3658     #   var type: (handle tree type-id) = parse-type(first-line)
 3659     #   v->type = type
 3660     #   return v
 3661     #
 3662     # . prologue
 3663     55/push-ebp
 3664     89/<- %ebp 4/r32/esp
 3665     # . save registers
 3666     51/push-ecx
 3667     52/push-edx
 3668     53/push-ebx
 3669     56/push-esi
 3670     57/push-edi
 3671     # esi = name
 3672     8b/-> *(ebp+8) 6/r32/esi
 3673     # if (!slice-ends-with?(name, ":")) abort
 3674     8b/-> *(esi+4) 1/r32/ecx  # Slice-end
 3675     49/decrement-ecx
 3676     8a/copy-byte *ecx 1/r32/CL
 3677     81 4/subop/and %ecx 0xff/imm32
 3678     81 7/subop/compare %ecx 0x3a/imm32/colon
 3679     0f 85/jump-if-!= $parse-var-with-type:abort/disp32
 3680     # --name->end to skip ':'
 3681     ff 1/subop/decrement *(esi+4)
 3682     # var s/ecx: slice
 3683     68/push 0/imm32/end
 3684     68/push 0/imm32/start
 3685     89/<- %ecx 4/r32/esp
 3686 $parse-var-with-type:parse-name:
 3687     (next-token-from-slice *esi *(esi+4) 0x2f %ecx)  # Slice-start, Slice-end, '/'
 3688 $parse-var-with-type:create-var:
 3689     # edi = new-var-from-slice(s)
 3690     (new-var-from-slice Heap %ecx)  # => eax
 3691     89/<- %edi 0/r32/eax
 3692     # save v->register
 3693 $parse-var-with-type:save-register:
 3694     # s = next-token(...)
 3695     (next-token-from-slice *(ecx+4) *(esi+4) 0x2f %ecx)  # s->end, name->end, '/'
 3696     # if (!slice-empty?(s)) v->register = slice-to-string(s)
 3697     {
 3698 $parse-var-with-type:write-register:
 3699       (slice-empty? %ecx)  # => eax
 3700       3d/compare-eax-and 0/imm32/false
 3701       75/jump-if-!= break/disp8
 3702       (slice-to-string Heap %ecx)
 3703       89/<- *(edi+0x10) 0/r32/eax  # Var-register
 3704     }
 3705 $parse-var-with-type:save-type:
 3706     (parse-type Heap *(ebp+0xc))  # => eax
 3707 #?     (write-buffered Stderr "saving to var ")
 3708 #?     (print-int32-buffered Stderr %edi)
 3709 #?     (write-buffered Stderr Newline)
 3710 #?     (flush Stderr)
 3711     89/<- *(edi+4) 0/r32/eax  # Var-type
 3712 $parse-var-with-type:end:
 3713     # return result
 3714     89/<- %eax 7/r32/edi
 3715     # . reclaim locals
 3716     81 0/subop/add %esp 8/imm32
 3717     # . restore registers
 3718     5f/pop-to-edi
 3719     5e/pop-to-esi
 3720     5b/pop-to-ebx
 3721     5a/pop-to-edx
 3722     59/pop-to-ecx
 3723     # . epilogue
 3724     89/<- %esp 5/r32/ebp
 3725     5d/pop-to-ebp
 3726     c3/return
 3727 
 3728 $parse-var-with-type:abort:
 3729     # error("var should have form 'name: type' in '" line "'\n")
 3730     (write-buffered Stderr "var should have form 'name: type' in '")
 3731     (flush Stderr)
 3732     (rewind-stream *(ebp+0xc))
 3733     (write-stream 2 *(ebp+0xc))
 3734     (write-buffered Stderr "'\n")
 3735     (flush Stderr)
 3736     # . syscall(exit, 1)
 3737     bb/copy-to-ebx  1/imm32
 3738     b8/copy-to-eax  1/imm32/exit
 3739     cd/syscall  0x80/imm8
 3740     # never gets here
 3741 
 3742 parse-type:  # ad: (address allocation-descriptor), in: (addr stream byte) -> result/eax: (handle tree type-id)
 3743     # pseudocode:
 3744     #   var s: slice = next-mu-token(in)
 3745     #   assert s != ""
 3746     #   assert s != "->"
 3747     #   assert s != "{"
 3748     #   assert s != "}"
 3749     #   if s == ")"
 3750     #     return 0
 3751     #   result = allocate(Tree)
 3752     #   zero-out(result, *Tree-size)
 3753     #   if s != "("
 3754     #     result->left-is-atom? = true
 3755     #     result->value = pos-or-insert-slice(Type-id, s)
 3756     #     return
 3757     #   result->left = parse-type(ad, in)
 3758     #   result->right = parse-type-tree(ad, in)
 3759     #
 3760     # . prologue
 3761     55/push-ebp
 3762     89/<- %ebp 4/r32/esp
 3763     # . save registers
 3764     51/push-ecx
 3765     52/push-edx
 3766     # var s/ecx: slice
 3767     68/push 0/imm32
 3768     68/push 0/imm32
 3769     89/<- %ecx 4/r32/esp
 3770     # s = next-mu-token(in)
 3771     (next-mu-token *(ebp+0xc) %ecx)
 3772 #?     (write-buffered Stderr "tok: ")
 3773 #?     (write-slice-buffered Stderr %ecx)
 3774 #?     (write-buffered Stderr "$\n")
 3775 #?     (flush Stderr)
 3776     # assert s != ""
 3777     (slice-equal? %ecx "")
 3778     3d/compare-eax-and 0/imm32/false
 3779     0f 85/jump-if-!= $parse-type:abort/disp32
 3780     # assert s != "{"
 3781     (slice-equal? %ecx "{")
 3782     3d/compare-eax-and 0/imm32/false
 3783     0f 85/jump-if-!= $parse-type:abort/disp32
 3784     # assert s != "}"
 3785     (slice-equal? %ecx "}")
 3786     3d/compare-eax-and 0/imm32/false
 3787     0f 85/jump-if-!= $parse-type:abort/disp32
 3788     # assert s != "->"
 3789     (slice-equal? %ecx "->")
 3790     3d/compare-eax-and 0/imm32/false
 3791     0f 85/jump-if-!= $parse-type:abort/disp32
 3792     # if (s == ")") return 0
 3793     (slice-equal? %ecx ")")
 3794     3d/compare-eax-and 0/imm32/false
 3795     b8/copy-to-eax 0/imm32
 3796     0f 85/jump-if-!= $parse-type:end/disp32
 3797     # var result/edx: (handle tree type-id)
 3798     (allocate *(ebp+8) *Tree-size)  # => eax
 3799 $aa-alloc:
 3800     89/<- %edx 0/r32/eax
 3801     {
 3802       # if (s != "(") break
 3803       (slice-equal? %ecx "(")
 3804       3d/compare-eax-and 0/imm32/false
 3805       75/jump-if-!= break/disp8
 3806       # EGREGIOUS HACK for static array sizes: if s is a number, parse it
 3807       {
 3808 $parse-type:int:
 3809         (is-hex-int? %ecx)  # => eax
 3810         3d/compare-eax-and 0/imm32/false
 3811         74/jump-if-= break/disp8
 3812         (parse-hex-int-from-slice %ecx)  # => eax
 3813         89/<- *(edx+4) 0/r32/eax  # Tree-left
 3814         e9/jump $parse-type:return-edx/disp32
 3815       }
 3816 $parse-type:atom:
 3817       # result->left-is-atom? = true
 3818       c7 0/subop/copy *edx 1/imm32/true  # Tree-is-atom
 3819       # result->value = pos-or-insert-slice(Type-id, s)
 3820       (pos-or-insert-slice Type-id %ecx)  # => eax
 3821 #?       (write-buffered Stderr "=> {")
 3822 #?       (print-int32-buffered Stderr %eax)
 3823 #?       (write-buffered Stderr ", 0}\n")
 3824 #?       (flush Stderr)
 3825       89/<- *(edx+4) 0/r32/eax  # Tree-value
 3826       e9/jump $parse-type:return-edx/disp32
 3827     }
 3828 $parse-type:non-atom:
 3829     # otherwise s == "("
 3830     # result->left = parse-type(ad, in)
 3831     (parse-type *(ebp+8) *(ebp+0xc))
 3832 #?     (write-buffered Stderr "=> {")
 3833 #?     (print-int32-buffered Stderr %eax)
 3834     89/<- *(edx+4) 0/r32/eax  # Tree-left
 3835     # result->right = parse-type-tree(ad, in)
 3836     (parse-type-tree *(ebp+8) *(ebp+0xc))
 3837 #?     (write-buffered Stderr Space)
 3838 #?     (print-int32-buffered Stderr %eax)
 3839 #?     (write-buffered Stderr "}\n")
 3840 #?     (flush Stderr)
 3841     89/<- *(edx+8) 0/r32/eax  # Tree-right
 3842 $parse-type:return-edx:
 3843     89/<- %eax 2/r32/edx
 3844 $parse-type:end:
 3845     # . reclaim locals
 3846     81 0/subop/add %esp 8/imm32
 3847     # . restore registers
 3848     5a/pop-to-edx
 3849     59/pop-to-ecx
 3850     # . epilogue
 3851     89/<- %esp 5/r32/ebp
 3852     5d/pop-to-ebp
 3853     c3/return
 3854 
 3855 $parse-type:abort:
 3856     # error("unexpected token when parsing type: '" s "'\n")
 3857     (write-buffered Stderr "unexpected token when parsing type: '")
 3858     (write-slice-buffered Stderr %ecx)
 3859     (write-buffered Stderr "'\n")
 3860     (flush Stderr)
 3861     # . syscall(exit, 1)
 3862     bb/copy-to-ebx  1/imm32
 3863     b8/copy-to-eax  1/imm32/exit
 3864     cd/syscall  0x80/imm8
 3865     # never gets here
 3866 
 3867 parse-type-tree:  # ad: (address allocation-descriptor), in: (addr stream byte) -> result/eax: (handle tree type-id)
 3868     # pseudocode:
 3869     #   var tmp: (handle tree type-id) = parse-type(ad, in)
 3870     #   if tmp == 0
 3871     #     return 0
 3872     #   result = allocate(Tree)
 3873     #   zero-out(result, *Tree-size)
 3874     #   result->left = tmp
 3875     #   result->right = parse-type-tree(ad, in)
 3876     #
 3877     # . prologue
 3878     55/push-ebp
 3879     89/<- %ebp 4/r32/esp
 3880     # . save registers
 3881     51/push-ecx
 3882     52/push-edx
 3883     # var tmp/eax: (handle tree type-id) = parse-type(ad, in)
 3884     (parse-type *(ebp+8) *(ebp+0xc))
 3885     # if (tmp == 0) return tmp
 3886     3d/compare-eax-and 0/imm32
 3887     74/jump-if-= $parse-type-tree:end/disp8
 3888     # var tmp2/ecx = tmp
 3889     89/<- %ecx 0/r32/eax
 3890     # var result/edx: (handle tree type-id)
 3891     (allocate *(ebp+8) *Tree-size)  # => eax
 3892     89/<- %edx 0/r32/eax
 3893     # result->left = tmp2
 3894     89/<- *(edx+4) 1/r32/ecx  # Tree-left
 3895     # result->right = parse-type-tree(ad, in)
 3896     (parse-type-tree *(ebp+8) *(ebp+0xc))
 3897     89/<- *(edx+8) 0/r32/eax  # Tree-right
 3898 $parse-type-tree:return-edx:
 3899     89/<- %eax 2/r32/edx
 3900 $parse-type-tree:end:
 3901     # . restore registers
 3902     5a/pop-to-edx
 3903     59/pop-to-ecx
 3904     # . epilogue
 3905     89/<- %esp 5/r32/ebp
 3906     5d/pop-to-ebp
 3907     c3/return
 3908 
 3909 next-mu-token:  # in: (addr stream byte), out: (addr slice)
 3910     # pseudocode:
 3911     # start:
 3912     #   skip-chars-matching-whitespace(in)
 3913     #   if in->read >= in->write              # end of in
 3914     #     out = {0, 0}
 3915     #     return
 3916     #   out->start = &in->data[in->read]
 3917     #   var curr-byte/eax: byte = in->data[in->read]
 3918     #   if curr->byte == ','                  # comment token
 3919     #     ++in->read
 3920     #     goto start
 3921     #   if curr-byte == '#'                   # comment
 3922     #     goto done                             # treat as eof
 3923     #   if curr-byte == '"'                   # string literal
 3924     #     skip-string(in)
 3925     #     goto done                           # no metadata
 3926     #   if curr-byte == '('
 3927     #     ++in->read
 3928     #     goto done
 3929     #   if curr-byte == ')'
 3930     #     ++in->read
 3931     #     goto done
 3932     #   # read a word
 3933     #   while true
 3934     #     if in->read >= in->write
 3935     #       break
 3936     #     curr-byte = in->data[in->read]
 3937     #     if curr-byte == ' '
 3938     #       break
 3939     #     if curr-byte == '\r'
 3940     #       break
 3941     #     if curr-byte == '\n'
 3942     #       break
 3943     #     if curr-byte == '('
 3944     #       break
 3945     #     if curr-byte == ')'
 3946     #       break
 3947     #     if curr-byte == ','
 3948     #       break
 3949     #     ++in->read
 3950     # done:
 3951     #   out->end = &in->data[in->read]
 3952     #
 3953     # . prologue
 3954     55/push-ebp
 3955     89/<- %ebp 4/r32/esp
 3956     # . save registers
 3957     50/push-eax
 3958     51/push-ecx
 3959     56/push-esi
 3960     57/push-edi
 3961     # esi = in
 3962     8b/-> *(ebp+8) 6/r32/esi
 3963     # edi = out
 3964     8b/-> *(ebp+0xc) 7/r32/edi
 3965 $next-mu-token:start:
 3966     (skip-chars-matching-whitespace %esi)
 3967 $next-mu-token:check0:
 3968     # if (in->read >= in->write) return out = {0, 0}
 3969     # . ecx = in->read
 3970     8b/-> *(esi+4) 1/r32/ecx
 3971     # . if (ecx >= in->write) return out = {0, 0}
 3972     3b/compare<- *esi 1/r32/ecx
 3973     c7 0/subop/copy *edi 0/imm32
 3974     c7 0/subop/copy *(edi+4) 0/imm32
 3975     0f 8d/jump-if->= $next-mu-token:end/disp32
 3976     # out->start = &in->data[in->read]
 3977     8d/copy-address *(esi+ecx+0xc) 0/r32/eax
 3978     89/<- *edi 0/r32/eax
 3979     # var curr-byte/eax: byte = in->data[in->read]
 3980     31/xor-with %eax 0/r32/eax
 3981     8a/copy-byte *(esi+ecx+0xc) 0/r32/AL
 3982     {
 3983 $next-mu-token:check-for-comma:
 3984       # if (curr-byte != ',') break
 3985       3d/compare-eax-and 0x2c/imm32/comma
 3986       75/jump-if-!= break/disp8
 3987       # ++in->read
 3988       ff 0/subop/increment *(esi+4)
 3989       # restart
 3990       e9/jump $next-mu-token:start/disp32
 3991     }
 3992     {
 3993 $next-mu-token:check-for-comment:
 3994       # if (curr-byte != '#') break
 3995       3d/compare-eax-and 0x23/imm32/pound
 3996       75/jump-if-!= break/disp8
 3997       # return eof
 3998       e9/jump $next-mu-token:done/disp32
 3999     }
 4000     {
 4001 $next-mu-token:check-for-string-literal:
 4002       # if (curr-byte != '"') break
 4003       3d/compare-eax-and 0x22/imm32/dquote
 4004       75/jump-if-!= break/disp8
 4005       (skip-string %esi)
 4006       # return
 4007       e9/jump $next-mu-token:done/disp32
 4008     }
 4009     {
 4010 $next-mu-token:check-for-open-paren:
 4011       # if (curr-byte != '(') break
 4012       3d/compare-eax-and 0x28/imm32/open-paren
 4013       75/jump-if-!= break/disp8
 4014       # ++in->read
 4015       ff 0/subop/increment *(esi+4)
 4016       # return
 4017       e9/jump $next-mu-token:done/disp32
 4018     }
 4019     {
 4020 $next-mu-token:check-for-close-paren:
 4021       # if (curr-byte != ')') break
 4022       3d/compare-eax-and 0x29/imm32/close-paren
 4023       75/jump-if-!= break/disp8
 4024       # ++in->read
 4025       ff 0/subop/increment *(esi+4)
 4026       # return
 4027       e9/jump $next-mu-token:done/disp32
 4028     }
 4029     {
 4030 $next-mu-token:regular-word-without-metadata:
 4031       # if (in->read >= in->write) break
 4032       # . ecx = in->read
 4033       8b/-> *(esi+4) 1/r32/ecx
 4034       # . if (ecx >= in->write) break
 4035       3b/compare<- *esi 1/r32/ecx
 4036       7d/jump-if->= break/disp8
 4037       # var c/eax: byte = in->data[in->read]
 4038       31/xor-with %eax 0/r32/eax
 4039       8a/copy-byte *(esi+ecx+0xc) 0/r32/AL
 4040       # if (c == ' ') break
 4041       3d/compare-eax-and 0x20/imm32/space
 4042       74/jump-if-= break/disp8
 4043       # if (c == '\r') break
 4044       3d/compare-eax-and 0xd/imm32/carriage-return
 4045       74/jump-if-= break/disp8
 4046       # if (c == '\n') break
 4047       3d/compare-eax-and 0xa/imm32/newline
 4048       74/jump-if-= break/disp8
 4049       # if (c == '(') break
 4050       3d/compare-eax-and 0x28/imm32/open-paren
 4051       0f 84/jump-if-= break/disp32
 4052       # if (c == ')') break
 4053       3d/compare-eax-and 0x29/imm32/close-paren
 4054       0f 84/jump-if-= break/disp32
 4055       # if (c == ',') break
 4056       3d/compare-eax-and 0x2c/imm32/comma
 4057       0f 84/jump-if-= break/disp32
 4058       # ++in->read
 4059       ff 0/subop/increment *(esi+4)
 4060       #
 4061       e9/jump loop/disp32
 4062     }
 4063 $next-mu-token:done:
 4064     # out->end = &in->data[in->read]
 4065     8b/-> *(esi+4) 1/r32/ecx
 4066     8d/copy-address *(esi+ecx+0xc) 0/r32/eax
 4067     89/<- *(edi+4) 0/r32/eax
 4068 $next-mu-token:end:
 4069     # . restore registers
 4070     5f/pop-to-edi
 4071     5e/pop-to-esi
 4072     59/pop-to-ecx
 4073     58/pop-to-eax
 4074     # . epilogue
 4075     89/<- %esp 5/r32/ebp
 4076     5d/pop-to-ebp
 4077     c3/return
 4078 
 4079 pos-or-insert-slice:  # arr: (addr stream (handle array byte)), s: (addr slice) -> index/eax: int
 4080     # . prologue
 4081     55/push-ebp
 4082     89/<- %ebp 4/r32/esp
 4083     # if (pos-slice(arr, s) != -1) return it
 4084     (pos-slice *(ebp+8) *(ebp+0xc))  # => eax
 4085     3d/compare-eax-and -1/imm32
 4086     75/jump-if-!= $pos-or-insert-slice:end/disp8
 4087 $pos-or-insert-slice:insert:
 4088     (slice-to-string Heap *(ebp+0xc))  # => eax
 4089     (write-int *(ebp+8) %eax)
 4090     (pos-slice *(ebp+8) *(ebp+0xc))  # => eax
 4091 $pos-or-insert-slice:end:
 4092     # . epilogue
 4093     89/<- %esp 5/r32/ebp
 4094     5d/pop-to-ebp
 4095     c3/return
 4096 
 4097 # return the index in an array of strings matching 's', -1 if not found
 4098 # index is denominated in elements, not bytes
 4099 pos-slice:  # arr: (addr stream (handle array byte)), s: (addr slice) -> index/eax: int
 4100     # . prologue
 4101     55/push-ebp
 4102     89/<- %ebp 4/r32/esp
 4103     # . save registers
 4104     51/push-ecx
 4105     52/push-edx
 4106     53/push-ebx
 4107     56/push-esi
 4108 #?     (write-buffered Stderr "pos-slice: ")
 4109 #?     (write-slice-buffered Stderr *(ebp+0xc))
 4110 #?     (write-buffered Stderr "\n")
 4111 #?     (flush Stderr)
 4112     # esi = arr
 4113     8b/-> *(ebp+8) 6/r32/esi
 4114     # var index/ecx: int = 0
 4115     b9/copy-to-ecx 0/imm32
 4116     # var curr/edx: (addr (addr array byte)) = arr->data
 4117     8d/copy-address *(esi+0xc) 2/r32/edx
 4118     # var max/ebx: (addr (addr array byte)) = &arr->data[arr->write]
 4119     8b/-> *esi 3/r32/ebx
 4120     8d/copy-address *(esi+ebx+0xc) 3/r32/ebx
 4121     {
 4122 #?       (write-buffered Stderr "  ")
 4123 #?       (print-int32-buffered Stderr %ecx)
 4124 #?       (write-buffered Stderr "\n")
 4125 #?       (flush Stderr)
 4126       # if (curr >= max) return -1
 4127       39/compare %edx 3/r32/ebx
 4128       b8/copy-to-eax -1/imm32
 4129       73/jump-if-addr>= $pos-slice:end/disp8
 4130       # if (slice-equal?(s, *curr)) break
 4131       (slice-equal? *(ebp+0xc) *edx)  # => eax
 4132       3d/compare-eax-and 0/imm32/false
 4133       75/jump-if-!= break/disp8
 4134       # ++index
 4135       41/increment-ecx
 4136       # curr += 4
 4137       81 0/subop/add %edx 4/imm32
 4138       #
 4139       eb/jump loop/disp8
 4140     }
 4141     # return index
 4142     89/<- %eax 1/r32/ecx
 4143 $pos-slice:end:
 4144 #?     (write-buffered Stderr "=> ")
 4145 #?     (print-int32-buffered Stderr %eax)
 4146 #?     (write-buffered Stderr "\n")
 4147     # . restore registers
 4148     5e/pop-to-esi
 4149     5b/pop-to-ebx
 4150     5a/pop-to-edx
 4151     59/pop-to-ecx
 4152     # . epilogue
 4153     89/<- %esp 5/r32/ebp
 4154     5d/pop-to-ebp
 4155     c3/return
 4156 
 4157 test-parse-var-with-type:
 4158     # . prologue
 4159     55/push-ebp
 4160     89/<- %ebp 4/r32/esp
 4161     # (eax..ecx) = "x:"
 4162     b8/copy-to-eax "x:"/imm32
 4163     8b/-> *eax 1/r32/ecx
 4164     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 4165     05/add-to-eax 4/imm32
 4166     # var slice/ecx: slice = {eax, ecx}
 4167     51/push-ecx
 4168     50/push-eax
 4169     89/<- %ecx 4/r32/esp
 4170     # _test-input-stream contains "int"
 4171     (clear-stream _test-input-stream)
 4172     (write _test-input-stream "int")
 4173     #
 4174     (parse-var-with-type %ecx _test-input-stream)
 4175     8b/-> *eax 2/r32/edx  # Var-name
 4176     (check-strings-equal %edx "x" "F - test-parse-var-with-type/name")
 4177     8b/-> *(eax+4) 2/r32/edx  # Var-type
 4178     (check-ints-equal *edx 1 "F - test-parse-var-with-type/type:0")  # Tree-is-atom
 4179     (check-ints-equal *(edx+4) 1 "F - test-parse-var-with-type/type:1")  # Tree-value
 4180     (check-ints-equal *(edx+8) 0 "F - test-parse-var-with-type/type:2")  # Tree-right
 4181     # . epilogue
 4182     89/<- %esp 5/r32/ebp
 4183     5d/pop-to-ebp
 4184     c3/return
 4185 
 4186 test-parse-var-with-type-and-register:
 4187     # . prologue
 4188     55/push-ebp
 4189     89/<- %ebp 4/r32/esp
 4190     # (eax..ecx) = "x/eax:"
 4191     b8/copy-to-eax "x/eax:"/imm32
 4192     8b/-> *eax 1/r32/ecx
 4193     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 4194     05/add-to-eax 4/imm32
 4195     # var slice/ecx: slice = {eax, ecx}
 4196     51/push-ecx
 4197     50/push-eax
 4198     89/<- %ecx 4/r32/esp
 4199     # _test-input-stream contains "int"
 4200     (clear-stream _test-input-stream)
 4201     (write _test-input-stream "int")
 4202     #
 4203     (parse-var-with-type %ecx _test-input-stream)
 4204     8b/-> *eax 2/r32/edx  # Var-name
 4205     (check-strings-equal %edx "x" "F - test-parse-var-with-type-and-register/name")
 4206     8b/-> *(eax+0x10) 2/r32/edx  # Var-register
 4207     (check-strings-equal %edx "eax" "F - test-parse-var-with-type-and-register/register")
 4208     8b/-> *(eax+4) 2/r32/edx  # Var-type
 4209     (check-ints-equal *edx 1 "F - test-parse-var-with-type-and-register/type:0")  # Tree-is-atom
 4210     (check-ints-equal *(edx+4) 1 "F - test-parse-var-with-type-and-register/type:1")  # Tree-left
 4211     (check-ints-equal *(edx+8) 0 "F - test-parse-var-with-type-and-register/type:2")  # Tree-right
 4212     # . epilogue
 4213     89/<- %esp 5/r32/ebp
 4214     5d/pop-to-ebp
 4215     c3/return
 4216 
 4217 test-parse-var-with-trailing-characters:
 4218     # . prologue
 4219     55/push-ebp
 4220     89/<- %ebp 4/r32/esp
 4221     # (eax..ecx) = "x:"
 4222     b8/copy-to-eax "x:"/imm32
 4223     8b/-> *eax 1/r32/ecx
 4224     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 4225     05/add-to-eax 4/imm32
 4226     # var slice/ecx: slice = {eax, ecx}
 4227     51/push-ecx
 4228     50/push-eax
 4229     89/<- %ecx 4/r32/esp
 4230     # _test-input-stream contains "int,"
 4231     (clear-stream _test-input-stream)
 4232     (write _test-input-stream "int,")
 4233     #
 4234     (parse-var-with-type %ecx _test-input-stream)
 4235     8b/-> *eax 2/r32/edx  # Var-name
 4236     (check-strings-equal %edx "x" "F - test-parse-var-with-trailing-characters/name")
 4237     8b/-> *(eax+0x10) 2/r32/edx  # Var-register
 4238     (check-ints-equal %edx 0 "F - test-parse-var-with-trailing-characters/register")
 4239     8b/-> *(eax+4) 2/r32/edx  # Var-type
 4240     (check-ints-equal *edx 1 "F - test-parse-var-with-trailing-characters/type:0")  # Tree-is-atom
 4241     (check-ints-equal *(edx+4) 1 "F - test-parse-var-with-trailing-characters/type:1")  # Tree-left
 4242     (check-ints-equal *(edx+8) 0 "F - test-parse-var-with-trailing-characters/type:1")  # Tree-right
 4243     # . epilogue
 4244     89/<- %esp 5/r32/ebp
 4245     5d/pop-to-ebp
 4246     c3/return
 4247 
 4248 test-parse-var-with-register-and-trailing-characters:
 4249     # . prologue
 4250     55/push-ebp
 4251     89/<- %ebp 4/r32/esp
 4252     # (eax..ecx) = "x/eax:"
 4253     b8/copy-to-eax "x/eax:"/imm32
 4254     8b/-> *eax 1/r32/ecx
 4255     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 4256     05/add-to-eax 4/imm32
 4257     # var slice/ecx: slice = {eax, ecx}
 4258     51/push-ecx
 4259     50/push-eax
 4260     89/<- %ecx 4/r32/esp
 4261     # _test-input-stream contains "int,"
 4262     (clear-stream _test-input-stream)
 4263     (write _test-input-stream "int,")
 4264     #
 4265     (parse-var-with-type %ecx _test-input-stream)
 4266     8b/-> *eax 2/r32/edx  # Var-name
 4267     (check-strings-equal %edx "x" "F - test-parse-var-with-register-and-trailing-characters/name")
 4268     8b/-> *(eax+0x10) 2/r32/edx  # Var-register
 4269     (check-strings-equal %edx "eax" "F - test-parse-var-with-register-and-trailing-characters/register")
 4270     8b/-> *(eax+4) 2/r32/edx  # Var-type
 4271     (check-ints-equal *edx 1 "F - test-parse-var-with-register-and-trailing-characters/type:0")  # Tree-is-atom
 4272     (check-ints-equal *(edx+4) 1 "F - test-parse-var-with-register-and-trailing-characters/type:1")  # Tree-left
 4273     (check-ints-equal *(edx+8) 0 "F - test-parse-var-with-register-and-trailing-characters/type:2")  # Tree-right
 4274     # . epilogue
 4275     89/<- %esp 5/r32/ebp
 4276     5d/pop-to-ebp
 4277     c3/return
 4278 
 4279 test-parse-var-with-compound-type:
 4280     # . prologue
 4281     55/push-ebp
 4282     89/<- %ebp 4/r32/esp
 4283     # (eax..ecx) = "x:"
 4284     b8/copy-to-eax "x:"/imm32
 4285     8b/-> *eax 1/r32/ecx
 4286     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 4287     05/add-to-eax 4/imm32
 4288     # var slice/ecx: slice = {eax, ecx}
 4289     51/push-ecx
 4290     50/push-eax
 4291     89/<- %ecx 4/r32/esp
 4292     # _test-input-stream contains "(addr int)"
 4293     (clear-stream _test-input-stream)
 4294     (write _test-input-stream "(addr int)")
 4295     #
 4296     (parse-var-with-type %ecx _test-input-stream)
 4297     8b/-> *eax 2/r32/edx  # Var-name
 4298     (check-strings-equal %edx "x" "F - test-parse-var-with-compound-type/name")
 4299     8b/-> *(eax+0x10) 2/r32/edx  # Var-register
 4300     (check-ints-equal %edx 0 "F - test-parse-var-with-compound-type/register")
 4301     # var type/edx: (handle tree type-id) = var->type
 4302     8b/-> *(eax+4) 2/r32/edx  # Var-type
 4303     # type is a non-atom
 4304     (check-ints-equal *edx 0 "F - test-parse-var-with-compound-type/type:0")  # Tree-is-atom
 4305     # type->left == atom(addr)
 4306     8b/-> *(edx+4) 0/r32/eax  # Tree-left
 4307     (check-ints-equal *eax 1 "F - test-parse-var-with-compound-type/type:1")  # Tree-is-atom
 4308     (check-ints-equal *(eax+4) 2 "F - test-parse-var-with-compound-type/type:2")  # Tree-value
 4309     # type->right->left == atom(int)
 4310     8b/-> *(edx+8) 2/r32/edx  # Tree-right
 4311     8b/-> *(edx+4) 0/r32/eax  # Tree-left
 4312     (check-ints-equal *eax 1 "F - test-parse-var-with-compound-type/type:3")  # Tree-is-atom
 4313     (check-ints-equal *(eax+4) 1 "F - test-parse-var-with-compound-type/type:4")  # Tree-value
 4314     # type->right->right == null
 4315     (check-ints-equal *(edx+8) 0 "F - test-parse-var-with-compound-type/type:5")  # Tree-right
 4316     # . epilogue
 4317     89/<- %esp 5/r32/ebp
 4318     5d/pop-to-ebp
 4319     c3/return
 4320 
 4321 # identifier starts with a letter or '$' or '_'
 4322 # no constraints at the moment on later letters
 4323 # all we really want to do so far is exclude '{', '}' and '->'
 4324 is-identifier?:  # in: (addr slice) -> result/eax: boolean
 4325     # . prologue
 4326     55/push-ebp
 4327     89/<- %ebp 4/r32/esp
 4328     # if (slice-empty?(in)) return false
 4329     (slice-empty? *(ebp+8))  # => eax
 4330     3d/compare-eax-and 0/imm32/false
 4331     75/jump-if-!= $is-identifier?:false/disp8
 4332     # var c/eax: byte = *in->start
 4333     8b/-> *(ebp+8) 0/r32/eax
 4334     8b/-> *eax 0/r32/eax
 4335     8a/copy-byte *eax 0/r32/AL
 4336     81 4/subop/and %eax 0xff/imm32
 4337     # if (c == '$') return true
 4338     3d/compare-eax-and 0x24/imm32/$
 4339     74/jump-if-= $is-identifier?:true/disp8
 4340     # if (c == '_') return true
 4341     3d/compare-eax-and 0x5f/imm32/_
 4342     74/jump-if-= $is-identifier?:true/disp8
 4343     # drop case
 4344     25/and-eax-with 0x5f/imm32
 4345     # if (c < 'A') return false
 4346     3d/compare-eax-and 0x41/imm32/A
 4347     7c/jump-if-< $is-identifier?:false/disp8
 4348     # if (c > 'Z') return false
 4349     3d/compare-eax-and 0x5a/imm32/Z
 4350     7f/jump-if-> $is-identifier?:false/disp8
 4351     # otherwise return true
 4352 $is-identifier?:true:
 4353     b8/copy-to-eax 1/imm32/true
 4354     eb/jump $is-identifier?:end/disp8
 4355 $is-identifier?:false:
 4356     b8/copy-to-eax 0/imm32/false
 4357 $is-identifier?:end:
 4358     # . epilogue
 4359     89/<- %esp 5/r32/ebp
 4360     5d/pop-to-ebp
 4361     c3/return
 4362 
 4363 test-is-identifier-dollar:
 4364     # . prologue
 4365     55/push-ebp
 4366     89/<- %ebp 4/r32/esp
 4367     # (eax..ecx) = "$a"
 4368     b8/copy-to-eax "$a"/imm32
 4369     8b/-> *eax 1/r32/ecx
 4370     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 4371     05/add-to-eax 4/imm32
 4372     # var slice/ecx: slice = {eax, ecx}
 4373     51/push-ecx
 4374     50/push-eax
 4375     89/<- %ecx 4/r32/esp
 4376     #
 4377     (is-identifier? %ecx)
 4378     (check-ints-equal %eax 1 "F - test-is-identifier-dollar")
 4379     # . epilogue
 4380     89/<- %esp 5/r32/ebp
 4381     5d/pop-to-ebp
 4382     c3/return
 4383 
 4384 test-is-identifier-underscore:
 4385     # . prologue
 4386     55/push-ebp
 4387     89/<- %ebp 4/r32/esp
 4388     # (eax..ecx) = "_a"
 4389     b8/copy-to-eax "_a"/imm32
 4390     8b/-> *eax 1/r32/ecx
 4391     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 4392     05/add-to-eax 4/imm32
 4393     # var slice/ecx: slice = {eax, ecx}
 4394     51/push-ecx
 4395     50/push-eax
 4396     89/<- %ecx 4/r32/esp
 4397     #
 4398     (is-identifier? %ecx)
 4399     (check-ints-equal %eax 1 "F - test-is-identifier-underscore")
 4400     # . epilogue
 4401     89/<- %esp 5/r32/ebp
 4402     5d/pop-to-ebp
 4403     c3/return
 4404 
 4405 test-is-identifier-a:
 4406     # . prologue
 4407     55/push-ebp
 4408     89/<- %ebp 4/r32/esp
 4409     # (eax..ecx) = "a$"
 4410     b8/copy-to-eax "a$"/imm32
 4411     8b/-> *eax 1/r32/ecx
 4412     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 4413     05/add-to-eax 4/imm32
 4414     # var slice/ecx: slice = {eax, ecx}
 4415     51/push-ecx
 4416     50/push-eax
 4417     89/<- %ecx 4/r32/esp
 4418     #
 4419     (is-identifier? %ecx)
 4420     (check-ints-equal %eax 1 "F - test-is-identifier-a")
 4421     # . epilogue
 4422     89/<- %esp 5/r32/ebp
 4423     5d/pop-to-ebp
 4424     c3/return
 4425 
 4426 test-is-identifier-z:
 4427     # . prologue
 4428     55/push-ebp
 4429     89/<- %ebp 4/r32/esp
 4430     # (eax..ecx) = "z$"
 4431     b8/copy-to-eax "z$"/imm32
 4432     8b/-> *eax 1/r32/ecx
 4433     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 4434     05/add-to-eax 4/imm32
 4435     # var slice/ecx: slice = {eax, ecx}
 4436     51/push-ecx
 4437     50/push-eax
 4438     89/<- %ecx 4/r32/esp
 4439     #
 4440     (is-identifier? %ecx)
 4441     (check-ints-equal %eax 1 "F - test-is-identifier-z")
 4442     # . epilogue
 4443     89/<- %esp 5/r32/ebp
 4444     5d/pop-to-ebp
 4445     c3/return
 4446 
 4447 test-is-identifier-A:
 4448     # . prologue
 4449     55/push-ebp
 4450     89/<- %ebp 4/r32/esp
 4451     # (eax..ecx) = "A$"
 4452     b8/copy-to-eax "A$"/imm32
 4453     8b/-> *eax 1/r32/ecx
 4454     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 4455     05/add-to-eax 4/imm32
 4456     # var slice/ecx: slice = {eax, ecx}
 4457     51/push-ecx
 4458     50/push-eax
 4459     89/<- %ecx 4/r32/esp
 4460     #
 4461     (is-identifier? %ecx)
 4462     (check-ints-equal %eax 1 "F - test-is-identifier-A")
 4463     # . epilogue
 4464     89/<- %esp 5/r32/ebp
 4465     5d/pop-to-ebp
 4466     c3/return
 4467 
 4468 test-is-identifier-Z:
 4469     # . prologue
 4470     55/push-ebp
 4471     89/<- %ebp 4/r32/esp
 4472     # (eax..ecx) = "Z$"
 4473     b8/copy-to-eax "Z$"/imm32
 4474     8b/-> *eax 1/r32/ecx
 4475     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 4476     05/add-to-eax 4/imm32
 4477     # var slice/ecx: slice = {eax, ecx}
 4478     51/push-ecx
 4479     50/push-eax
 4480     89/<- %ecx 4/r32/esp
 4481     #
 4482     (is-identifier? %ecx)
 4483     (check-ints-equal %eax 1 "F - test-is-identifier-Z")
 4484     # . epilogue
 4485     89/<- %esp 5/r32/ebp
 4486     5d/pop-to-ebp
 4487     c3/return
 4488 
 4489 test-is-identifier-@:
 4490     # character before 'A' is invalid
 4491     # . prologue
 4492     55/push-ebp
 4493     89/<- %ebp 4/r32/esp
 4494     # (eax..ecx) = "@a"
 4495     b8/copy-to-eax "@a"/imm32
 4496     8b/-> *eax 1/r32/ecx
 4497     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 4498     05/add-to-eax 4/imm32
 4499     # var slice/ecx: slice = {eax, ecx}
 4500     51/push-ecx
 4501     50/push-eax
 4502     89/<- %ecx 4/r32/esp
 4503     #
 4504     (is-identifier? %ecx)
 4505     (check-ints-equal %eax 0 "F - test-is-identifier-@")
 4506     # . epilogue
 4507     89/<- %esp 5/r32/ebp
 4508     5d/pop-to-ebp
 4509     c3/return
 4510 
 4511 test-is-identifier-square-bracket:
 4512     # character after 'Z' is invalid
 4513     # . prologue
 4514     55/push-ebp
 4515     89/<- %ebp 4/r32/esp
 4516     # (eax..ecx) = "[a"
 4517     b8/copy-to-eax "[a"/imm32
 4518     8b/-> *eax 1/r32/ecx
 4519     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 4520     05/add-to-eax 4/imm32
 4521     # var slice/ecx: slice = {eax, ecx}
 4522     51/push-ecx
 4523     50/push-eax
 4524     89/<- %ecx 4/r32/esp
 4525     #
 4526     (is-identifier? %ecx)
 4527     (check-ints-equal %eax 0 "F - test-is-identifier-@")
 4528     # . epilogue
 4529     89/<- %esp 5/r32/ebp
 4530     5d/pop-to-ebp
 4531     c3/return
 4532 
 4533 test-is-identifier-backtick:
 4534     # character before 'a' is invalid
 4535     # . prologue
 4536     55/push-ebp
 4537     89/<- %ebp 4/r32/esp
 4538     # (eax..ecx) = "`a"
 4539     b8/copy-to-eax "`a"/imm32
 4540     8b/-> *eax 1/r32/ecx
 4541     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 4542     05/add-to-eax 4/imm32
 4543     # var slice/ecx: slice = {eax, ecx}
 4544     51/push-ecx
 4545     50/push-eax
 4546     89/<- %ecx 4/r32/esp
 4547     #
 4548     (is-identifier? %ecx)
 4549     (check-ints-equal %eax 0 "F - test-is-identifier-backtick")
 4550     # . epilogue
 4551     89/<- %esp 5/r32/ebp
 4552     5d/pop-to-ebp
 4553     c3/return
 4554 
 4555 test-is-identifier-curly-brace-open:
 4556     # character after 'z' is invalid; also used for blocks
 4557     # . prologue
 4558     55/push-ebp
 4559     89/<- %ebp 4/r32/esp
 4560     # (eax..ecx) = "{a"
 4561     b8/copy-to-eax "{a"/imm32
 4562     8b/-> *eax 1/r32/ecx
 4563     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 4564     05/add-to-eax 4/imm32
 4565     # var slice/ecx: slice = {eax, ecx}
 4566     51/push-ecx
 4567     50/push-eax
 4568     89/<- %ecx 4/r32/esp
 4569     #
 4570     (is-identifier? %ecx)
 4571     (check-ints-equal %eax 0 "F - test-is-identifier-curly-brace-open")
 4572     # . epilogue
 4573     89/<- %esp 5/r32/ebp
 4574     5d/pop-to-ebp
 4575     c3/return
 4576 
 4577 test-is-identifier-curly-brace-close:
 4578     # . prologue
 4579     55/push-ebp
 4580     89/<- %ebp 4/r32/esp
 4581     # (eax..ecx) = "}a"
 4582     b8/copy-to-eax "}a"/imm32
 4583     8b/-> *eax 1/r32/ecx
 4584     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 4585     05/add-to-eax 4/imm32
 4586     # var slice/ecx: slice = {eax, ecx}
 4587     51/push-ecx
 4588     50/push-eax
 4589     89/<- %ecx 4/r32/esp
 4590     #
 4591     (is-identifier? %ecx)
 4592     (check-ints-equal %eax 0 "F - test-is-identifier-curly-brace-close")
 4593     # . epilogue
 4594     89/<- %esp 5/r32/ebp
 4595     5d/pop-to-ebp
 4596     c3/return
 4597 
 4598 test-is-identifier-hyphen:
 4599     # disallow leading '-' since '->' has special meaning
 4600     # . prologue
 4601     55/push-ebp
 4602     89/<- %ebp 4/r32/esp
 4603     # (eax..ecx) = "-a"
 4604     b8/copy-to-eax "-a"/imm32
 4605     8b/-> *eax 1/r32/ecx
 4606     8d/copy-address *(eax+ecx+4) 1/r32/ecx
 4607     05/add-to-eax 4/imm32
 4608     # var slice/ecx: slice = {eax, ecx}
 4609     51/push-ecx
 4610     50/push-eax
 4611     89/<- %ecx 4/r32/esp
 4612     #
 4613     (is-identifier? %ecx)
 4614     (check-ints-equal %eax 0 "F - test-is-identifier-hyphen")
 4615     # . epilogue
 4616     89/<- %esp 5/r32/ebp
 4617     5d/pop-to-ebp
 4618     c3/return
 4619 
 4620 populate-mu-function-body:  # in: (addr buffered-file), out: (handle function), vars: (addr stack (handle var))
 4621     # . prologue
 4622     55/push-ebp
 4623     89/<- %ebp 4/r32/esp
 4624     # . save registers
 4625     50/push-eax
 4626     56/push-esi
 4627     57/push-edi
 4628     # esi = in
 4629     8b/-> *(ebp+8) 6/r32/esi
 4630     # edi = out
 4631     8b/-> *(ebp+0xc) 7/r32/edi
 4632     # var eax: (handle block) = parse-mu-block(in, vars, fn)
 4633     (parse-mu-block %esi *(ebp+0x10) %edi)  # => eax
 4634     # out->body = eax
 4635     89/<- *(edi+0x10) 0/r32/eax  # Function-body
 4636 $populate-mu-function-body:end:
 4637     # . restore registers
 4638     5f/pop-to-edi
 4639     5e/pop-to-esi
 4640     58/pop-to-eax
 4641     # . epilogue
 4642     89/<- %esp 5/r32/ebp
 4643     5d/pop-to-ebp
 4644     c3/return
 4645 
 4646 # parses a block, assuming that the leading '{' has already been read by the caller
 4647 parse-mu-block:  # in: (addr buffered-file), vars: (addr stack (handle var)), fn: (handle function) -> result/eax: (handle block)
 4648     # pseudocode:
 4649     #   var line: (stream byte 512)
 4650     #   var word-slice: slice
 4651     #   result/eax = allocate(Heap, Stmt-size)
 4652     #   result->tag = 0/block
 4653     #   result->name = some unique name
 4654     #   while true                                  # line loop
 4655     #     clear-stream(line)
 4656     #     read-line-buffered(in, line)
 4657     #     if (line->write == 0) break               # end of file
 4658     #     word-slice = next-mu-token(line)
 4659     #     if slice-empty?(word-slice)               # end of line
 4660     #       continue
 4661     #     else if slice-starts-with?(word-slice, "#")
 4662     #       continue
 4663     #     else if slice-equal?(word-slice, "{")
 4664     #       assert(no-tokens-in(line))
 4665     #       block = parse-mu-block(in, vars, fn)
 4666     #       append-to-block(result, block)
 4667     #     else if slice-equal?(word-slice, "}")
 4668     #       break
 4669     #     else if slice-ends-with?(word-slice, ":")
 4670     #       # TODO: error-check the rest of 'line'
 4671     #       --word-slice->end to skip ':'
 4672     #       named-block = parse-mu-named-block(word-slice, in, vars, fn)
 4673     #       append-to-block(result, named-block)
 4674     #     else if slice-equal?(word-slice, "var")
 4675     #       var-def = parse-mu-var-def(line, vars)
 4676     #       append-to-block(result, var-def)
 4677     #     else
 4678     #       stmt = parse-mu-stmt(line, vars, fn)
 4679     #       append-to-block(result, stmt)
 4680     #   return result
 4681     #
 4682     # . prologue
 4683     55/push-ebp
 4684     89/<- %ebp 4/r32/esp
 4685     # . save registers
 4686     51/push-ecx
 4687     52/push-edx
 4688     53/push-ebx
 4689     57/push-edi
 4690     # var line/ecx: (stream byte 512)
 4691     81 5/subop/subtract %esp 0x200/imm32
 4692     68/push 0x200/imm32/size
 4693     68/push 0/imm32/read
 4694     68/push 0/imm32/write
 4695     89/<- %ecx 4/r32/esp
 4696     # var word-slice/edx: slice
 4697     68/push 0/imm32/end
 4698     68/push 0/imm32/start
 4699     89/<- %edx 4/r32/esp
 4700     # edi = result
 4701     (allocate Heap *Stmt-size)  # => eax
 4702     89/<- %edi 0/r32/eax
 4703     # set result->tag
 4704     c7 0/subop/copy *edi 0/imm32/block  # Stmt-tag
 4705     # set result->var
 4706     (new-block-name *(ebp+0x10))  # => eax
 4707     89/<- *(edi+8) 0/r32/eax  # Block-var
 4708     # push result->var to vars
 4709     (push *(ebp+0xc) %eax)
 4710     {
 4711 $parse-mu-block:line-loop:
 4712       # line = read-line-buffered(in)
 4713       (clear-stream %ecx)
 4714       (read-line-buffered *(ebp+8) %ecx)
 4715 #?       (write-buffered Stderr "line: ")
 4716 #?       (write-stream-data Stderr %ecx)
 4717 #?       (write-buffered Stderr Newline)
 4718 #?       (flush Stderr)
 4719       # if (line->write == 0) break
 4720       81 7/subop/compare *ecx 0/imm32
 4721       0f 84/jump-if-= break/disp32
 4722       # word-slice = next-mu-token(line)
 4723       (next-mu-token %ecx %edx)
 4724 #?       (write-buffered Stderr "word: ")
 4725 #?       (write-slice-buffered Stderr %edx)
 4726 #?       (write-buffered Stderr Newline)
 4727 #?       (flush Stderr)
 4728       # if slice-empty?(word-slice) continue
 4729       (slice-empty? %edx)
 4730       3d/compare-eax-and 0/imm32/false
 4731       0f 85/jump-if-!= loop/disp32
 4732       # if (slice-starts-with?(word-slice, '#') continue
 4733       # . eax = *word-slice->start
 4734       8b/-> *edx 0/r32/eax
 4735       8a/copy-byte *eax 0/r32/AL
 4736       81 4/subop/and %eax 0xff/imm32
 4737       # . if (eax == '#') continue
 4738       3d/compare-eax-and 0x23/imm32/hash
 4739       0f 84/jump-if-= loop/disp32
 4740       # if slice-equal?(word-slice, "{")
 4741       {
 4742 $parse-mu-block:check-for-block:
 4743         (slice-equal? %edx "{")
 4744         3d/compare-eax-and 0/imm32/false
 4745         74/jump-if-= break/disp8
 4746         (check-no-tokens-left %ecx)
 4747         # parse new block and append
 4748         (parse-mu-block *(ebp+8) *(ebp+0xc) *(ebp+0x10))  # => eax
 4749         (append-to-block Heap %edi %eax)
 4750         e9/jump $parse-mu-block:line-loop/disp32
 4751       }
 4752       # if slice-equal?(word-slice, "}") break
 4753 $parse-mu-block:check-for-end:
 4754       (slice-equal? %edx "}")
 4755       3d/compare-eax-and 0/imm32/false
 4756       0f 85/jump-if-!= break/disp32
 4757       # if slice-ends-with?(word-slice, ":") parse named block and append
 4758       {
 4759 $parse-mu-block:check-for-named-block:
 4760         # . eax = *(word-slice->end-1)
 4761         8b/-> *(edx+4) 0/r32/eax
 4762         48/decrement-eax
 4763         8a/copy-byte *eax 0/r32/AL
 4764         81 4/subop/and %eax 0xff/imm32
 4765         # . if (eax != ':') break
 4766         3d/compare-eax-and 0x3a/imm32/colon
 4767         0f 85/jump-if-!= break/disp32
 4768         # TODO: error-check the rest of 'line'
 4769         #
 4770         # skip ':'
 4771         ff 1/subop/decrement *(edx+4)  # Slice-end
 4772         #
 4773         (parse-mu-named-block %edx *(ebp+8) *(ebp+0xc) *(ebp+0x10))  # => eax
 4774         (append-to-block Heap %edi %eax)
 4775         e9/jump $parse-mu-block:line-loop/disp32
 4776       }
 4777       # if slice-equal?(word-slice, "var")
 4778       {
 4779 $parse-mu-block:check-for-var:
 4780         (slice-equal? %edx "var")
 4781         3d/compare-eax-and 0/imm32/false
 4782         74/jump-if-= break/disp8
 4783         #
 4784         (parse-mu-var-def %ecx *(ebp+0xc))  # => eax
 4785         (append-to-block Heap %edi %eax)
 4786         e9/jump $parse-mu-block:line-loop/disp32
 4787       }
 4788 $parse-mu-block:regular-stmt:
 4789       # otherwise
 4790       (parse-mu-stmt %ecx *(ebp+0xc) *(ebp+0x10))  # => eax
 4791       (append-to-block Heap %edi %eax)
 4792       e9/jump loop/disp32
 4793     } # end line loop
 4794     #
 4795     (pop *(ebp+0xc))  # => eax
 4796     # return result
 4797     89/<- %eax 7/r32/edi
 4798 $parse-mu-block:end:
 4799     # . reclaim locals
 4800     81 0/subop/add %esp 0x214/imm32
 4801     # . restore registers
 4802     5f/pop-to-edi
 4803     5b/pop-to-ebx
 4804     5a/pop-to-edx
 4805     59/pop-to-ecx
 4806     # . epilogue
 4807     89/<- %esp 5/r32/ebp
 4808     5d/pop-to-ebp
 4809     c3/return
 4810 
 4811 $parse-mu-block:abort:
 4812     # error("'{' or '}' should be on its own line, but got '")
 4813     (write-buffered Stderr "'{' or '}' should be on its own line, but got '")
 4814     (rewind-stream %ecx)
 4815     (write-stream 2 %ecx)
 4816     (write-buffered Stderr "'\n")
 4817     (flush Stderr)
 4818     # . syscall(exit, 1)
 4819     bb/copy-to-ebx  1/imm32
 4820     b8/copy-to-eax  1/imm32/exit
 4821     cd/syscall  0x80/imm8
 4822     # never gets here
 4823 
 4824 new-block-name:  # fn: (handle function) -> result/eax: (handle var)
 4825     # . prologue
 4826     55/push-ebp
 4827     89/<- %ebp 4/r32/esp
 4828     # . save registers
 4829     51/push-ecx
 4830     52/push-edx
 4831     # var n/ecx: int = len(fn->name) + 10 for an int + 2 for '$:'
 4832     8b/-> *(ebp+8) 0/r32/eax
 4833     8b/-> *eax 0/r32/eax  # Function-name
 4834     8b/-> *eax 0/r32/eax  # String-size
 4835     05/add-to-eax 0xd/imm32  # 10 + 2 for '$:'
 4836     89/<- %ecx 0/r32/eax
 4837     # var name/edx: (stream byte n)
 4838     29/subtract-from %esp 1/r32/ecx
 4839     ff 6/subop/push %ecx
 4840     68/push 0/imm32/read
 4841     68/push 0/imm32/write
 4842     89/<- %edx 4/r32/esp
 4843     (clear-stream %edx)
 4844     # eax = fn->name
 4845     8b/-> *(ebp+8) 0/r32/eax
 4846     8b/-> *eax 0/r32/eax  # Function-name
 4847     # construct result using Next-block-index (and increment it)
 4848     (write %edx "$")
 4849     (write %edx %eax)
 4850     (write %edx ":")
 4851     (print-int32 %edx *Next-block-index)
 4852     ff 0/subop/increment *Next-block-index
 4853     # var s/eax: slice = {name->data, name->data + name->write}  (clobbering edx)
 4854     # . eax = name->write
 4855     8b/-> *edx 0/r32/eax
 4856     # . edx = name->data
 4857     8d/copy-address *(edx+0xc) 2/r32/edx
 4858     # . eax = name->write + name->data
 4859     01/add-to %eax 2/r32/edx
 4860     # . push {edx, eax}
 4861     ff 6/subop/push %eax
 4862     ff 6/subop/push %edx
 4863     89/<- %eax 4/r32/esp
 4864     # result->var = new literal(s)
 4865     (new-literal Heap %eax)  # => eax
 4866 $new-block-name:end:
 4867     # . reclaim locals
 4868     81 0/subop/add %ecx 0xc/imm32  # name.{read/write/len}
 4869     81 0/subop/add %ecx 8/imm32  # slice
 4870     01/add-to %esp 1/r32/ecx
 4871     # . restore registers
 4872     5a/pop-to-edx
 4873     59/pop-to-ecx
 4874     # . epilogue
 4875     89/<- %esp 5/r32/ebp
 4876     5d/pop-to-ebp
 4877     c3/return
 4878 
 4879 == data
 4880 
 4881 # Global state added to each var record when parsing a function
 4882 Next-block-index:  # (addr int)
 4883     1/imm32
 4884 
 4885 == code
 4886 
 4887 check-no-tokens-left:  # line: (addr stream byte)
 4888     # . prologue
 4889     55/push-ebp
 4890     89/<- %ebp 4/r32/esp
 4891     # . save registers
 4892     50/push-eax
 4893     51/push-ecx
 4894     # var s/ecx: slice
 4895     68/push 0/imm32/end
 4896     68/push 0/imm32/start
 4897     89/<- %ecx 4/r32/esp
 4898     #
 4899     (next-mu-token *(ebp+8) %ecx)
 4900     # if slice-empty?(s) return
 4901     (slice-empty? %ecx)
 4902     3d/compare-eax-and 0/imm32/false
 4903     75/jump-if-!= $check-no-tokens-left:end/disp8
 4904     # if (slice-starts-with?(s, '#') return
 4905     # . eax = *s->start
 4906     8b/-> *edx 0/r32/eax
 4907     8a/copy-byte *eax 0/r32/AL
 4908     81 4/subop/and %eax 0xff/imm32
 4909     # . if (eax == '#') continue
 4910     3d/compare-eax-and 0x23/imm32/hash
 4911     74/jump-if-= $check-no-tokens-left:end/disp8
 4912     # abort
 4913     (write-buffered Stderr "'{' or '}' should be on its own line, but got '")
 4914     (rewind-stream %ecx)
 4915     (write-stream 2 %ecx)
 4916     (write-buffered Stderr "'\n")
 4917     (flush Stderr)
 4918     # . syscall(exit, 1)
 4919     bb/copy-to-ebx  1/imm32
 4920     b8/copy-to-eax  1/imm32/exit
 4921     cd/syscall  0x80/imm8
 4922     # never gets here
 4923 $check-no-tokens-left:end:
 4924     # . reclaim locals
 4925     81 0/subop/add %esp 8/imm32
 4926     # . restore registers
 4927     59/pop-to-ecx
 4928     58/pop-to-eax
 4929     # . epilogue
 4930     89/<- %esp 5/r32/ebp
 4931     5d/pop-to-ebp
 4932     c3/return
 4933 
 4934 parse-mu-named-block:  # name: (addr slice), in: (addr buffered-file), vars: (addr stack (handle var)), fn: (handle function) -> result/eax: (handle stmt)
 4935     # pseudocode:
 4936     #   var v: (handle var) = new-literal(name)
 4937     #   push(vars, v)
 4938     #   result = parse-mu-block(in, vars, fn)
 4939     #   pop(vars)
 4940     #   result->name = s
 4941     #   return result
 4942     #
 4943     # . prologue
 4944     55/push-ebp
 4945     89/<- %ebp 4/r32/esp
 4946     # . save registers
 4947     51/push-ecx
 4948     # var v/ecx: (handle var)
 4949     (new-literal Heap *(ebp+8))  # => eax
 4950     89/<- %ecx 0/r32/eax
 4951     # push(vars, v)
 4952     (push *(ebp+0x10) %ecx)
 4953     # eax = result
 4954     (parse-mu-block *(ebp+0xc) *(ebp+0x10) *(ebp+0x14))  # => eax
 4955     # pop the var
 4956     50/push-eax
 4957     (pop *(ebp+0x10))  # => eax
 4958     58/pop-to-eax
 4959     # result->tag = named-block
 4960     c7 0/subop/copy *eax 0/imm32/block  # Stmt-tag
 4961     # result->var = v
 4962     89/<- *(eax+8) 1/r32/ecx  # Block-var
 4963 $parse-mu-named-block:end:
 4964     # . restore registers
 4965     59/pop-to-ecx
 4966     # . epilogue
 4967     89/<- %esp 5/r32/ebp
 4968     5d/pop-to-ebp
 4969     c3/return
 4970 
 4971 parse-mu-var-def:  # line: (addr stream byte), vars: (addr stack (handle var)) -> result/eax: (handle stmt)
 4972     # . prologue
 4973     55/push-ebp
 4974     89/<- %ebp 4/r32/esp
 4975     # . save registers
 4976     51/push-ecx
 4977     52/push-edx
 4978     # var word-slice/ecx: slice
 4979     68/push 0/imm32/end
 4980     68/push 0/imm32/start
 4981     89/<- %ecx 4/r32/esp
 4982     # var v/edx: (handle var) = parse-var-with-type(line)
 4983     (next-mu-token *(ebp+8) %ecx)
 4984     (parse-var-with-type %ecx *(ebp+8))  # => eax
 4985     89/<- %edx 0/r32/eax
 4986     #
 4987     (push *(ebp+0xc) %edx)
 4988     # either v has no register and there's no more to this line
 4989     8b/-> *(edx+0x10) 0/r32/eax  # Var-register
 4990     3d/compare-eax-and 0/imm32
 4991     {
 4992       75/jump-if-!= break/disp8
 4993       # TODO: ensure that there's nothing else on this line
 4994       (new-var-def Heap %edx)  # => eax
 4995       eb/jump $parse-mu-var-def:end/disp8
 4996     }
 4997     # or v has a register and there's more to this line
 4998     {
 4999       74/jump-if-= break/disp8
 5000       # ensure that the next word is '<-'
 5001       (next-mu-token *(ebp+8) %ecx)
 5002       (slice-equal? %ecx "<-")  # => eax
 5003       3d/compare-eax-and 0/imm32/false
 5004       74/jump-if-= $parse-mu-var-def:abort/disp8
 5005       #
 5006       (new-reg-var-def Heap %edx)  # => eax
 5007       (add-operation-and-inputs-to-stmt %eax *(ebp+8) *(ebp+0xc))
 5008     }
 5009 $parse-mu-var-def:end:
 5010     # . reclaim locals
 5011     81 0/subop/add %esp 8/imm32
 5012     # . restore registers
 5013     5a/pop-to-edx
 5014     59/pop-to-ecx
 5015     # . epilogue
 5016     89/<- %esp 5/r32/ebp
 5017     5d/pop-to-ebp
 5018     c3/return
 5019 
 5020 $parse-mu-var-def:abort:
 5021     (rewind-stream *(ebp+8))
 5022     # error("register variable requires a valid instruction to initialize but got '" line "'\n")
 5023     (write-buffered Stderr "register variable requires a valid instruction to initialize but got '")
 5024     (flush Stderr)
 5025     (write-stream 2 *(ebp+8))
 5026     (write-buffered Stderr "'\n")
 5027     (flush Stderr)
 5028     # . syscall(exit, 1)
 5029     bb/copy-to-ebx  1/imm32
 5030     b8/copy-to-eax  1/imm32/exit
 5031     cd/syscall  0x80/imm8
 5032     # never gets here
 5033 
 5034 test-parse-mu-var-def:
 5035     # 'var n: int'
 5036     # . prologue
 5037     55/push-ebp
 5038     89/<- %ebp 4/r32/esp
 5039     # setup
 5040     (clear-stream _test-input-stream)
 5041     (write _test-input-stream "n: int\n")  # caller has consumed the 'var'
 5042     # var vars/ecx: (stack (addr var) 4)
 5043     81 5/subop/subtract %esp 0x10/imm32
 5044     68/push 0x10/imm32/size
 5045     68/push 0/imm32/top
 5046     89/<- %ecx 4/r32/esp
 5047     (clear-stack %ecx)
 5048     # convert
 5049     (parse-mu-var-def _test-input-stream %ecx)  # => eax
 5050     # check result
 5051     (check-ints-equal *eax 2 "F - test-parse-mu-var-def/tag")  # Stmt-tag is var-def
 5052     8b/-> *(eax+4) 0/r32/eax  # Vardef-var
 5053     (check-strings-equal *eax "n" "F - test-parse-mu-var-def/var-name")  # Var-name
 5054     (check-ints-equal *(eax+0x10) 0 "F - test-parse-mu-var-def/var-register")  # Var-register
 5055     # ensure type is int
 5056     8b/-> *(eax+4) 0/r32/eax  # Var-type
 5057     (check-ints-equal *eax 1 "F - test-parse-mu-var-def/var-type:0")  # Tree-is-atom
 5058     (check-ints-equal *(eax+4) 1 "F - test-parse-mu-var-def/var-type:1")  # Tree-value
 5059     (check-ints-equal *(eax+8) 0 "F - test-parse-mu-var-def/var-type:2")  # Tree-right
 5060     # . epilogue
 5061     89/<- %esp 5/r32/ebp
 5062     5d/pop-to-ebp
 5063     c3/return
 5064 
 5065 test-parse-mu-reg-var-def:
 5066     # 'var n/eax: int <- copy 0'
 5067     # . prologue
 5068     55/push-ebp
 5069     89/<- %ebp 4/r32/esp
 5070     # setup
 5071     (clear-stream _test-input-stream)
 5072     (write _test-input-stream "n/eax: int <- copy 0\n")  # caller has consumed the 'var'
 5073     # var vars/ecx: (stack (addr var) 4)
 5074     81 5/subop/subtract %esp 0x10/imm32
 5075     68/push 0x10/imm32/size
 5076     68/push 0/imm32/top
 5077     89/<- %ecx 4/r32/esp
 5078     (clear-stack %ecx)
 5079     # convert
 5080     (parse-mu-var-def _test-input-stream %ecx)  # => eax
 5081     # check result
 5082     (check-ints-equal *eax 3 "F - test-parse-mu-reg-var-def/tag")  # Stmt-tag is reg-var-def
 5083     8b/-> *(eax+0xc) 0/r32/eax  # Regvardef-outputs
 5084     (check-ints-equal *(eax+4) 0 "F - test-parse-mu-reg-var-def/single-output")  # List-next
 5085     8b/-> *eax 0/r32/eax  # Stmt-var-value
 5086     (check-strings-equal *eax "n" "F - test-parse-mu-reg-var-def/output-name")  # Var-name
 5087     (check-strings-equal *(eax+0x10) "eax" "F - test-parse-mu-reg-var-def/output-register")  # Var-register
 5088     # ensure type is int
 5089     8b/-> *(eax+4) 0/r32/eax  # Var-type
 5090     (check-ints-equal *eax 1 "F - test-parse-mu-reg-var-def/var-type:0")  # Tree-is-atom
 5091     (check-ints-equal *(eax+4) 1 "F - test-parse-mu-reg-var-def/output-type:0")  # Tree-value
 5092     (check-ints-equal *(eax+8) 0 "F - test-parse-mu-reg-var-def/output-type:0")  # Tree-right
 5093     # . epilogue
 5094     89/<- %esp 5/r32/ebp
 5095     5d/pop-to-ebp
 5096     c3/return
 5097 
 5098 parse-mu-stmt:  # line: (addr stream byte), vars: (addr stack (handle var)), fn: (handle function) -> result/eax: (handle stmt)
 5099     # pseudocode:
 5100     #   var name: slice
 5101     #   result = allocate(Heap, Stmt-size)
 5102     #   if stmt-has-outputs?(line)
 5103     #     while true
 5104     #       name = next-mu-token(line)
 5105     #       if (name == '<-') break
 5106     #       assert(is-identifier?(name))
 5107     #       var v: (handle var) = lookup-or-define-var(name, vars, fn)  # regular stmts may define vars in fn outputs
 5108     #       result->outputs = append(result->outputs, v)
 5109     #   add-operation-and-inputs-to-stmt(result, line, vars)
 5110     #
 5111     # . prologue
 5112     55/push-ebp
 5113     89/<- %ebp 4/r32/esp
 5114     # . save registers
 5115     51/push-ecx
 5116     52/push-edx
 5117     57/push-edi
 5118     # var name/ecx: slice
 5119     68/push 0/imm32/end
 5120     68/push 0/imm32/start
 5121     89/<- %ecx 4/r32/esp
 5122     # var is-deref?/edx: boolean = false
 5123     ba/copy-to-edx 0/imm32/false
 5124     # result/edi: (handle stmt)
 5125     (allocate Heap *Stmt-size)  # => eax
 5126     89/<- %edi 0/r32/eax
 5127     # result->tag = 1/stmt
 5128     c7 0/subop/copy *edi 1/imm32/stmt1  # Stmt-tag
 5129     {
 5130       (stmt-has-outputs? *(ebp+8))
 5131       3d/compare-eax-and 0/imm32/false
 5132       0f 84/jump-if-= break/disp32
 5133       {
 5134 $parse-mu-stmt:read-outputs:
 5135         # name = next-mu-token(line)
 5136         (next-mu-token *(ebp+8) %ecx)
 5137         # if slice-empty?(word-slice) break
 5138         (slice-empty? %ecx)  # => eax
 5139         3d/compare-eax-and 0/imm32/false
 5140         0f 85/jump-if-!= break/disp32
 5141         # if (name == "<-") break
 5142         (slice-equal? %ecx "<-")  # => eax
 5143         3d/compare-eax-and 0/imm32/false
 5144         0f 85/jump-if-!= break/disp32
 5145         # is-deref? = false
 5146         ba/copy-to-edx 0/imm32/false
 5147         # if (slice-starts-with?(name, '*')) ++name->start and set is-deref?
 5148         8b/-> *ecx 0/r32/eax  # Slice-start
 5149         8a/copy-byte *eax 0/r32/AL
 5150         81 4/subop/and %eax 0xff/imm32
 5151         3d/compare-eax-and 0x2a/imm32/asterisk
 5152         {
 5153           75/jump-if-!= break/disp8
 5154           ff 0/subop/increment *ecx
 5155           ba/copy-to-edx 1/imm32/true
 5156         }
 5157         # assert(is-identifier?(name))
 5158         (is-identifier? %ecx)  # => eax
 5159         3d/compare-eax-and 0/imm32/false
 5160         0f 84/jump-if-= $parse-mu-stmt:abort/disp32
 5161         # result->outputs = new stmt-var(lookup(name, vars, fn), result->outputs, is-deref?)
 5162         (lookup-or-define-var %ecx *(ebp+0xc) *(ebp+0x10))  # => eax
 5163         (append-stmt-var Heap %eax *(edi+0xc) %edx)  # Stmt1-outputs => eax
 5164         89/<- *(edi+0xc) 0/r32/eax  # Stmt1-outputs
 5165         e9/jump loop/disp32
 5166       }
 5167     }
 5168     (add-operation-and-inputs-to-stmt %edi *(ebp+8) *(ebp+0xc))
 5169 $parse-mu-stmt:end:
 5170     # return result
 5171     89/<- %eax 7/r32/edi
 5172     # . reclaim locals
 5173     81 0/subop/add %esp 8/imm32
 5174     # . restore registers
 5175     5f/pop-to-edi
 5176     5a/pop-to-edx
 5177     59/pop-to-ecx
 5178     # . epilogue
 5179     89/<- %esp 5/r32/ebp
 5180     5d/pop-to-ebp
 5181     c3/return
 5182 
 5183 $parse-mu-stmt:abort:
 5184     # error("invalid identifier '" name "'\n")
 5185     (write-buffered Stderr "invalid identifier '")
 5186     (write-slice-buffered Stderr %ecx)
 5187     (write-buffered Stderr "'\n")
 5188     (flush Stderr)
 5189     # . syscall(exit, 1)
 5190     bb/copy-to-ebx  1/imm32
 5191     b8/copy-to-eax  1/imm32/exit
 5192     cd/syscall  0x80/imm8
 5193     # never gets here
 5194 
 5195 add-operation-and-inputs-to-stmt:  # stmt: (handle stmt), line: (addr stream byte), vars: (addr stack (handle var))
 5196     # pseudocode:
 5197     #   stmt->name = slice-to-string(next-mu-token(line))
 5198     #   while true
 5199     #     name = next-mu-token(line)
 5200     #     v = lookup-var-or-literal(name)
 5201     #     stmt->inouts = append(stmt->inouts, v)
 5202     #
 5203     # . prologue
 5204     55/push-ebp
 5205     89/<- %ebp 4/r32/esp
 5206     # . save registers
 5207     50/push-eax
 5208     51/push-ecx
 5209     52/push-edx
 5210     53/push-ebx
 5211     57/push-edi
 5212     # edi = stmt
 5213     8b/-> *(ebp+8) 7/r32/edi
 5214     # var name/ecx: slice
 5215     68/push 0/imm32/end
 5216     68/push 0/imm32/start
 5217     89/<- %ecx 4/r32/esp
 5218     # var is-deref?/edx: boolean = false
 5219     ba/copy-to-edx 0/imm32/false
 5220 $add-operation-and-inputs-to-stmt:read-operation:
 5221     (next-mu-token *(ebp+0xc) %ecx)
 5222     (slice-to-string Heap %ecx)  # => eax
 5223     89/<- *(edi+4) 0/r32/eax  # Stmt1-operation or Regvardef-operation
 5224     # var is-get?/ebx: boolean = (name == "get")
 5225     (slice-equal? %ecx "get")  # => eax
 5226     89/<- %ebx 0/r32/eax
 5227     {
 5228 $add-operation-and-inputs-to-stmt:read-inouts:
 5229       # name = next-mu-token(line)
 5230       (next-mu-token *(ebp+0xc) %ecx)
 5231       # if slice-empty?(word-slice) break
 5232       (slice-empty? %ecx)  # => eax
 5233       3d/compare-eax-and 0/imm32/false
 5234       0f 85/jump-if-!= break/disp32
 5235       # if (name == "<-") abort
 5236       (slice-equal? %ecx "<-")
 5237       3d/compare-eax-and 0/imm32/false
 5238       0f 85/jump-if-!= $add-operation-and-inputs-to-stmt:abort/disp32
 5239       # if (is-get? && second operand) lookup or create offset
 5240       {
 5241         81 7/subop/compare %ebx 0/imm32/false
 5242         74/jump-if-= break/disp8
 5243         81 7/subop/compare *(edi+8) 0/imm32  # Stmt1-inouts or Regvardef-inouts
 5244         74/jump-if-= break/disp8
 5245         (lookup-or-create-constant *(edi+8) %ecx)  # Stmt1-inouts => eax
 5246 #?         (write-buffered Stderr "creating new output var ")
 5247 #?         (print-int32-buffered Stderr %eax)
 5248 #?         (write-buffered Stderr " for field called ")
 5249 #?         (write-slice-buffered Stderr %ecx)
 5250 #?         (write-buffered Stderr Newline)
 5251 #?         (flush Stderr)
 5252         e9/jump $add-operation-and-inputs-to-stmt:save-var/disp32
 5253       }
 5254       # is-deref? = false
 5255       ba/copy-to-edx 0/imm32/false
 5256       # if (slice-starts-with?(name, '*')) ++name->start and set is-deref?
 5257       8b/-> *ecx 0/r32/eax  # Slice-start
 5258       8a/copy-byte *eax 0/r32/AL
 5259       81 4/subop/and %eax 0xff/imm32
 5260       3d/compare-eax-and 0x2a/imm32/asterisk
 5261       {
 5262         75/jump-if-!= break/disp8
 5263 $add-operation-and-inputs-to-stmt:inout-is-deref:
 5264         ff 0/subop/increment *ecx
 5265         ba/copy-to-edx 1/imm32/true
 5266       }
 5267       (lookup-var-or-literal %ecx *(ebp+0x10))  # => eax
 5268 $add-operation-and-inputs-to-stmt:save-var:
 5269       (append-stmt-var Heap %eax *(edi+8) %edx)  # Stmt1-inouts or Regvardef-inouts => eax
 5270       89/<- *(edi+8) 0/r32/eax  # Stmt1-inouts or Regvardef-inouts
 5271       e9/jump loop/disp32
 5272     }
 5273 $add-operation-and-inputs-to-stmt:end:
 5274     # . reclaim locals
 5275     81 0/subop/add %esp 8/imm32
 5276     # . restore registers
 5277     5f/pop-to-edi
 5278     5b/pop-to-ebx
 5279     5a/pop-to-edx
 5280     59/pop-to-ecx
 5281     58/pop-to-eax
 5282     # . epilogue
 5283     89/<- %esp 5/r32/ebp
 5284     5d/pop-to-ebp
 5285     c3/return
 5286 
 5287 $add-operation-and-inputs-to-stmt:abort:
 5288     # error("invalid statement '" line "'\n")
 5289     (rewind-stream *(ebp+8))
 5290     (write-buffered Stderr "invalid identifier '")
 5291     (flush Stderr)
 5292     (write-stream 2 *(ebp+8))
 5293     (write-buffered Stderr "'\n")
 5294     (flush Stderr)
 5295     # . syscall(exit, 1)
 5296     bb/copy-to-ebx  1/imm32
 5297     b8/copy-to-eax  1/imm32/exit
 5298     cd/syscall  0x80/imm8
 5299     # never gets here
 5300 
 5301 stmt-has-outputs?:  # line: (addr stream byte) -> result/eax: boolean
 5302     # . prologue
 5303     55/push-ebp
 5304     89/<- %ebp 4/r32/esp
 5305     # . save registers
 5306     51/push-ecx
 5307     # var word-slice/ecx: slice
 5308     68/push 0/imm32/end
 5309     68/push 0/imm32/start
 5310     89/<- %ecx 4/r32/esp
 5311     # result = false
 5312     b8/copy-to-eax 0/imm32/false
 5313     (rewind-stream *(ebp+8))
 5314     {
 5315       (next-mu-token *(ebp+8) %ecx)
 5316       # if slice-empty?(word-slice) break
 5317       (slice-empty? %ecx)
 5318       3d/compare-eax-and 0/imm32/false
 5319       b8/copy-to-eax 0/imm32/false/result  # restore result (if we're here it's still false)
 5320       0f 85/jump-if-!= break/disp32
 5321       # if slice-starts-with?(word-slice, '#') break
 5322       # . eax = *word-slice->start
 5323       8b/-> *ecx 0/r32/eax
 5324       8a/copy-byte *eax 0/r32/AL
 5325       81 4/subop/and %eax 0xff/imm32
 5326       # . if (eax == '#') break
 5327       3d/compare-eax-and 0x23/imm32/hash
 5328       b8/copy-to-eax 0/imm32/false/result  # restore result (if we're here it's still false)
 5329       0f 84/jump-if-= break/disp32
 5330       # if slice-equal?(word-slice, '<-') return true
 5331       (slice-equal? %ecx "<-")
 5332       3d/compare-eax-and 0/imm32/false
 5333       74/jump-if-= loop/disp8
 5334       b8/copy-to-eax 1/imm32/true
 5335     }
 5336 $stmt-has-outputs:end:
 5337     (rewind-stream *(ebp+8))
 5338     # . reclaim locals
 5339     81 0/subop/add %esp 8/imm32
 5340     # . restore registers
 5341     59/pop-to-ecx
 5342     # . epilogue
 5343     89/<- %esp 5/r32/ebp
 5344     5d/pop-to-ebp
 5345     c3/return
 5346 
 5347 # if 'name' starts with a digit, create a new literal var for it
 5348 # otherwise return first 'name' from the top (back) of 'vars' and abort if not found
 5349 lookup-var-or-literal:  # name: (addr slice), vars: (addr stack (handle var)) -> result/eax: (handle var)
 5350     # . prologue
 5351     55/push-ebp
 5352     89/<- %ebp 4/r32/esp
 5353     # . save registers
 5354     51/push-ecx
 5355     56/push-esi
 5356     # esi = name
 5357     8b/-> *(ebp+8) 6/r32/esi
 5358     # if slice-empty?(name) abort
 5359     (slice-empty? %esi)  # => eax
 5360     3d/compare-eax-and 0/imm32/false
 5361     0f 85/jump-if-!= $lookup-var-or-literal:abort/disp32
 5362     # var c/ecx: byte = *name->start
 5363     8b/-> *esi 1/r32/ecx
 5364     8a/copy-byte *ecx 1/r32/CL
 5365     81 4/subop/and %ecx 0xff/imm32
 5366     # if is-decimal-digit?(c) return new var(name)
 5367     {
 5368       (is-decimal-digit? %ecx)  # => eax
 5369       3d/compare-eax-and 0/imm32/false
 5370       74/jump-if-= break/disp8
 5371       (new-literal-integer Heap %esi)  # => eax
 5372       eb/jump $lookup-var-or-literal:end/disp8
 5373     }
 5374     # else if (c == '"') return new var(name)
 5375     {
 5376       81 7/subop/compare %ecx 0x22/imm32/dquote
 5377       75/jump-if-!= break/disp8
 5378       (new-literal Heap %esi)  # => eax
 5379       eb/jump $lookup-var-or-literal:end/disp8
 5380     }
 5381     # otherwise return lookup-var(name, vars)
 5382     {
 5383       (lookup-var %esi *(ebp+0xc))  # => eax
 5384     }
 5385 $lookup-var-or-literal:end:
 5386     # . restore registers
 5387     5e/pop-to-esi
 5388     59/pop-to-ecx
 5389     # . epilogue
 5390     89/<- %esp 5/r32/ebp
 5391     5d/pop-to-ebp
 5392     c3/return
 5393 
 5394 $lookup-var-or-literal:abort:
 5395     (write-buffered Stderr "empty variable!")
 5396     (flush Stderr)
 5397     # . syscall(exit, 1)
 5398     bb/copy-to-ebx  1/imm32
 5399     b8/copy-to-eax  1/imm32/exit
 5400     cd/syscall  0x80/imm8
 5401     # never gets here
 5402 
 5403 # return first 'name' from the top (back) of 'vars' and abort if not found
 5404 lookup-var:  # name: (addr slice), vars: (addr stack (handle var)) -> result/eax: (handle var)
 5405     # . prologue
 5406     55/push-ebp
 5407     89/<- %ebp 4/r32/esp
 5408     # var target/eax: (handle array byte) = slice-to-string(name)
 5409     (slice-to-string Heap *(ebp+8))  # => eax
 5410     #
 5411     (lookup-var-helper %eax *(ebp+0xc))  # => eax
 5412     # if (result == 0) abort
 5413     3d/compare-eax-and 0/imm32
 5414     74/jump-if-= $lookup-var:abort/disp8
 5415 $lookup-var:end:
 5416     # . epilogue
 5417     89/<- %esp 5/r32/ebp
 5418     5d/pop-to-ebp
 5419     c3/return
 5420 
 5421 $lookup-var:abort:
 5422     (write-buffered Stderr "unknown variable '")
 5423     (write-slice-buffered Stderr *(ebp+8))
 5424     (write-buffered Stderr "'\n")
 5425     (flush Stderr)
 5426     # . syscall(exit, 1)
 5427     bb/copy-to-ebx  1/imm32
 5428     b8/copy-to-eax  1/imm32/exit
 5429     cd/syscall  0x80/imm8
 5430     # never gets here
 5431 
 5432 # return first 'name' from the top (back) of 'vars', and 0/null if not found
 5433 lookup-var-helper:  # name: (addr array byte), vars: (addr stack (handle var)) -> result/eax: (handle var)
 5434     # pseudocode:
 5435     #   var curr: (addr handle var) = &vars->data[vars->top - 4]
 5436     #   var min = vars->data
 5437     #   while curr >= min
 5438     #     var v: (handle var) = *curr
 5439     #     if v->name == name
 5440     #       return v
 5441     #   return 0
 5442     #
 5443     # . prologue
 5444     55/push-ebp
 5445     89/<- %ebp 4/r32/esp
 5446     # . save registers
 5447     52/push-edx
 5448     53/push-ebx
 5449     56/push-esi
 5450     # esi = vars
 5451     8b/-> *(ebp+0xc) 6/r32/esi
 5452     # ebx = vars->top
 5453     8b/-> *esi 3/r32/ebx
 5454     # if (vars->top > vars->size) abort
 5455     3b/compare<- *(esi+4) 0/r32/eax
 5456     0f 8f/jump-if-> $lookup-var-helper:error1/disp32
 5457     # var min/edx: (addr handle var) = vars->data
 5458     8d/copy-address *(esi+8) 2/r32/edx
 5459     # var curr/ebx: (addr handle var) = &vars->data[vars->top - 4]
 5460     81 5/subop/subtract %ebx 4/imm32
 5461     8d/copy-address *(esi+ebx+8) 3/r32/ebx
 5462     {
 5463       # if (curr < min) return 0
 5464       39/compare %ebx 2/r32/edx
 5465       b8/copy-to-eax 0/imm32
 5466       0f 82/jump-if-addr< break/disp32
 5467       # var v/eax: (handle var) = *curr
 5468       8b/-> *ebx 0/r32/eax
 5469       # if (v->name == name) return v
 5470       (string-equal? *eax *(ebp+8))  # Var-name
 5471       3d/compare-eax-and 0/imm32/false
 5472       8b/-> *ebx 0/r32/eax
 5473       75/jump-if-!= break/disp8
 5474       # curr -= 4
 5475       81 5/subop/subtract %ebx 4/imm32
 5476       e9/jump loop/disp32
 5477     }
 5478 $lookup-var-helper:end:
 5479     # . restore registers
 5480     5e/pop-to-esi
 5481     5b/pop-to-ebx
 5482     5a/pop-to-edx
 5483     # . epilogue
 5484     89/<- %esp 5/r32/ebp
 5485     5d/pop-to-ebp
 5486     c3/return
 5487 
 5488 $lookup-var-helper:error1:
 5489     (write-buffered Stderr "malformed stack when looking up '")
 5490     (write-slice-buffered Stderr *(ebp+8))
 5491     (write-buffered Stderr "'\n")
 5492     (flush Stderr)
 5493     # . syscall(exit, 1)
 5494     bb/copy-to-ebx  1/imm32
 5495     b8/copy-to-eax  1/imm32/exit
 5496     cd/syscall  0x80/imm8
 5497     # never gets here
 5498 
 5499 # return first 'name' from the top (back) of 'vars' and create a new var for a fn output if not found
 5500 lookup-or-define-var:  # name: (addr slice), vars: (addr stack (handle var)), fn: (handle function) -> result/eax: (handle var)
 5501     # . prologue
 5502     55/push-ebp
 5503     89/<- %ebp 4/r32/esp
 5504     # . save registers
 5505     51/push-ecx
 5506     # var target/ecx: (handle array byte) = slice-to-string(name)
 5507     (slice-to-string Heap *(ebp+8))  # => eax
 5508     89/<- %ecx 0/r32/eax
 5509     #
 5510     (lookup-var-helper %ecx *(ebp+0xc))  # => eax
 5511     {
 5512       # if (result != 0) return
 5513       3d/compare-eax-and 0/imm32
 5514       75/jump-if-!= break/disp8
 5515       # if name is one of fn's outputs, return it
 5516       {
 5517         (find-in-function-outputs *(ebp+0x10) %ecx)  # => eax
 5518         3d/compare-eax-and 0/imm32
 5519         # otherwise abort
 5520         0f 84/jump-if-!= $lookup-var:abort/disp32
 5521       }
 5522     }
 5523 $lookup-or-define-var:end:
 5524     # . restore registers
 5525     59/pop-to-ecx
 5526     # . epilogue
 5527     89/<- %esp 5/r32/ebp
 5528     5d/pop-to-ebp
 5529     c3/return
 5530 
 5531 find-in-function-outputs:  # fn: (handle function), name: (handle array byte) -> result/eax: (handle var)
 5532     # . prologue
 5533     55/push-ebp
 5534     89/<- %ebp 4/r32/esp
 5535     # . save registers
 5536     51/push-ecx
 5537     # var curr/ecx: (handle list var) = fn->outputs
 5538     8b/-> *(ebp+8) 1/r32/ecx
 5539     8b/-> *(ecx+0xc) 1/r32/ecx
 5540     # while curr != null
 5541     {
 5542       81 7/subop/compare %ecx 0/imm32
 5543       74/jump-if-= break/disp8
 5544       # var v: (handle var) = *curr
 5545       8b/-> *ecx 0/r32/eax  # List-value
 5546       # if (curr->name == name) return curr
 5547       50/push-eax
 5548       (string-equal? *eax *(ebp+0xc))
 5549       3d/compare-eax-and 0/imm32/false
 5550       58/pop-to-eax
 5551       75/jump-if-!= $find-in-function-outputs:end/disp8
 5552       # curr = curr->next
 5553       8b/-> *(ecx+4) 1/r32/ecx  # List-next
 5554       eb/jump loop/disp8
 5555     }
 5556     b8/copy-to-eax 0/imm32
 5557 $find-in-function-outputs:end:
 5558     # . restore registers
 5559     59/pop-to-ecx
 5560     # . epilogue
 5561     89/<- %esp 5/r32/ebp
 5562     5d/pop-to-ebp
 5563     c3/return
 5564 
 5565 test-parse-mu-stmt:
 5566     # . prologue
 5567     55/push-ebp
 5568     89/<- %ebp 4/r32/esp
 5569     # setup
 5570     (clear-stream _test-input-stream)
 5571     (write _test-input-stream "increment n\n")
 5572     # var vars/ecx: (stack (addr var) 4)
 5573     81 5/subop/subtract %esp 0x10/imm32
 5574     68/push 0x10/imm32/size
 5575     68/push 0/imm32/top
 5576     89/<- %ecx 4/r32/esp
 5577     (clear-stack %ecx)
 5578     # var v/edx: var
 5579     81 5/subop/subtract %esp 0x14/imm32  # Var-size
 5580     89/<- %edx 4/r32/esp
 5581     (zero-out %edx 0x14)  # Var-size
 5582     # v->name = "n"
 5583     c7 0/subop/copy *edx "n"/imm32  # Var-name
 5584     #
 5585     (push %ecx %edx)
 5586     # convert
 5587     (parse-mu-stmt _test-input-stream %ecx)  # => eax
 5588     # check result
 5589     (check-ints-equal *eax 1 "F - test-parse-mu-stmt/tag")  # Stmt-tag is Stmt1
 5590     (check-strings-equal *(eax+4) "increment" "F - test-parse-mu-stmt/name")  # Stmt1-operation
 5591     # edx: (handle list var) = result->inouts
 5592     8b/-> *(eax+8) 2/r32/edx  # Stmt1-inouts
 5593     # ebx: (handle var) = result->inouts->value
 5594     8b/-> *edx 3/r32/ebx  # Stmt-var-value
 5595     (check-strings-equal *ebx "n" "F - test-parse-mu-stmt/inout:0")  # Var-name
 5596     # . epilogue
 5597     89/<- %esp 5/r32/ebp
 5598     5d/pop-to-ebp
 5599     c3/return
 5600 
 5601 test-parse-mu-stmt-with-comma:
 5602     # . prologue
 5603     55/push-ebp
 5604     89/<- %ebp 4/r32/esp
 5605     # setup
 5606     (clear-stream _test-input-stream)
 5607     (write _test-input-stream "copy-to n, 3\n")
 5608     # var vars/ecx: (stack (addr var) 4)
 5609     81 5/subop/subtract %esp 0x10/imm32
 5610     68/push 0x10/imm32/size
 5611     68/push 0/imm32/top
 5612     89/<- %ecx 4/r32/esp
 5613     (clear-stack %ecx)
 5614     # var v/edx: var
 5615     81 5/subop/subtract %esp 0x14/imm32  # Var-size
 5616     89/<- %edx 4/r32/esp
 5617     (zero-out %edx 0x14)  # Var-size
 5618     # v->name = "n"
 5619     c7 0/subop/copy *edx "n"/imm32  # Var-name
 5620     #
 5621     (push %ecx %edx)
 5622     # convert
 5623     (parse-mu-stmt _test-input-stream %ecx)  # => eax
 5624     # check result
 5625     (check-ints-equal *eax 1 "F - test-parse-mu-stmt-with-comma/tag")  # Stmt-tag is Stmt1
 5626     (check-strings-equal *(eax+4) "copy-to" "F - test-parse-mu-stmt-with-comma/name")  # Stmt1-operation
 5627     # edx: (handle list var) = result->inouts
 5628     8b/-> *(eax+8) 2/r32/edx  # Stmt1-inouts
 5629     # ebx: (handle var) = result->inouts->value
 5630     8b/-> *edx 3/r32/ebx  # Stmt-var-value
 5631     (check-strings-equal *ebx "n" "F - test-parse-mu-stmt-with-comma/inout:0")  # Var-name
 5632     # . epilogue
 5633     89/<- %esp 5/r32/ebp
 5634     5d/pop-to-ebp
 5635     c3/return
 5636 
 5637 new-var:  # ad: (addr allocation-descriptor), name: (addr array byte) -> result/eax: (handle var)
 5638     # . prologue
 5639     55/push-ebp
 5640     89/<- %ebp 4/r32/esp
 5641     # . save registers
 5642     51/push-ecx
 5643     #
 5644     (allocate *(ebp+8) *Var-size)  # => eax
 5645     8b/-> *(ebp+0xc) 1/r32/ecx
 5646     89/<- *eax 1/r32/ecx  # Var-name
 5647 $new-var:end:
 5648     # . restore registers
 5649     59/pop-to-ecx
 5650     # . epilogue
 5651     89/<- %esp 5/r32/ebp
 5652     5d/pop-to-ebp
 5653     c3/return
 5654 
 5655 new-literal-integer:  # ad: (addr allocation-descriptor), name: (addr slice) -> result/eax: (handle var)
 5656     # . prologue
 5657     55/push-ebp
 5658     89/<- %ebp 4/r32/esp
 5659     # . save registers
 5660     51/push-ecx
 5661     # if (!is-hex-int?(name)) abort
 5662     (is-hex-int? *(ebp+0xc))  # => eax
 5663     3d/compare-eax-and 0/imm32/false
 5664     0f 84/jump-if-= $new-literal-integer:abort/disp32
 5665     # var type/ecx: (handle tree type-id) = new type()
 5666     (allocate *(ebp+8) *Tree-size)  # => eax
 5667     # nothing else to do; default type is 'literal'
 5668     c7 0/subop/copy *eax 1/imm32/true  # Tree-is-atom
 5669     89/<- %ecx 0/r32/eax
 5670     # result = new var(s)
 5671     (new-var-from-slice *(ebp+8) *(ebp+0xc))  # => eax
 5672     89/<- *(eax+4) 1/r32/ecx
 5673 $new-literal-integer:end:
 5674     # . restore registers
 5675     59/pop-to-ecx
 5676     # . epilogue
 5677     89/<- %esp 5/r32/ebp
 5678     5d/pop-to-ebp
 5679     c3/return
 5680 
 5681 $new-literal-integer:abort:
 5682     (write-buffered Stderr "variable cannot begin with a digit '")
 5683     (write-slice-buffered Stderr *(ebp+0xc))
 5684     (write-buffered Stderr "'\n")
 5685     (flush Stderr)
 5686     # . syscall(exit, 1)
 5687     bb/copy-to-ebx  1/imm32
 5688     b8/copy-to-eax  1/imm32/exit
 5689     cd/syscall  0x80/imm8
 5690     # never gets here
 5691 
 5692 new-literal:  # ad: (addr allocation-descriptor), name: (addr slice) -> result/eax: (handle var)
 5693     # . prologue
 5694     55/push-ebp
 5695     89/<- %ebp 4/r32/esp
 5696     # . save registers
 5697     51/push-ecx
 5698     52/push-edx
 5699     # var s/ecx: (addr array byte)
 5700     (slice-to-string Heap *(ebp+0xc))  # => eax
 5701     89/<- %ecx 0/r32/eax
 5702     # type = new type()
 5703     (allocate *(ebp+8) *Tree-size)  # => eax
 5704     # nothing else to do; default type is 'literal'
 5705     c7 0/subop/copy *eax 1/imm32/true  # Tree-is-atom
 5706     89/<- %edx 0/r32/eax
 5707     # eax = result
 5708     (new-var *(ebp+8) %ecx)  # => eax
 5709     # result->type = type
 5710     89/<- *(eax+4) 2/r32/edx  # Var-type
 5711 $new-literal:end:
 5712     # . restore registers
 5713     5a/pop-to-edx
 5714     59/pop-to-ecx
 5715     # . epilogue
 5716     89/<- %esp 5/r32/ebp
 5717     5d/pop-to-ebp
 5718     c3/return
 5719 
 5720 new-var-from-slice:  # ad: (addr allocation-descriptor), name: (addr slice) -> result/eax: (handle var)
 5721     # . prologue
 5722     55/push-ebp
 5723     89/<- %ebp 4/r32/esp
 5724     # . save registers
 5725     51/push-ecx
 5726     # result = new-var(slice-to-string(name))
 5727     (slice-to-string Heap *(ebp+0xc))  # => eax
 5728     (new-var *(ebp+8) %eax)
 5729 $new-var-from-slice:end:
 5730     # . restore registers
 5731     59/pop-to-ecx
 5732     # . epilogue
 5733     89/<- %esp 5/r32/ebp
 5734     5d/pop-to-ebp
 5735     c3/return
 5736 
 5737 new-block:  # ad: (addr allocation-descriptor), data: (handle list stmt) -> result/eax: (handle stmt)
 5738     # . prologue
 5739     55/push-ebp
 5740     89/<- %ebp 4/r32/esp
 5741     # . save registers
 5742     51/push-ecx
 5743     #
 5744     (allocate *(ebp+8) *Stmt-size)  # => eax
 5745     c7 0/subop/copy *eax 0/imm32/tag/block  # Stmt-tag
 5746     8b/-> *(ebp+0xc) 1/r32/ecx
 5747     89/<- *(eax+4) 1/r32/ecx  # Block-stmts
 5748 $new-block:end:
 5749     # . restore registers
 5750     59/pop-to-ecx
 5751     # . epilogue
 5752     89/<- %esp 5/r32/ebp
 5753     5d/pop-to-ebp
 5754     c3/return
 5755 
 5756 new-var-def:  # ad: (addr allocation-descriptor), var: (handle var) -> result/eax: (handle stmt)
 5757     # . prologue
 5758     55/push-ebp
 5759     89/<- %ebp 4/r32/esp
 5760     # . save registers
 5761     51/push-ecx
 5762     #
 5763     (allocate *(ebp+8) *Stmt-size)  # => eax
 5764     c7 0/subop/copy *eax 2/imm32/tag/var-on-stack  # Stmt-tag
 5765     # result->var = var
 5766     8b/-> *(ebp+0xc) 1/r32/ecx
 5767     89/<- *(eax+4) 1/r32/ecx  # Vardef-var
 5768 $new-var-def:end:
 5769     # . restore registers
 5770     59/pop-to-ecx
 5771     # . epilogue
 5772     89/<- %esp 5/r32/ebp
 5773     5d/pop-to-ebp
 5774     c3/return
 5775 
 5776 new-reg-var-def:  # ad: (addr allocation-descriptor), var: (handle var) -> result/eax: (handle stmt)
 5777     # . prologue
 5778     55/push-ebp
 5779     89/<- %ebp 4/r32/esp
 5780     # . save registers
 5781     51/push-ecx
 5782     57/push-edi
 5783     # ecx = var
 5784     8b/-> *(ebp+0xc) 1/r32/ecx
 5785     # edi = result
 5786     (allocate *(ebp+8) *Stmt-size)  # => eax
 5787     89/<- %edi 0/r32/eax
 5788     # set tag
 5789     c7 0/subop/copy *edi 3/imm32/tag/var-in-register  # Stmt-tag
 5790     # set output
 5791     (append-stmt-var Heap %ecx *(edi+0xc) 0)  # Regvardef-outputs => eax
 5792     89/<- *(edi+0xc) 0/r32/eax  # Regvardef-outputs
 5793 $new-reg-var-def:end:
 5794     89/<- %eax 7/r32/edi
 5795     # . restore registers
 5796     5f/pop-to-edi
 5797     59/pop-to-ecx
 5798     # . epilogue
 5799     89/<- %esp 5/r32/ebp
 5800     5d/pop-to-ebp
 5801     c3/return
 5802 
 5803 append-list:  # ad: (addr allocation-descriptor), value: _type, list: (handle list _type) -> result/eax: (handle list _type)
 5804     # . prologue
 5805     55/push-ebp
 5806     89/<- %ebp 4/r32/esp
 5807     # . save registers
 5808     51/push-ecx
 5809     #
 5810     (allocate *(ebp+8) *List-size)  # => eax
 5811     8b/-> *(ebp+0xc) 1/r32/ecx
 5812     89/<- *eax 1/r32/ecx  # List-value
 5813     # if (list == null) return result
 5814     81 7/subop/compare *(ebp+0x10) 0/imm32
 5815     74/jump-if-= $append-list:end/disp8
 5816     # otherwise append
 5817     # var curr/ecx = list
 5818     8b/-> *(ebp+0x10) 1/r32/ecx
 5819     # while (curr->next != null) curr = curr->next
 5820     {
 5821       81 7/subop/compare *(ecx+4) 0/imm32  # List-next
 5822       74/jump-if-= break/disp8
 5823       # curr = curr->next
 5824       8b/-> *(ecx+4) 1/r32/ecx
 5825       eb/jump loop/disp8
 5826     }
 5827     # curr->next = result
 5828     89/<- *(ecx+4) 0/r32/eax
 5829     # return list
 5830     8b/-> *(ebp+0x10) 0/r32/eax
 5831 $append-list:end:
 5832     # . restore registers
 5833     59/pop-to-ecx
 5834     # . epilogue
 5835     89/<- %esp 5/r32/ebp
 5836     5d/pop-to-ebp
 5837     c3/return
 5838 
 5839 append-stmt-var:  # ad: (addr allocation-descriptor), v: (handle var), vars: (handle stmt-var), is-deref?: boolean -> result/eax: (handle stmt-var)
 5840     # . prologue
 5841     55/push-ebp
 5842     89/<- %ebp 4/r32/esp
 5843     # . save registers
 5844     51/push-ecx
 5845     #
 5846     (allocate *(ebp+8) *Stmt-var-size)  # => eax
 5847     8b/-> *(ebp+0xc) 1/r32/ecx
 5848     89/<- *eax 1/r32/ecx  # Stmt-var-value
 5849     8b/-> *(ebp+0x14) 1/r32/ecx
 5850     89/<- *(eax+8) 1/r32/ecx  # Stmt-var-is-deref
 5851     # if (list == null) return result
 5852     81 7/subop/compare *(ebp+0x10) 0/imm32
 5853     74/jump-if-= $append-stmt-var:end/disp8
 5854     # otherwise append
 5855     # var curr/ecx: (handle stmt-var) = vars
 5856     8b/-> *(ebp+0x10) 1/r32/ecx
 5857     # while (curr->next != null) curr = curr->next
 5858     {
 5859       81 7/subop/compare *(ecx+4) 0/imm32  # List-next
 5860       74/jump-if-= break/disp8
 5861       # curr = curr->next
 5862       8b/-> *(ecx+4) 1/r32/ecx
 5863       eb/jump loop/disp8
 5864     }
 5865     # curr->next = result
 5866     89/<- *(ecx+4) 0/r32/eax
 5867     # return vars
 5868     8b/-> *(ebp+0x10) 0/r32/eax
 5869 $append-stmt-var:end:
 5870     # . restore registers
 5871     59/pop-to-ecx
 5872     # . epilogue
 5873     89/<- %esp 5/r32/ebp
 5874     5d/pop-to-ebp
 5875     c3/return
 5876 
 5877 append-to-block:  # ad: (addr allocation-descriptor), block: (handle block), x: (handle stmt)
 5878     # . prologue
 5879     55/push-ebp
 5880     89/<- %ebp 4/r32/esp
 5881     # . save registers
 5882     56/push-esi
 5883     # esi = block
 5884     8b/-> *(ebp+0xc) 6/r32/esi
 5885     (append-list *(ebp+8) *(ebp+0x10) *(esi+4))  # ad, x, Block-stmts
 5886     89/<- *(esi+4) 0/r32/eax  # Block-stmts
 5887 $append-to-block:end:
 5888     # . restore registers
 5889     5e/pop-to-esi
 5890     # . epilogue
 5891     89/<- %esp 5/r32/ebp
 5892     5d/pop-to-ebp
 5893     c3/return
 5894 
 5895 ## Parsing types
 5896 # We need to create metadata on user-defined types, and we need to use this
 5897 # metadata as we parse instructions.
 5898 # However, we also want to allow types to be used before their definitions.
 5899 # This means we can't ever assume any type data structures exist.
 5900 
 5901 lookup-or-create-constant:  # container: (handle stmt-var), field-name: (addr slice) -> result/eax: (handle var)
 5902     # . prologue
 5903     55/push-ebp
 5904     89/<- %ebp 4/r32/esp
 5905     # . save registers
 5906     56/push-esi
 5907     # var container-type/esi: type-id
 5908     (container-type *(ebp+8))  # => eax
 5909 #?     (write-buffered Stderr "lookup-or-create-constant: container type-id: ")
 5910 #?     (print-int32-buffered Stderr %eax)
 5911 #?     (write-buffered Stderr Newline)
 5912 #?     (flush Stderr)
 5913     89/<- %esi 0/r32/eax
 5914     # var typeinfo/eax: (addr typeinfo)
 5915     (find-or-create-typeinfo %esi)  # => eax
 5916 #?     (write-buffered Stderr "lookup-or-create-constant: typeinfo: ")
 5917 #?     (print-int32-buffered Stderr %eax)
 5918 #?     (write-buffered Stderr Newline)
 5919 #?     (flush Stderr)
 5920     # result = find-or-create-typeinfo-output-var(typeinfo, field-name)
 5921     (find-or-create-typeinfo-output-var %eax *(ebp+0xc))  # => eax
 5922 $lookup-or-create-constant:end:
 5923     # . restore registers
 5924     5e/pop-to-esi
 5925     # . epilogue
 5926     89/<- %esp 5/r32/ebp
 5927     5d/pop-to-ebp
 5928     c3/return
 5929 
 5930 # if addr var:
 5931 #   container->var->type->right->left->value
 5932 # otherwise
 5933 #   container->var->type->value
 5934 container-type:  # container: (handle stmt-var) -> result/eax: type-id
 5935     # . prologue
 5936     55/push-ebp
 5937     89/<- %ebp 4/r32/esp
 5938     #
 5939     8b/-> *(ebp+8) 0/r32/eax
 5940     8b/-> *eax 0/r32/eax  # Stmt-var-value
 5941     8b/-> *(eax+4) 0/r32/eax  # Var-type
 5942     {
 5943       81 7/subop/compare *(eax+8) 0/imm32  # Tree-right
 5944       74/jump-if-= break/disp8
 5945       8b/-> *(eax+8) 0/r32/eax  # Tree-right
 5946       8b/-> *(eax+4) 0/r32/eax  # Tree-left
 5947     }
 5948     8b/-> *(eax+4) 0/r32/eax  # Tree-value
 5949 $container-type:end:
 5950     # . epilogue
 5951     89/<- %esp 5/r32/ebp
 5952     5d/pop-to-ebp
 5953     c3/return
 5954 
 5955 find-or-create-typeinfo:  # t: type-id -> result/eax: (handle typeinfo)
 5956     # . prologue
 5957     55/push-ebp
 5958     89/<- %ebp 4/r32/esp
 5959     # . save registers
 5960     51/push-ecx
 5961     # eax = find-typeinfo(t)
 5962     (find-typeinfo *(ebp+8))  # => eax
 5963     {
 5964       # if (curr != 0) break
 5965       3d/compare-eax-and 0/imm32
 5966       75/jump-if-!= break/disp8
 5967 $find-or-create-typeinfo:create:
 5968       (allocate Heap *Typeinfo-size)  # => eax
 5969       # result->id = t
 5970       8b/-> *(ebp+8) 1/r32/ecx
 5971       89/<- *eax 1/r32/ecx  # Typeinfo-id
 5972       # result->fields = new table
 5973       # . ecx = new table
 5974       50/push-eax
 5975       (new-stream Heap 0x40 *Typeinfo-fields-row-size)  # => eax
 5976       89/<- %ecx 0/r32/eax
 5977       58/pop-to-eax
 5978       # . result->fields = ecx
 5979       89/<- *(eax+4) 1/r32/ecx  # Typeinfo-fields
 5980       # result->next = Program->types
 5981       8b/-> *_Program-types 1/r32/ecx
 5982       89/<- *(eax+0xc) 1/r32/ecx  # Typeinfo-next
 5983       # Program->types = result
 5984       89/<- *_Program-types 0/r32/eax
 5985     }
 5986 $find-or-create-typeinfo:end:
 5987     # . restore registers
 5988     59/pop-to-ecx
 5989     # . epilogue
 5990     89/<- %esp 5/r32/ebp
 5991     5d/pop-to-ebp
 5992     c3/return
 5993 
 5994 find-typeinfo:  # t: type-id -> result/eax: (handle typeinfo)
 5995     # . prologue
 5996     55/push-ebp
 5997     89/<- %ebp 4/r32/esp
 5998     # . save registers
 5999     51/push-ecx
 6000     # ecx = t
 6001     8b/-> *(ebp+8) 1/r32/ecx
 6002     # var curr/eax: (handle typeinfo) = Program->types
 6003     8b/-> *_Program-types 0/r32/eax
 6004     {
 6005       # if (curr == 0) break
 6006       3d/compare-eax-and 0/imm32
 6007       74/jump-if-= break/disp8
 6008       # if (curr->id == t) return curr
 6009       39/compare *eax 1/r32/ecx  # Typeinfo-id
 6010       0f 84/jump-if-= $find-or-create-typeinfo:end/disp32
 6011       # curr = curr->next
 6012       8b/-> *(eax+0xc) 0/r32/eax  # Typeinfo-next
 6013       #
 6014       eb/jump loop/disp8
 6015     }
 6016 $find-typeinfo:end:
 6017     # . restore registers
 6018     59/pop-to-ecx
 6019     # . epilogue
 6020     89/<- %esp 5/r32/ebp
 6021     5d/pop-to-ebp
 6022     c3/return
 6023 
 6024 find-or-create-typeinfo-output-var:  # T: (handle typeinfo), f: (addr slice) -> result/eax: (handle var)
 6025     # . prologue
 6026     55/push-ebp
 6027     89/<- %ebp 4/r32/esp
 6028     # . save registers
 6029     51/push-ecx
 6030     56/push-esi
 6031     # esi = find-or-create-typeinfo-fields(T, f)
 6032     (find-or-create-typeinfo-fields *(ebp+8) *(ebp+0xc))  # => eax
 6033     89/<- %esi 0/r32/eax
 6034     # if output var doesn't exist, create it
 6035     {
 6036       81 7/subop/compare *(esi+8) 0/imm32  # Typeinfo-entry-output-var
 6037       75/jump-if-!= break/disp8
 6038       # var type/eax: (handle tree type-id) = new var("dummy name", constant type, -1 offset)
 6039       (allocate Heap *Tree-size)  # => eax
 6040       c7 0/subop/copy *(eax+4) 6/imm32/constant  # Tree-value
 6041       c7 0/subop/copy *(eax+8) 0/imm32  # Tree-right
 6042       89/<- %ecx 0/r32/eax
 6043       # eax = result
 6044       (new-var Heap "field")  # => eax
 6045       # result->type = type
 6046       89/<- *(eax+4) 1/r32/ecx  # Var-type
 6047       # result->offset isn't filled out yet
 6048       c7 0/subop/copy *(eax+0xc) -1/imm32/uninitialized  # Var-offset
 6049       # save result as output var
 6050       89/<- *(esi+8) 0/r32/eax  # Typeinfo-entry-output-var
 6051     }
 6052     # return the output var
 6053     8b/-> *(esi+8) 0/r32/eax  # Typeinfo-entry-output-var
 6054 $find-or-create-typeinfo-output-var:end:
 6055     # . restore registers
 6056     5e/pop-to-esi
 6057     59/pop-to-ecx
 6058     # . epilogue
 6059     89/<- %esp 5/r32/ebp
 6060     5d/pop-to-ebp
 6061     c3/return
 6062 
 6063 find-or-create-typeinfo-fields:  # T: (handle typeinfo), f: (addr slice) -> result/eax: (handle typeinfo-entry)
 6064     # . prologue
 6065     55/push-ebp
 6066     89/<- %ebp 4/r32/esp
 6067     # . save registers
 6068     56/push-esi
 6069     # esi = T->fields
 6070     8b/-> *(ebp+8) 6/r32/esi
 6071     8b/-> *(esi+4) 6/r32/esi  # Typeinfo-fields
 6072     # esi = get-or-insert(T->fields, f)
 6073     (get-or-insert-slice %esi *(ebp+0xc) *Typeinfo-fields-row-size Heap)  # => eax
 6074     89/<- %esi 0/r32/eax
 6075     # if typeinfo-entry doesn't exist, allocate it
 6076     {
 6077       81 7/subop/compare *esi 0/imm32  # output var
 6078       75/jump-if-!= break/disp8
 6079       (allocate Heap *Typeinfo-entry-size)  # => eax
 6080       89/<- *esi 0/r32/eax
 6081     }
 6082     # eax = T->fields[f]->entry
 6083     8b/-> *esi 0/r32/eax
 6084 $find-or-create-typeinfo-fields:end:
 6085     # . restore registers
 6086     5e/pop-to-esi
 6087     # . epilogue
 6088     89/<- %esp 5/r32/ebp
 6089     5d/pop-to-ebp
 6090     c3/return
 6091 
 6092 populate-mu-type:  # in: (addr stream byte), t: (handle typeinfo)
 6093     # pseudocode:
 6094     #   var line: (stream byte 512)
 6095     #   curr-index = 0
 6096     #   while true
 6097     #     clear-stream(line)
 6098     #     read-line-buffered(in, line)
 6099     #     if line->write == 0
 6100     #       abort
 6101     #     word-slice = next-mu-token(line)
 6102     #     if slice-empty?(word-slice)               # end of line
 6103     #       continue
 6104     #     if slice-equal?(word-slice, "}")
 6105     #       break
 6106     #     var v: (handle var) = parse-var-with-type(word-slice, line)
 6107     #     var r: (handle typeinfo-fields) = find-or-create-typeinfo-fields(t, word-slice/v->name)
 6108     #     TODO: ensure that r->first is null
 6109     #     r->index = curr-index
 6110     #     curr-index++
 6111     #     r->input-var = v
 6112     #     if r->output-var == 0
 6113     #       r->output-var = new literal
 6114     #     TODO: ensure nothing else in line
 6115     # t->total-size-in-bytes = -2 (not yet initialized)
 6116     #
 6117     # . prologue
 6118     55/push-ebp
 6119     89/<- %ebp 4/r32/esp
 6120     # var curr-index: int at *(ebp-4)
 6121     68/push 0/imm32
 6122     # . save registers
 6123     50/push-eax
 6124     51/push-ecx
 6125     52/push-edx
 6126     53/push-ebx
 6127     56/push-esi
 6128     57/push-edi
 6129     # edi = t
 6130     8b/-> *(ebp+0xc) 7/r32/edi
 6131     # var line/ecx: (stream byte 512)
 6132     81 5/subop/subtract %esp 0x200/imm32
 6133     68/push 0x200/imm32/size
 6134     68/push 0/imm32/read
 6135     68/push 0/imm32/write
 6136     89/<- %ecx 4/r32/esp
 6137     # var word-slice/edx: slice
 6138     68/push 0/imm32/end
 6139     68/push 0/imm32/start
 6140     89/<- %edx 4/r32/esp
 6141     {
 6142 $populate-mu-type:line-loop:
 6143       (clear-stream %ecx)
 6144       (read-line-buffered *(ebp+8) %ecx)
 6145       # if (line->write == 0) abort
 6146       81 7/subop/compare *ecx 0/imm32
 6147       0f 84/jump-if-= $populate-mu-type:abort/disp32
 6148 +--  6 lines: #?       # dump line ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 6154       (next-mu-token %ecx %edx)
 6155       # if slice-empty?(word-slice) continue
 6156       (slice-empty? %edx)  # => eax
 6157       3d/compare-eax-and 0/imm32
 6158       0f 85/jump-if-!= loop/disp32
 6159       # if slice-equal?(word-slice, "}") break
 6160       (slice-equal? %edx "}")
 6161       3d/compare-eax-and 0/imm32
 6162       0f 85/jump-if-!= break/disp32
 6163 $populate-mu-type:parse-element:
 6164       # var v/esi: (handle var) = parse-var-with-type(word-slice, first-line)
 6165       (parse-var-with-type %edx %ecx)  # => eax
 6166       89/<- %esi 0/r32/eax
 6167 $populate-mu-type:create-typeinfo-fields:
 6168       # var r/ebx: (handle typeinfo-entry)
 6169       (find-or-create-typeinfo-fields %edi %edx)  # => eax
 6170       89/<- %ebx 0/r32/eax
 6171 #?       (write-buffered Stderr "var ")
 6172 #?       (write-buffered Stderr *esi)  # Var-name
 6173 #?       (write-buffered Stderr " is at index ")
 6174 #?       (print-int32-buffered Stderr *(ebp-4))
 6175 #?       (write-buffered Stderr Newline)
 6176 #?       (flush Stderr)
 6177       # r->index = curr-index
 6178       8b/-> *(ebp-4) 0/r32/eax
 6179       89/<- *(ebx+4) 0/r32/eax  # Typeinfo-entry-index
 6180       # ++curr-index
 6181       ff 0/subop/increment *(ebp-4)
 6182 $populate-mu-type:set-input-type:
 6183       # r->input-var = v
 6184       89/<- *ebx 6/r32/esi  # Typeinfo-entry-input-var
 6185       {
 6186 $populate-mu-type:create-output-type:
 6187         # if (r->output-var == 0) create a new var with some placeholder data
 6188         81 7/subop/compare *(ebx+8) 0/imm32  # Typeinfo-entry-output-var
 6189         75/jump-if-!= break/disp8
 6190         (new-literal Heap %edx)  # => eax
 6191         89/<- *(ebx+8) 0/r32/eax  # Typeinfo-entry-output-var
 6192       }
 6193       e9/jump loop/disp32
 6194     }
 6195 $populate-mu-type:invalidate-total-size-in-bytes:
 6196     # Offsets and total size may not be accurate here since we may not yet
 6197     # have encountered the element types.
 6198     # We'll recompute them separately after parsing the entire program.
 6199     c7 0/subop/copy *(edi+8) -2/imm32/uninitialized  # Typeinfo-total-size-in-bytes
 6200 $populate-mu-type:end:
 6201     # . reclaim locals
 6202     81 0/subop/add %esp 0x214/imm32
 6203     # . restore registers
 6204     5f/pop-to-edi
 6205     5e/pop-to-esi
 6206     5b/pop-to-ebx
 6207     5a/pop-to-edx
 6208     59/pop-to-ecx
 6209     58/pop-to-eax
 6210     # reclaim curr-index
 6211     81 0/subop/add %esp 4/imm32
 6212     # . epilogue
 6213     89/<- %esp 5/r32/ebp
 6214     5d/pop-to-ebp
 6215     c3/return
 6216 
 6217 $populate-mu-type:abort:
 6218     # error("unexpected top-level command: " word-slice "\n")
 6219     (write-buffered Stderr "incomplete type definition '")
 6220     (type-name *edi)  # Typeinfo-id => eax
 6221     (write-buffered Stderr %eax)
 6222     (write-buffered Stderr "\n")
 6223     (flush Stderr)
 6224     # . syscall(exit, 1)
 6225     bb/copy-to-ebx  1/imm32
 6226     b8/copy-to-eax  1/imm32/exit
 6227     cd/syscall  0x80/imm8
 6228     # never gets here
 6229 
 6230 type-name:  # index: int -> result/eax: (addr array byte)
 6231     # . prologue
 6232     55/push-ebp
 6233     89/<- %ebp 4/r32/esp
 6234     #
 6235     (index Type-id *(ebp+8))
 6236 $type-name:end:
 6237     # . epilogue
 6238     89/<- %esp 5/r32/ebp
 6239     5d/pop-to-ebp
 6240     c3/return
 6241 
 6242 index:  # arr: (addr stream (handle array byte)), index: int -> result/eax: (addr array byte)
 6243     # . prologue
 6244     55/push-ebp
 6245     89/<- %ebp 4/r32/esp
 6246     # . save registers
 6247     56/push-esi
 6248     # TODO: bounds-check index
 6249     # esi = arr
 6250     8b/-> *(ebp+8) 6/r32/esi
 6251     # eax = index
 6252     8b/-> *(ebp+0xc) 0/r32/eax
 6253     # eax = *(arr + 12 + index)
 6254     8b/-> *(esi+eax+0xc) 0/r32/eax
 6255 $index:end:
 6256     # . restore registers
 6257     5e/pop-to-esi
 6258     # . epilogue
 6259     89/<- %esp 5/r32/ebp
 6260     5d/pop-to-ebp
 6261     c3/return
 6262 
 6263 #######################################################
 6264 # Compute type sizes
 6265 #######################################################
 6266 
 6267 # Compute the sizes of all user-defined types.
 6268 # We'll need the sizes of their elements, which may be other user-defined
 6269 # types, which we will compute as needed.
 6270 
 6271 # Initially, all user-defined types have their sizes set to -2 (invalid)
 6272 populate-mu-type-sizes:
 6273     # . prologue
 6274     55/push-ebp
 6275     89/<- %ebp 4/r32/esp
 6276     # . save registers
 6277     51/push-ecx
 6278 $populate-mu-type-sizes:total-sizes:
 6279     # var curr/ecx: (handle typeinfo) = *Program->types
 6280     8b/-> *_Program-types 1/r32/ecx
 6281     {
 6282       # if (curr == null) break
 6283       81 7/subop/compare %ecx 0/imm32
 6284       74/jump-if-= break/disp8
 6285       (populate-mu-type-sizes-in-type %ecx)
 6286       # curr = curr->next
 6287       8b/-> *(ecx+0xc) 1/r32/ecx  # Typeinfo-next
 6288       eb/jump loop/disp8
 6289     }
 6290 $populate-mu-type-sizes:offsets:
 6291     # var curr/ecx: (handle typeinfo) = *Program->types
 6292     8b/-> *_Program-types 1/r32/ecx
 6293     {
 6294       # if (curr == null) break
 6295       81 7/subop/compare %ecx 0/imm32
 6296       74/jump-if-= break/disp8
 6297       (populate-mu-type-offsets %ecx)
 6298       # curr = curr->next
 6299       8b/-> *(ecx+0xc) 1/r32/ecx  # Typeinfo-next
 6300       eb/jump loop/disp8
 6301     }
 6302 $populate-mu-type-sizes:end:
 6303     # . restore registers
 6304     59/pop-to-ecx
 6305     # . epilogue
 6306     89/<- %esp 5/r32/ebp
 6307     5d/pop-to-ebp
 6308     c3/return
 6309 
 6310 # compute sizes of all fields, recursing as necessary
 6311 # sum up all their sizes to arrive at total size
 6312 # fields may be out of order, but that doesn't affect the answer
 6313 populate-mu-type-sizes-in-type:  # T: (handle typeinfo)
 6314     # . prologue
 6315     55/push-ebp
 6316     89/<- %ebp 4/r32/esp
 6317     # . save registers
 6318     50/push-eax
 6319     51/push-ecx
 6320     52/push-edx
 6321     56/push-esi
 6322     57/push-edi
 6323     # esi = T
 6324     8b/-> *(ebp+8) 6/r32/esi
 6325     # if T is already computed, return
 6326     81 7/subop/compare *(esi+8) 0/imm32  # Typeinfo-total-size-in-bytes
 6327     7d/jump-if->= $populate-mu-type-sizes-in-type:end/disp8
 6328     # if T is being computed, abort
 6329     81 7/subop/compare *(esi+8) -1/imm32/being-computed  # Typeinfo-total-size-in-bytes
 6330     74/jump-if-= $populate-mu-type-sizes-in-type:abort/disp8
 6331     # tag T (-2 to -1) to avoid infinite recursion
 6332     c7 0/subop/copy *(esi+8) -1/imm32/being-computed  # Typeinfo-total-size-in-bytes
 6333     # var total-size/edi: int = 0
 6334     bf/copy-to-edi 0/imm32
 6335     # - for every field, if it's a user-defined type, compute its size
 6336     # var table/ecx: (handle table string_key (handle typeinfo-entry)) = T->fields
 6337     8b/-> *(esi+4) 1/r32/ecx  # Typeinfo-fields
 6338     # var table-size/edx: int = table->write
 6339     8b/-> *ecx 2/r32/edx  # stream-write
 6340     # var curr/ecx: (addr table_row) = table->data
 6341     8d/copy-address *(ecx+0xc) 1/r32/ecx
 6342     # var max/edx: (addr table_row) = table->data + table->write
 6343     8d/copy-address *(ecx+edx) 2/r32/edx
 6344     {
 6345 $populate-mu-type-sizes-in-type:loop:
 6346       # if (curr >= max) break
 6347       39/compare %ecx 2/r32/edx
 6348       73/jump-if-addr>= break/disp8
 6349       # var t/eax: (handle typeinfo-entry) = curr->value
 6350       8b/-> *(ecx+4) 0/r32/eax
 6351       # compute size of t
 6352       (compute-size-of-var *eax)  # Typeinfo-entry-input-var => eax
 6353       # result += eax
 6354       01/add-to %edi 0/r32/eax
 6355       # curr += row-size
 6356       81 0/subop/add %ecx 8/imm32
 6357       #
 6358       eb/jump loop/disp8
 6359     }
 6360     # - save result
 6361     89/<- *(esi+8) 7/r32/edi  # Typeinfo-total-size-in-bytes
 6362 $populate-mu-type-sizes-in-type:end:
 6363     # . restore registers
 6364     5f/pop-to-edi
 6365     5e/pop-to-esi
 6366     5a/pop-to-edx
 6367     59/pop-to-ecx
 6368     58/pop-to-eax
 6369     # . epilogue
 6370     89/<- %esp 5/r32/ebp
 6371     5d/pop-to-ebp
 6372     c3/return
 6373 
 6374 $populate-mu-type-sizes-in-type:abort:
 6375     (write-buffered Stderr "cycle in type definitions\n")
 6376     (flush Stderr)
 6377     # . syscall(exit, 1)
 6378     bb/copy-to-ebx  1/imm32
 6379     b8/copy-to-eax  1/imm32/exit
 6380     cd/syscall  0x80/imm8
 6381     # never gets here
 6382 
 6383 # Analogous to size-of, except we need to compute what size-of can just read
 6384 # off the right data structures.
 6385 compute-size-of-var:  # in: (handle var) -> result/eax: int
 6386     # . prologue
 6387     55/push-ebp
 6388     89/<- %ebp 4/r32/esp
 6389     # . push registers
 6390     51/push-ecx
 6391     # var t/ecx: (handle tree type-id) = v->type
 6392     8b/-> *(ebp+8) 1/r32/ecx
 6393     8b/-> *(ecx+4) 1/r32/ecx  # Var-type
 6394     # if (t->left-is-atom == false) t = t->left
 6395     {
 6396       81 7/subop/compare *ecx 0/imm32/false  # Tree-is-atom
 6397       75/jump-if-!= break/disp8
 6398       8b/-> *(ecx+4) 1/r32/ecx  # Tree-left
 6399     }
 6400     (compute-size-of-type-id *(ecx+4))  # Tree-left => eax
 6401 $compute-size-of-var:end:
 6402     # . restore registers
 6403     59/pop-to-ecx
 6404     # . epilogue
 6405     89/<- %esp 5/r32/ebp
 6406     5d/pop-to-ebp
 6407     c3/return
 6408 
 6409 compute-size-of-type-id:  # t: type-id -> result/eax: int
 6410     # . prologue
 6411     55/push-ebp
 6412     89/<- %ebp 4/r32/esp
 6413     #
 6414     8b/-> *(ebp+8) 0/r32/eax
 6415     # if v is a literal, return 0
 6416     3d/compare-eax-and 0/imm32
 6417     74/jump-if-= $compute-size-of-type-id:end/disp8  # eax changes type from type-id to int
 6418     # if v has a user-defined type, compute its size
 6419     # TODO: support non-atom type
 6420     (find-typeinfo %eax)  # => eax
 6421     {
 6422       3d/compare-eax-and 0/imm32
 6423       74/jump-if-= break/disp8
 6424 $compute-size-of-type-id:user-defined:
 6425       (populate-mu-type-sizes %eax)
 6426       8b/-> *(eax+8) 0/r32/eax  # Typeinfo-total-size-in-bytes
 6427       eb/jump $compute-size-of-type-id:end/disp8
 6428     }
 6429     # otherwise return the word size
 6430     b8/copy-to-eax 4/imm32
 6431 $compute-size-of-type-id:end:
 6432     # . epilogue
 6433     89/<- %esp 5/r32/ebp
 6434     5d/pop-to-ebp
 6435     c3/return
 6436 
 6437 # at this point we have total sizes for all user-defined types
 6438 # compute offsets for each element
 6439 # complication: fields may be out of order
 6440 populate-mu-type-offsets:  # in: (handle typeinfo)
 6441     # . prologue
 6442     55/push-ebp
 6443     89/<- %ebp 4/r32/esp
 6444     # . save registers
 6445     50/push-eax
 6446     51/push-ecx
 6447     52/push-edx
 6448     53/push-ebx
 6449     56/push-esi
 6450     57/push-edi
 6451     # var curr-offset/edi: int = 0
 6452     bf/copy-to-edi 0/imm32
 6453     # var table/ecx: (handle table string_key (handle typeinfo-entry)) = T->fields
 6454     8b/-> *(ebp+8) 1/r32/ecx
 6455     8b/-> *(ecx+4) 1/r32/ecx  # Typeinfo-fields
 6456     # var num-elems/edx: int = table->write / 8
 6457     8b/-> *ecx 2/r32/edx  # stream-write
 6458     c1 5/subop/shift-right-logical  %edx 3/imm8
 6459     # var i/ebx: int = 0
 6460     bb/copy-to-ebx 0/imm32
 6461     {
 6462 $populate-mu-type-offsets:loop:
 6463       39/compare %ebx 2/r32/edx
 6464       7d/jump-if->= break/disp8
 6465       # var v/esi: (handle typeinfo-entry)
 6466       (locate-typeinfo-entry-with-index %ecx %ebx)  # => eax
 6467       89/<- %esi 0/r32/eax
 6468       # v->output-var->offset = curr-offset
 6469       8b/-> *(esi+8) 0/r32/eax  # Typeinfo-entry-output-var
 6470       89/<- *(eax+0xc) 7/r32/edi  # Var-offset
 6471       # curr-offset += size-of(v->input-var)
 6472       8b/-> *esi 0/r32/eax  # Typeinfo-entry-input-var
 6473       (size-of %eax)  # => eax
 6474       01/add-to %edi 0/r32/eax
 6475       # ++i
 6476       43/increment-ebx
 6477       eb/jump loop/disp8
 6478     }
 6479 $populate-mu-type-offsets:end:
 6480     # . restore registers
 6481     5f/pop-to-edi
 6482     5e/pop-to-esi
 6483     5b/pop-to-ebx
 6484     5a/pop-to-edx
 6485     59/pop-to-ecx
 6486     58/pop-to-eax
 6487     # . epilogue
 6488     89/<- %esp 5/r32/ebp
 6489     5d/pop-to-ebp
 6490     c3/return
 6491 
 6492 locate-typeinfo-entry-with-index:  # table: (handle table string_key (handle typeinfo-entry)), idx: int -> result/eax: (handle typeinfo-entry)
 6493     # . prologue
 6494     55/push-ebp
 6495     89/<- %ebp 4/r32/esp
 6496     # . save registers
 6497     51/push-ecx
 6498     52/push-edx
 6499     53/push-ebx
 6500     56/push-esi
 6501     57/push-edi
 6502     # esi = table
 6503     8b/-> *(ebp+8) 6/r32/esi
 6504     # var curr/ecx: (addr string_key) = table->data
 6505     8d/copy-address *(esi+0xc) 1/r32/ecx
 6506     # var max/edx: (addr byte) = &table->data[table->write]
 6507     8b/-> *esi 2/r32/edx
 6508     8d/copy-address *(ecx+edx) 2/r32/edx
 6509     {
 6510 $locate-typeinfo-entry-with-index:loop:
 6511       39/compare %ecx 2/r32/edx
 6512       73/jump-if-addr>= $locate-typeinfo-entry-with-index:abort/disp8
 6513       # var v/ebx: (handle typeinfo-entry)
 6514       8b/-> *(ecx+4) 3/r32/ebx
 6515       # if (v->index == idx) return v
 6516       8b/-> *(ebx+4) 0/r32/eax  # Typeinfo-entry-index
 6517       39/compare *(ebp+0xc) 0/r32/eax
 6518       89/<- %eax 3/r32/ebx
 6519       74/jump-if-= break/disp8
 6520       # curr += 8
 6521       81 0/subop/add %ecx 8/imm32
 6522       eb/jump loop/disp8
 6523     }
 6524 $locate-typeinfo-entry-with-index:end:
 6525     # . restore registers
 6526     5f/pop-to-edi
 6527     5e/pop-to-esi
 6528     5b/pop-to-ebx
 6529     5a/pop-to-edx
 6530     59/pop-to-ecx
 6531     # . epilogue
 6532     89/<- %esp 5/r32/ebp
 6533     5d/pop-to-ebp
 6534     c3/return
 6535 
 6536 $locate-typeinfo-entry-with-index:abort:
 6537     (write-buffered Stderr "overflowing typeinfo-entry->index ")
 6538     (print-int32-buffered Stderr %ecx)
 6539     (write-buffered Stderr "\n")
 6540     (flush Stderr)
 6541     # . syscall(exit, 1)
 6542     bb/copy-to-ebx  1/imm32
 6543     b8/copy-to-eax  1/imm32/exit
 6544     cd/syscall  0x80/imm8
 6545     # never gets here
 6546 
 6547 #######################################################
 6548 # Type-checking
 6549 #######################################################
 6550 
 6551 check-mu-types:
 6552     # . prologue
 6553     55/push-ebp
 6554     89/<- %ebp 4/r32/esp
 6555     #
 6556 $check-mu-types:end:
 6557     # . epilogue
 6558     89/<- %esp 5/r32/ebp
 6559     5d/pop-to-ebp
 6560     c3/return
 6561 
 6562 size-of:  # v: (handle var) -> result/eax: int
 6563     # . prologue
 6564     55/push-ebp
 6565     89/<- %ebp 4/r32/esp
 6566     # . save registers
 6567     51/push-ecx
 6568     # var t/ecx: (handle tree type-id) = v->type
 6569     8b/-> *(ebp+8) 1/r32/ecx
 6570     8b/-> *(ecx+4) 1/r32/ecx  # Var-type
 6571     # if is-mu-array?(t) return size-of-array(t)
 6572     {
 6573       (is-mu-array? %ecx)  # => eax
 6574       3d/compare-eax-and 0/imm32/false
 6575       74/jump-if-= break/disp8
 6576       (size-of-array %ecx)  # => eax
 6577       eb/jump $size-of:end/disp8
 6578     }
 6579     # if (t->left-is-atom == false) t = t->left
 6580     {
 6581       81 7/subop/compare *ecx 0/imm32/false  # Tree-is-atom
 6582       75/jump-if-!= break/disp8
 6583       8b/-> *(ecx+4) 1/r32/ecx  # Tree-left
 6584     }
 6585     (size-of-type-id *(ecx+4))  # Tree-left => eax
 6586 $size-of:end:
 6587     # . restore registers
 6588     59/pop-to-ecx
 6589     # . epilogue
 6590     89/<- %esp 5/r32/ebp
 6591     5d/pop-to-ebp
 6592     c3/return
 6593 
 6594 size-of-deref:  # v: (handle var) -> result/eax: int
 6595     # . prologue
 6596     55/push-ebp
 6597     89/<- %ebp 4/r32/esp
 6598     # . save registers
 6599     51/push-ecx
 6600     # var t/ecx: (handle tree type-id) = v->type
 6601     8b/-> *(ebp+8) 1/r32/ecx
 6602     8b/-> *(ecx+4) 1/r32/ecx  # Var-type
 6603     # TODO: assert(t is an addr)
 6604     8b/-> *(ecx+8) 1/r32/ecx  # Tree-right
 6605     # if is-mu-array?(t) return size-of-array(t)
 6606     {
 6607       (is-mu-array? %ecx)  # => eax
 6608       3d/compare-eax-and 0/imm32/false
 6609       74/jump-if-= break/disp8
 6610       (size-of-array %ecx)  # => eax
 6611       eb/jump $size-of:end/disp8
 6612     }
 6613     # if (t->left-is-atom == false) t = t->left
 6614     {
 6615       81 7/subop/compare *ecx 0/imm32/false  # Tree-is-atom
 6616       75/jump-if-!= break/disp8
 6617       8b/-> *(ecx+4) 1/r32/ecx  # Tree-left
 6618     }
 6619     (size-of-type-id *(ecx+4))  # Tree-left => eax
 6620 $size-of-deref:end:
 6621     # . restore registers
 6622     59/pop-to-ecx
 6623     # . epilogue
 6624     89/<- %esp 5/r32/ebp
 6625     5d/pop-to-ebp
 6626     c3/return
 6627 
 6628 is-mu-array?:  # t: (handle tree type-id) -> result/eax: boolean
 6629     # . prologue
 6630     55/push-ebp
 6631     89/<- %ebp 4/r32/esp
 6632     # . save registers
 6633     51/push-ecx
 6634     # ecx = t
 6635     8b/-> *(ebp+8) 1/r32/ecx
 6636     # result = false
 6637     b8/copy-to-eax 0/imm32/false
 6638     # if t->left-is-atom, return false
 6639     81 7/subop/compare *ecx 0/imm32/false  # Tree-is-atom
 6640     75/jump-if-!= $is-mu-array?:end/disp8
 6641     # if !t->left->left-is-atom, return false
 6642     8b/-> *(ecx+4) 1/r32/ecx  # Tree-left
 6643     81 7/subop/compare *ecx 0/imm32/false  # Tree-is-atom
 6644     74/jump-if-= $is-mu-array?:end/disp8
 6645     # return t->left->value == array
 6646     81 7/subop/compare *(ecx+4) 3/imm32/array-type-id  # Tree-value
 6647     0f 94/set-if-= %al
 6648 $is-mu-array?:end:
 6649     # . restore registers
 6650     59/pop-to-ecx
 6651     # . epilogue
 6652     89/<- %esp 5/r32/ebp
 6653     5d/pop-to-ebp
 6654     c3/return
 6655 
 6656 size-of-array:  # a: (handle tree type-id) -> result/eax: int
 6657     # . prologue
 6658     55/push-ebp
 6659     89/<- %ebp 4/r32/esp
 6660     # . save registers
 6661     51/push-ecx
 6662     52/push-edx
 6663     #
 6664     8b/-> *(ebp+8) 1/r32/ecx
 6665     # TODO: assert that a->left is 'array'
 6666     8b/-> *(ecx+8) 1/r32/ecx  # Tree-right
 6667     # var elem-type/edx: type-id = a->right->value
 6668     8b/-> *(ecx+4) 2/r32/edx  # Tree-value
 6669     8b/-> *(edx+4) 2/r32/edx  # Tree-value
 6670     # var array-size/ecx: int = a->right->right->left->value
 6671     8b/-> *(ecx+8) 1/r32/ecx  # Tree-right
 6672     8b/-> *(ecx+4) 1/r32/ecx  # Tree-left
 6673     8b/-> *(ecx+4) 1/r32/ecx  # Tree-value
 6674     # return array-size * size-of(elem-type)
 6675     (size-of-type-id %edx)  # => eax
 6676     f7 4/subop/multiply-into-eax %ecx
 6677     05/add-to-eax 4/imm32  # for array size
 6678 $size-of-array:end:
 6679     # . restore registers
 6680     5a/pop-to-edx
 6681     59/pop-to-ecx
 6682     # . epilogue
 6683     89/<- %esp 5/r32/ebp
 6684     5d/pop-to-ebp
 6685     c3/return
 6686 
 6687 size-of-type-id:  # t: type-id -> result/eax: int
 6688     # . prologue
 6689     55/push-ebp
 6690     89/<- %ebp 4/r32/esp
 6691     #
 6692     8b/-> *(ebp+8) 0/r32/eax
 6693     # if v is a literal, return 0
 6694     3d/compare-eax-and 0/imm32
 6695     74/jump-if-= $size-of-type-id:end/disp8  # eax changes type from type-id to int
 6696     # if v has a user-defined type, return its size
 6697     # TODO: support non-atom type
 6698     (find-typeinfo %eax)  # => eax
 6699     {
 6700       3d/compare-eax-and 0/imm32
 6701       74/jump-if-= break/disp8
 6702 $size-of-type-id:user-defined:
 6703       8b/-> *(eax+8) 0/r32/eax  # Typeinfo-total-size-in-bytes
 6704       eb/jump $size-of-type-id:end/disp8
 6705     }
 6706     # otherwise return the word size
 6707     b8/copy-to-eax 4/imm32
 6708 $size-of-type-id:end:
 6709     # . epilogue
 6710     89/<- %esp 5/r32/ebp
 6711     5d/pop-to-ebp
 6712     c3/return
 6713 
 6714 type-equal?:  # a: (handle tree type-id), b: (handle tree type-id) -> result/eax: boolean
 6715     # . prologue
 6716     55/push-ebp
 6717     89/<- %ebp 4/r32/esp
 6718     # . save registers
 6719     51/push-ecx
 6720     52/push-edx
 6721     # ecx = a
 6722     8b/-> *(ebp+8) 1/r32/ecx
 6723     # edx = b
 6724     8b/-> *(ebp+0xc) 2/r32/edx
 6725     # if (a == b) return true
 6726     8b/-> %ecx 0/r32/eax  # Var-type
 6727     39/compare %edx 0/r32/eax  # Var-type
 6728     b8/copy-to-eax 1/imm32/true
 6729     74/jump-if-= $type-equal?:end/disp8
 6730     # if (a < MAX_TYPE_ID) return false
 6731     81 7/subop/compare %ecx 0x10000/imm32
 6732     b8/copy-to-eax 0/imm32/false
 6733     72/jump-if-addr< $type-equal?:end/disp8
 6734     # if (b < MAX_TYPE_ID) return false
 6735     81 7/subop/compare %edx 0x10000/imm32
 6736     b8/copy-to-eax 0/imm32/false
 6737     72/jump-if-addr< $type-equal?:end/disp8
 6738     # if (!type-equal?(a->left, b->left)) return false
 6739     (type-equal? *(ecx+4) *(edx+4))  # Tree-left, Tree-left => eax
 6740     3d/compare-eax-and 0/imm32/false
 6741     74/jump-if-= $type-equal?:end/disp8
 6742     # return type-equal?(a->right, b->right)
 6743     (type-equal? *(ecx+8) *(edx+8))  # Tree-right, Tree-right => eax
 6744 $type-equal?:end:
 6745     # . restore registers
 6746     5a/pop-to-edx
 6747     59/pop-to-ecx
 6748     # . epilogue
 6749     89/<- %esp 5/r32/ebp
 6750     5d/pop-to-ebp
 6751     c3/return
 6752 
 6753 #######################################################
 6754 # Code-generation
 6755 #######################################################
 6756 
 6757 == data
 6758 
 6759 Curr-block-depth:  # (addr int)
 6760     0/imm32
 6761 Curr-local-stack-offset:  # (addr int)
 6762     0/imm32
 6763 
 6764 == code
 6765 
 6766 emit-subx:  # out: (addr buffered-file)
 6767     # . prologue
 6768     55/push-ebp
 6769     89/<- %ebp 4/r32/esp
 6770     # . save registers
 6771     50/push-eax
 6772     51/push-ecx
 6773     57/push-edi
 6774     # edi = out
 6775     8b/-> *(ebp+8) 7/r32/edi
 6776     # var curr/ecx: (handle function) = *Program->functions
 6777     8b/-> *_Program-functions 1/r32/ecx
 6778     {
 6779       # if (curr == null) break
 6780       81 7/subop/compare %ecx 0/imm32
 6781       0f 84/jump-if-= break/disp32
 6782       (emit-subx-function %edi %ecx)
 6783       # curr = curr->next
 6784       8b/-> *(ecx+0x14) 1/r32/ecx  # Function-next
 6785       e9/jump loop/disp32
 6786     }
 6787 $emit-subx:end:
 6788     # . restore registers
 6789     5f/pop-to-edi
 6790     59/pop-to-ecx
 6791     58/pop-to-eax
 6792     # . epilogue
 6793     89/<- %esp 5/r32/ebp
 6794     5d/pop-to-ebp
 6795     c3/return
 6796 
 6797 emit-subx-function:  # out: (addr buffered-file), f: (handle function)
 6798     # . prologue
 6799     55/push-ebp
 6800     89/<- %ebp 4/r32/esp
 6801     # some preprocessing
 6802     (populate-mu-type-offsets-in-inouts *(ebp+0xc))
 6803     # . save registers
 6804     50/push-eax
 6805     51/push-ecx
 6806     52/push-edx
 6807     57/push-edi
 6808     # edi = out
 6809     8b/-> *(ebp+8) 7/r32/edi
 6810     # ecx = f
 6811     8b/-> *(ebp+0xc) 1/r32/ecx
 6812     # var vars/edx: (stack (addr var) 256)
 6813     81 5/subop/subtract %esp 0x400/imm32
 6814     68/push 0x400/imm32/size
 6815     68/push 0/imm32/top
 6816     89/<- %edx 4/r32/esp
 6817     #
 6818     (write-buffered %edi *ecx)
 6819     (write-buffered %edi ":\n")
 6820     # initialize some global state
 6821     c7 0/subop/copy *Curr-block-depth 1/imm32
 6822     c7 0/subop/copy *Curr-local-stack-offset 0/imm32
 6823     #
 6824     (emit-subx-prologue %edi)
 6825     (emit-subx-block %edi *(ecx+0x10) %edx)  # Function-body
 6826     (emit-subx-epilogue %edi)
 6827     # TODO: validate that *Curr-block-depth and *Curr-local-stack-offset have
 6828     # been cleaned up
 6829 $emit-subx-function:end:
 6830     # . reclaim locals
 6831     81 0/subop/add %esp 408/imm32
 6832     # . restore registers
 6833     5f/pop-to-edi
 6834     5a/pop-to-edx
 6835     59/pop-to-ecx
 6836     58/pop-to-eax
 6837     # . epilogue
 6838     89/<- %esp 5/r32/ebp
 6839     5d/pop-to-ebp
 6840     c3/return
 6841 
 6842 populate-mu-type-offsets-in-inouts:  # f: (handle function)
 6843     # . prologue
 6844     55/push-ebp
 6845     89/<- %ebp 4/r32/esp
 6846     # . save registers
 6847     50/push-eax
 6848     51/push-ecx
 6849     52/push-edx
 6850     53/push-ebx
 6851     57/push-edi
 6852     # var next-offset/edx: int = 8
 6853     ba/copy-to-edx 8/imm32
 6854     # var curr/ecx: (handle list var) = f->inouts
 6855     8b/-> *(ebp+8) 1/r32/ecx
 6856     8b/-> *(ecx+8) 1/r32/ecx  # Function-inouts
 6857     {
 6858 $populate-mu-type-offsets-in-inouts:loop:
 6859       81 7/subop/compare %ecx 0/imm32
 6860       74/jump-if-= break/disp8
 6861       # var v/ebx: (handle var) = curr->value
 6862       8b/-> *ecx 3/r32/ebx  # List-value
 6863       # v->offset = next-offset
 6864       89/<- *(ebx+0xc) 2/r32/edx  # Var-offset
 6865       # next-offset += size-of(v)
 6866       (size-of %ebx)  # => eax
 6867       01/add-to %edx 0/r32/eax
 6868       # curr = curr->next
 6869       8b/-> *(ecx+4) 1/r32/ecx  # List-next
 6870       eb/jump loop/disp8
 6871     }
 6872 $populate-mu-type-offsets-in-inouts:end:
 6873     # . restore registers
 6874     5f/pop-to-edi
 6875     5b/pop-to-ebx
 6876     5a/pop-to-edx
 6877     59/pop-to-ecx
 6878     58/pop-to-eax
 6879     # . epilogue
 6880     89/<- %esp 5/r32/ebp
 6881     5d/pop-to-ebp
 6882     c3/return
 6883 
 6884 emit-subx-stmt-list:  # out: (addr buffered-file), stmts: (handle list stmt), vars: (addr stack (handle var))
 6885     # . prologue
 6886     55/push-ebp
 6887     89/<- %ebp 4/r32/esp
 6888     # . save registers
 6889     50/push-eax
 6890     51/push-ecx
 6891     52/push-edx
 6892     53/push-ebx
 6893     56/push-esi
 6894     # esi = stmts
 6895     8b/-> *(ebp+0xc) 6/r32/esi
 6896     # var var-seen?/edx: boolean <- copy false
 6897     ba/copy-to-edx 0/imm32/false
 6898     #
 6899     {
 6900 $emit-subx-stmt-list:loop:
 6901       81 7/subop/compare %esi 0/imm32
 6902       0f 84/jump-if-= break/disp32
 6903       # var curr-stmt/ecx = stmts->value
 6904       8b/-> *esi 1/r32/ecx  # List-value
 6905       {
 6906 $emit-subx-stmt-list:check-for-block:
 6907         81 7/subop/compare *ecx 0/imm32/block  # Stmt-tag
 6908         75/jump-if-!= break/disp8
 6909 $emit-subx-stmt-list:block:
 6910         (emit-subx-block *(ebp+8) %ecx *(ebp+0x10))
 6911       }
 6912       {
 6913 $emit-subx-stmt-list:check-for-stmt:
 6914         81 7/subop/compare *ecx 1/imm32/stmt1  # Stmt-tag
 6915         0f 85/jump-if-!= break/disp32
 6916 $emit-subx-stmt-list:stmt1:
 6917         {
 6918           (is-mu-branch? %ecx)  # => eax
 6919           3d/compare-eax-and 0/imm32/false
 6920           0f 84/jump-if-= break/disp32
 6921 $emit-subx-stmt-list:branch-stmt:
 6922           # if !var-seen? break
 6923           81 7/subop/compare %edx 0/imm32/false
 6924           0f 84/jump-if-= break/disp32
 6925 $emit-subx-stmt-list:branch-stmt-and-var-seen:
 6926 +-- 26 lines: # unconditional loops --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 6952 +-- 15 lines: # unconditional breaks -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 6967 +-- 37 lines: # simple conditional branches without a target -------------------------------------------------------------------------------------------------------------------------------------------------------
 7004 +-- 19 lines: # conditional branches with an explicit target -------------------------------------------------------------------------------------------------------------------------------------------------------
 7023         }
 7024 $emit-subx-stmt-list:1-to-1:
 7025         (emit-subx-stmt *(ebp+8) %ecx Primitives)
 7026       }
 7027       {
 7028 $emit-subx-stmt-list:check-for-var-def:
 7029         81 7/subop/compare *ecx 2/imm32/var-def  # Stmt-tag
 7030         75/jump-if-!= break/disp8
 7031 $emit-subx-stmt-list:var-def:
 7032         (emit-subx-var-def *(ebp+8) %ecx)
 7033         (push *(ebp+0x10) *(ecx+4))  # Vardef-var
 7034         # var-seen? = true
 7035         ba/copy-to-edx 1/imm32/true
 7036       }
 7037       {
 7038 $emit-subx-stmt-list:check-for-reg-var-def:
 7039         81 7/subop/compare *ecx 3/imm32/reg-var-def  # Stmt-tag
 7040         0f 85/jump-if-!= break/disp32
 7041 $emit-subx-stmt-list:reg-var-def:
 7042         # TODO: ensure that there's exactly one output
 7043         (compute-reg-and-maybe-emit-spill *(ebp+8) %ecx *(ebp+0x10))
 7044         # register variable definition
 7045         (push *(ebp+0x10) %eax)
 7046         # emit the instruction as usual
 7047         (emit-subx-stmt *(ebp+8) %ecx Primitives)
 7048         # var-seen? = true
 7049         ba/copy-to-edx 1/imm32/true
 7050       }
 7051 $emit-subx-stmt-list:continue:
 7052       # TODO: raise an error on unrecognized Stmt-tag
 7053       8b/-> *(esi+4) 6/r32/esi  # List-next
 7054       e9/jump loop/disp32
 7055     }
 7056 $emit-subx-stmt-list:emit-cleanup:
 7057     (emit-cleanup-code-until-depth *(ebp+8) *(ebp+0x10) *Curr-block-depth)
 7058 $emit-subx-stmt-list:cleanup:
 7059     (clean-up-blocks *(ebp+0x10) *Curr-block-depth)
 7060 $emit-subx-stmt-list:end:
 7061     # . restore registers
 7062     5e/pop-to-esi
 7063     5b/pop-to-ebx
 7064     5a/pop-to-edx
 7065     59/pop-to-ecx
 7066     58/pop-to-eax
 7067     # . epilogue
 7068     89/<- %esp 5/r32/ebp
 7069     5d/pop-to-ebp
 7070     c3/return
 7071 
 7072 compute-reg-and-maybe-emit-spill:  # out: (addr buffered-file), stmt: (handle reg-var-def), vars: (addr stack (handle var)) -> result/eax: (handle var)
 7073     # . prologue
 7074     55/push-ebp
 7075     89/<- %ebp 4/r32/esp
 7076     # . save registers
 7077     51/push-ecx
 7078     # ecx = stmt
 7079     8b/-> *(ebp+0xc) 1/r32/ecx
 7080     # var output/ecx: (handle var) = curr-stmt->outputs->value
 7081     8b/-> *(ecx+0xc) 1/r32/ecx  # Regvardef-inouts
 7082     8b/-> *ecx 1/r32/ecx  # List-value
 7083     # v->block-depth = *Curr-block-depth
 7084     8b/-> *Curr-block-depth 0/r32/eax
 7085     89/<- *(ecx+8) 0/r32/eax  # Var-block-depth
 7086     # var reg/eax: (handle array byte) = output->register
 7087     8b/-> *(ecx+0x10) 0/r32/eax  # Var-register
 7088     # ensure that output is in a register
 7089     3d/compare-eax-and 0/imm32
 7090     0f 84/jump-if-= $compute-reg-and-maybe-emit-spill:abort/disp32
 7091     # if already-spilled-this-block?(reg, vars) return
 7092     (already-spilled-this-block? %ecx *(ebp+0x10))  # => eax
 7093     3d/compare-eax-and 0/imm32/false
 7094     75/jump-if-!= $compute-reg-and-maybe-emit-spill:end/disp8
 7095     # TODO: assert(size-of(output) == 4)
 7096     # *Curr-local-stack-offset -= 4
 7097     81 5/subop/subtract *Curr-local-stack-offset 4/imm32
 7098     # emit spill
 7099     (emit-indent *(ebp+8) *Curr-block-depth)
 7100     (write-buffered *(ebp+8) "ff 6/subop/push %")
 7101     (write-buffered *(ebp+8) *(ecx+0x10))  # Var-register
 7102     (write-buffered *(ebp+8) Newline)
 7103 $compute-reg-and-maybe-emit-spill:end:
 7104     # return output
 7105     89/<- %eax 1/r32/ecx
 7106     # . restore registers
 7107     59/pop-to-ecx
 7108     # . epilogue
 7109     89/<- %esp 5/r32/ebp
 7110     5d/pop-to-ebp
 7111     c3/return
 7112 
 7113 $compute-reg-and-maybe-emit-spill:abort:
 7114     # error("var '" var->name "' initialized from an instruction must live in a register\n")
 7115     (write-buffered Stderr "var '")
 7116     (write-buffered Stderr *eax)  # Var-name
 7117     (write-buffered Stderr "' initialized from an instruction must live in a register\n")
 7118     (flush Stderr)
 7119     # . syscall(exit, 1)
 7120     bb/copy-to-ebx  1/imm32
 7121     b8/copy-to-eax  1/imm32/exit
 7122     cd/syscall  0x80/imm8
 7123     # never gets here
 7124 
 7125 emit-subx-cleanup-and-unconditional-nonlocal-branch:  # out: (addr buffered-file), stmt: (addr stmt1), vars: (addr stack (handle var))
 7126     # . prologue
 7127     55/push-ebp
 7128     89/<- %ebp 4/r32/esp
 7129     # . save registers
 7130     50/push-eax
 7131     51/push-ecx
 7132     52/push-edx
 7133     # ecx = stmt
 7134     8b/-> *(ebp+0xc) 1/r32/ecx
 7135     # var target/edx: (addr array byte) = curr-stmt->inouts->value->name
 7136     8b/-> *(ecx+8) 2/r32/edx  # Stmt1-inouts
 7137     8b/-> *edx 2/r32/edx  # Stmt-var-value
 7138     8b/-> *edx 2/r32/edx  # Var-name
 7139     # clean up until target block
 7140     (emit-cleanup-code-until-target *(ebp+8) *(ebp+0x10) %edx)
 7141     # emit jump to target block
 7142     (emit-indent *(ebp+8) *Curr-block-depth)
 7143     (write-buffered *(ebp+8) "e9/jump ")
 7144     (write-buffered *(ebp+8) %edx)
 7145     (string-starts-with? *(ecx+4) "break")
 7146     3d/compare-eax-and 0/imm32/false
 7147     {
 7148       74/jump-if-= break/disp8
 7149       (write-buffered *(ebp+8) ":break/disp32\n")
 7150     }
 7151     3d/compare-eax-and 0/imm32/false  # just in case the function call modified flags
 7152     {
 7153       75/jump-if-!= break/disp8
 7154       (write-buffered *(ebp+8) ":loop/disp32\n")
 7155     }
 7156 $emit-subx-cleanup-and-unconditional-nonlocal-branch:end:
 7157     # . restore registers
 7158     5a/pop-to-edx
 7159     59/pop-to-ecx
 7160     58/pop-to-eax
 7161     # . epilogue
 7162     89/<- %esp 5/r32/ebp
 7163     5d/pop-to-ebp
 7164     c3/return
 7165 
 7166 is-mu-branch?:  # stmt: (addr stmt1) -> result/eax: boolean
 7167     # . prologue
 7168     55/push-ebp
 7169     89/<- %ebp 4/r32/esp
 7170     # . save registers
 7171     51/push-ecx
 7172     # ecx = stmt
 7173     8b/-> *(ebp+8) 1/r32/ecx
 7174     # if (stmt->operation starts with "loop") return true
 7175     (string-starts-with? *(ecx+4) "loop")  # Stmt1-operation => eax
 7176     3d/compare-eax-and 0/imm32/false
 7177     75/jump-if-not-equal $is-mu-branch?:end/disp8
 7178     # otherwise return (stmt->operation starts with "break")
 7179     (string-starts-with? *(ecx+4) "break")  # Stmt1-operation => eax
 7180 $is-mu-branch?:end:
 7181     # . restore registers
 7182     59/pop-to-ecx
 7183     # . epilogue
 7184     89/<- %esp 5/r32/ebp
 7185     5d/pop-to-ebp
 7186     c3/return
 7187 
 7188 emit-reverse-break:  # out: (addr buffered-file), stmt: (addr stmt1)
 7189     # . prologue
 7190     55/push-ebp
 7191     89/<- %ebp 4/r32/esp
 7192     # . save registers
 7193     50/push-eax
 7194     # eax = stmt
 7195     8b/-> *(ebp+0xc) 0/r32/eax
 7196     #
 7197     (get Reverse-branch *(eax+4) 8 "reverse-branch: ")  # Stmt1-operation => eax: (addr addr array byte)
 7198     (emit-indent *(ebp+8) *Curr-block-depth)
 7199     (write-buffered *(ebp+8) *eax)
 7200     (write-buffered *(ebp+8) " break/disp32\n")
 7201 $emit-reverse-break:end:
 7202     # . restore registers
 7203     58/pop-to-eax
 7204     # . epilogue
 7205     89/<- %esp 5/r32/ebp
 7206     5d/pop-to-ebp
 7207     c3/return
 7208 
 7209 == data
 7210 
 7211 Reverse-branch:  # (table string string)
 7212   # a table is a stream
 7213   0xa0/imm32/write
 7214   0/imm32/read
 7215   0xa0/imm32/size
 7216   # data
 7217   "break-if-="/imm32      "0f 85/jump-if-!="/imm32
 7218   "loop-if-="/imm32       "0f 85/jump-if-!="/imm32
 7219   "break-if-!="/imm32     "0f 84/jump-if-="/imm32
 7220   "loop-if-!="/imm32      "0f 84/jump-if-="/imm32
 7221   "break-if-<"/imm32      "0f 8d/jump-if->="/imm32
 7222   "loop-if-<"/imm32       "0f 8d/jump-if->="/imm32
 7223   "break-if->"/imm32      "0f 8e/jump-if-<="/imm32
 7224   "loop-if->"/imm32       "0f 8e/jump-if-<="/imm32
 7225   "break-if-<="/imm32     "0f 87/jump-if->"/imm32
 7226   "loop-if-<="/imm32      "0f 87/jump-if->"/imm32
 7227   "break-if->="/imm32     "0f 8c/jump-if-<"/imm32
 7228   "loop-if->="/imm32      "0f 8c/jump-if-<"/imm32
 7229   "break-if-addr<"/imm32  "0f 83/jump-if-addr>="/imm32
 7230   "loop-if-addr<"/imm32   "0f 83/jump-if-addr>="/imm32
 7231   "break-if-addr>"/imm32  "0f 86/jump-if-addr<="/imm32
 7232   "loop-if-addr>"/imm32   "0f 86/jump-if-addr<="/imm32
 7233   "break-if-addr<="/imm32 "0f 87/jump-if-addr>"/imm32
 7234   "loop-if-addr<="/imm32  "0f 87/jump-if-addr>"/imm32
 7235   "break-if-addr>="/imm32 "0f 82/jump-if-addr<"/imm32
 7236   "loop-if-addr>="/imm32  "0f 82/jump-if-addr<"/imm32
 7237 
 7238 == code
 7239 
 7240 emit-unconditional-jump-to-depth:  # out: (addr buffered-file), vars: (addr stack (handle var)), depth: int, label-suffix: (addr array byte)
 7241     # . prologue
 7242     55/push-ebp
 7243     89/<- %ebp 4/r32/esp
 7244     # . save registers
 7245     50/push-eax
 7246     51/push-ecx
 7247     52/push-edx
 7248     53/push-ebx
 7249     # ecx = vars
 7250     8b/-> *(ebp+0xc) 1/r32/ecx
 7251     # var eax: int = vars->top
 7252     8b/-> *ecx 0/r32/eax
 7253     # var min/ecx: (address (handle var)) = vars->data
 7254     81 0/subop/add %ecx 8/imm32
 7255     # var curr/eax: (address (handle var)) = &vars->data[vars->top - 4]
 7256     81 5/subop/subtract %eax 4/imm32
 7257     8d/copy-address *(ecx+eax) 0/r32/eax
 7258     # edx = depth
 7259     8b/-> *(ebp+0x10) 2/r32/edx
 7260     {
 7261 $emit-unconditional-jump-to-depth:loop:
 7262       # if (curr < min) break
 7263       39/compare %eax 1/r32/ecx
 7264       0f 82/jump-if-addr< break/disp32
 7265       # var v/ebx: (handle var) = *curr
 7266       8b/-> *eax 3/r32/ebx
 7267       # if (v->block-depth < until-block-depth) break
 7268       39/compare *(ebx+8) 2/r32/edx  # Var-block-depth
 7269       0f 8c/jump-if-< break/disp32
 7270       {
 7271 $emit-unconditional-jump-to-depth:check:
 7272         # if v->block-depth != until-block-depth, continue
 7273         39/compare *(ebx+8) 2/r32/edx  # Var-block-depth
 7274         0f 85/jump-if-!= break/disp32
 7275 $emit-unconditional-jump-to-depth:depth-found:
 7276         # if v is not a literal, continue
 7277         # . var eax: int = size-of(v)
 7278         50/push-eax
 7279         (size-of %ebx)  # => eax
 7280         # . if (eax != 0) continue
 7281         3d/compare-eax-and 0/imm32
 7282         58/pop-to-eax
 7283         #
 7284         0f 85/jump-if-!= break/disp32
 7285 $emit-unconditional-jump-to-depth:label-found:
 7286         # emit unconditional jump, then return
 7287         (emit-indent *(ebp+8) *Curr-block-depth)
 7288         (write-buffered *(ebp+8) "e9/jump ")
 7289         (write-buffered *(ebp+8) *ebx)  # Var-name
 7290         (write-buffered *(ebp+8) ":")
 7291         (write-buffered *(ebp+8) *(ebp+0x14))
 7292         (write-buffered *(ebp+8) "/disp32\n")
 7293         eb/jump $emit-unconditional-jump-to-depth:end/disp8
 7294       }
 7295       # curr -= 4
 7296       2d/subtract-from-eax 4/imm32
 7297       e9/jump loop/disp32
 7298     }
 7299     # TODO: error if no label at 'depth' was found
 7300 $emit-unconditional-jump-to-depth:end:
 7301     # . restore registers
 7302     5b/pop-to-ebx
 7303     5a/pop-to-edx
 7304     59/pop-to-ecx
 7305     58/pop-to-eax
 7306     # . epilogue
 7307     89/<- %esp 5/r32/ebp
 7308     5d/pop-to-ebp
 7309     c3/return
 7310 
 7311 # emit clean-up code for 'vars' until some block depth
 7312 # doesn't actually modify 'vars' so we need traverse manually inside the stack
 7313 emit-cleanup-code-until-depth:  # out: (addr buffered-file), vars: (addr stack (handle var)), until-block-depth: int
 7314     # . prologue
 7315     55/push-ebp
 7316     89/<- %ebp 4/r32/esp
 7317     # . save registers
 7318     50/push-eax
 7319     51/push-ecx
 7320     52/push-edx
 7321     53/push-ebx
 7322     # ecx = vars
 7323     8b/-> *(ebp+0xc) 1/r32/ecx
 7324     # var eax: int = vars->top
 7325     8b/-> *ecx 0/r32/eax
 7326     # var min/ecx: (address (handle var)) = vars->data
 7327     81 0/subop/add %ecx 8/imm32
 7328     # var curr/eax: (address (handle var)) = &vars->data[vars->top - 4]
 7329     81 5/subop/subtract %eax 4/imm32
 7330     8d/copy-address *(ecx+eax) 0/r32/eax
 7331     # edx = until-block-depth
 7332     8b/-> *(ebp+0x10) 2/r32/edx
 7333     {
 7334 $emit-cleanup-code-until-depth:loop:
 7335       # if (curr < min) break
 7336       39/compare %eax 1/r32/ecx
 7337       0f 82/jump-if-addr< break/disp32
 7338       # var v/ebx: (handle var) = *curr
 7339       8b/-> *eax 3/r32/ebx
 7340       # if (v->block-depth < until-block-depth) break
 7341       39/compare *(ebx+8) 2/r32/edx  # Var-block-depth
 7342       0f 8c/jump-if-< break/disp32
 7343       # if v is in a register
 7344       81 7/subop/compare *(ebx+0x10) 0/imm32  # Var-register
 7345       {
 7346         0f 84/jump-if-= break/disp32
 7347         50/push-eax
 7348         {
 7349 $emit-cleanup-code-until-depth:check-for-previous-spill:
 7350           (same-register-spilled-before? %ebx *(ebp+0xc) %eax)  # => eax
 7351           3d/compare-eax-and 0/imm32/false
 7352           0f 85/jump-if-!= break/disp32
 7353 $emit-cleanup-code-until-depth:reclaim-var-in-register:
 7354           (emit-indent *(ebp+8) *Curr-block-depth)
 7355           (write-buffered *(ebp+8) "8f 0/subop/pop %")
 7356           (write-buffered *(ebp+8) *(ebx+0x10))
 7357           (write-buffered *(ebp+8) Newline)
 7358         }
 7359         58/pop-to-eax
 7360         eb/jump $emit-cleanup-code-until-depth:continue/disp8
 7361       }
 7362       # otherwise v is on the stack
 7363       {
 7364         75/jump-if-!= break/disp8
 7365 $emit-cleanup-code-until-depth:reclaim-var-on-stack:
 7366         50/push-eax
 7367         (size-of %ebx)  # => eax
 7368         # don't emit code for labels
 7369         3d/compare-eax-and 0/imm32
 7370         74/jump-if-= break/disp8
 7371         #
 7372         (emit-indent *(ebp+8) *Curr-block-depth)
 7373         (write-buffered *(ebp+8) "81 0/subop/add %esp ")
 7374         (print-int32-buffered *(ebp+8) %eax)
 7375         (write-buffered *(ebp+8) "/imm32\n")
 7376         58/pop-to-eax
 7377       }
 7378 $emit-cleanup-code-until-depth:continue:
 7379       # curr -= 4
 7380       2d/subtract-from-eax 4/imm32
 7381       e9/jump loop/disp32
 7382     }
 7383 $emit-cleanup-code-until-depth:end:
 7384     # . restore registers
 7385     5b/pop-to-ebx
 7386     5a/pop-to-edx
 7387     59/pop-to-ecx
 7388     58/pop-to-eax
 7389     # . epilogue
 7390     89/<- %esp 5/r32/ebp
 7391     5d/pop-to-ebp
 7392     c3/return
 7393 
 7394 # emit clean-up code for 'vars' until a given label is encountered
 7395 # doesn't actually modify 'vars' so we need traverse manually inside the stack
 7396 emit-cleanup-code-until-target:  # out: (addr buffered-file), vars: (addr stack (handle var)), until-block-label: (addr array byte)
 7397     # . prologue
 7398     55/push-ebp
 7399     89/<- %ebp 4/r32/esp
 7400     # . save registers
 7401     50/push-eax
 7402     51/push-ecx
 7403     52/push-edx
 7404     53/push-ebx
 7405     # ecx = vars
 7406     8b/-> *(ebp+0xc) 1/r32/ecx
 7407     # var eax: int = vars->top
 7408     8b/-> *ecx 0/r32/eax
 7409     # var min/ecx: (address (handle var)) = vars->data
 7410     81 0/subop/add %ecx 8/imm32
 7411     # var curr/edx: (address (handle var)) = &vars->data[vars->top - 4]
 7412     81 5/subop/subtract %eax 4/imm32
 7413     8d/copy-address *(ecx+eax) 2/r32/edx
 7414     {
 7415 $emit-cleanup-code-until-target:loop:
 7416       # if (curr < min) break
 7417       39/compare %edx 1/r32/ecx
 7418       0f 82/jump-if-addr< break/disp32
 7419       # var v/ebx: (handle var) = *curr
 7420       8b/-> *edx 3/r32/ebx
 7421       # if (v->name == until-block-label) break
 7422       (string-equal? *ebx *(ebp+0x10))  # => eax
 7423       3d/compare-eax-and 0/imm32/false
 7424       0f 85/jump-if-!= break/disp32
 7425       # if v is in a register
 7426       81 7/subop/compare *(ebx+0x10) 0/imm32  # Var-register
 7427       {
 7428         74/jump-if-= break/disp8
 7429         50/push-eax
 7430         {
 7431 $emit-cleanup-code-until-target:check-for-previous-spill:
 7432           (same-register-spilled-before? %ebx *(ebp+0xc) %edx)  # => eax
 7433           3d/compare-eax-and 0/imm32/false
 7434           75/jump-if-!= break/disp8
 7435 $emit-cleanup-code-until-target:reclaim-var-in-register:
 7436           (emit-indent *(ebp+8) *Curr-block-depth)
 7437           (write-buffered *(ebp+8) "8f 0/subop/pop %")
 7438           (write-buffered *(ebp+8) *(ebx+0x10))
 7439           (write-buffered *(ebp+8) Newline)
 7440         }
 7441         58/pop-to-eax
 7442         eb/jump $emit-cleanup-code-until-target:continue/disp8
 7443       }
 7444       # otherwise v is on the stack
 7445       {
 7446         75/jump-if-!= break/disp8
 7447 $emit-cleanup-code-until-target:reclaim-var-on-stack:
 7448         (size-of %ebx)  # => eax
 7449         # don't emit code for labels
 7450         3d/compare-eax-and 0/imm32
 7451         74/jump-if-= break/disp8
 7452         #
 7453         (emit-indent *(ebp+8) *Curr-block-depth)
 7454         (write-buffered *(ebp+8) "81 0/subop/add %esp ")
 7455         (print-int32-buffered *(ebp+8) %eax)
 7456         (write-buffered *(ebp+8) "/imm32\n")
 7457       }
 7458 $emit-cleanup-code-until-target:continue:
 7459       # curr -= 4
 7460       81 5/subop/subtract %edx 4/imm32
 7461       e9/jump loop/disp32
 7462     }
 7463 $emit-cleanup-code-until-target:end:
 7464     # . restore registers
 7465     5b/pop-to-ebx
 7466     5a/pop-to-edx
 7467     59/pop-to-ecx
 7468     58/pop-to-eax
 7469     # . epilogue
 7470     89/<- %esp 5/r32/ebp
 7471     5d/pop-to-ebp
 7472     c3/return
 7473 
 7474 # is there already a var with the same block-depth and register as 'v' on the 'vars' stack?
 7475 # v is guaranteed not to be within vars
 7476 already-spilled-this-block?:  # v: (handle var), vars: (addr stack (handle var)) -> result/eax: boolean
 7477     # . prologue
 7478     55/push-ebp
 7479     89/<- %ebp 4/r32/esp
 7480     # . save registers
 7481     51/push-ecx
 7482     52/push-edx
 7483     53/push-ebx
 7484     56/push-esi
 7485     57/push-edi
 7486     # ecx = vars
 7487     8b/-> *(ebp+0xc) 1/r32/ecx
 7488     # var eax: int = vars->top
 7489     8b/-> *ecx 0/r32/eax
 7490     # var min/ecx: (address (handle var)) = vars->data
 7491     81 0/subop/add %ecx 8/imm32
 7492     # var curr/edx: (address (handle var)) = &vars->data[vars->top - 4]
 7493     81 5/subop/subtract %eax 4/imm32
 7494     8d/copy-address *(ecx+eax) 2/r32/edx
 7495     # var depth/ebx: int = v->block-depth
 7496     8b/-> *(ebp+8) 3/r32/ebx
 7497     8b/-> *(ebx+8) 3/r32/ebx  # Var-block-depth
 7498     # var needle/esi: (handle array byte) = v->register
 7499     8b/-> *(ebp+8) 6/r32/esi
 7500     8b/-> *(esi+0x10) 6/r32/esi  # Var-register
 7501     {
 7502 $already-spilled-this-block?:loop:
 7503       # if (curr < min) break
 7504       39/compare %edx 1/r32/ecx
 7505       0f 82/jump-if-addr< break/disp32
 7506       # var cand/edi: (handle var) = *curr
 7507       8b/-> *edx 7/r32/edi
 7508       # if (cand->block-depth < depth) break
 7509       39/compare *(edi+8) 3/r32/ebx  # Var-block-depth
 7510       0f 8c/jump-if-< break/disp32
 7511       # var cand-reg/edi: (handle array byte) = cand->reg
 7512       8b/-> *(edi+0x10) 7/r32/edi
 7513       # if (cand-reg == null) continue
 7514       {
 7515 $already-spilled-this-block?:check-reg:
 7516         81 7/subop/compare %edi 0/imm32
 7517         74/jump-if-= break/disp8
 7518         # if (cand-reg == needle) return true
 7519         (string-equal? %esi %edi)  # => eax
 7520         3d/compare-eax-and 0/imm32/false
 7521         74/jump-if-= break/disp8
 7522         b8/copy-to-eax 1/imm32/true
 7523         eb/jump $already-spilled-this-block?:end/disp8
 7524       }
 7525 $already-spilled-this-block?:continue:
 7526       # curr -= 4
 7527       81 5/subop/subtract %edx 4/imm32
 7528       e9/jump loop/disp32
 7529     }
 7530     # return false
 7531     b8/copy-to-eax 0/imm32/false
 7532 $already-spilled-this-block?:end:
 7533     # . restore registers
 7534     5f/pop-to-edi
 7535     5e/pop-to-esi
 7536     5b/pop-to-ebx
 7537     5a/pop-to-edx
 7538     59/pop-to-ecx
 7539     # . epilogue
 7540     89/<- %esp 5/r32/ebp
 7541     5d/pop-to-ebp
 7542     c3/return
 7543 
 7544 # is there a var before 'v' with the same block-depth and register on the 'vars' stack?
 7545 # v is guaranteed to be within vars
 7546 # 'start' is provided as an optimization, a pointer within vars
 7547 # *start == v
 7548 same-register-spilled-before?:  # v: (handle var), vars: (addr stack (handle var)), start: (addr (handle var)) -> result/eax: boolean
 7549     # . prologue
 7550     55/push-ebp
 7551     89/<- %ebp 4/r32/esp
 7552     # . save registers
 7553     51/push-ecx
 7554     52/push-edx
 7555     53/push-ebx
 7556     56/push-esi
 7557     57/push-edi
 7558     # ecx = v
 7559     8b/-> *(ebp+8) 1/r32/ecx
 7560     # var reg/edx: (handle array byte) = v->register
 7561     8b/-> *(ecx+0x10) 2/r32/edx  # Var-register
 7562     # var depth/ebx: int = v->block-depth
 7563     8b/-> *(ecx+8) 3/r32/ebx  # Var-block-depth
 7564     # var min/ecx: (address (handle var)) = vars->data
 7565     8b/-> *(ebp+0xc) 1/r32/ecx
 7566     81 0/subop/add %ecx 8/imm32
 7567     # TODO: check that start >= min and start < &vars->data[top]
 7568     # TODO: check that *start == v
 7569     # var curr/esi: (address (handle var)) = start
 7570     8b/-> *(ebp+0x10) 6/r32/esi
 7571     # curr -= 4
 7572     81 5/subop/subtract %esi 4/imm32
 7573     {
 7574 $same-register-spilled-before?:loop:
 7575       # if (curr < min) break
 7576       39/compare %esi 1/r32/ecx
 7577       0f 82/jump-if-addr< break/disp32
 7578       # var x/eax: (handle var) = *curr
 7579       8b/-> *esi 0/r32/eax
 7580       # if (x->block-depth < depth) break
 7581       39/compare *(eax+8) 3/r32/ebx  # Var-block-depth
 7582       0f 8c/jump-if-< break/disp32
 7583       # if (x->register == 0) continue
 7584       81 7/subop/compare *(eax+0x10) 0/imm32  # Var-register
 7585       74/jump-if-= $same-register-spilled-before?:continue/disp8
 7586       # if (x->register == reg) return true
 7587       (string-equal? *(eax+0x10) %edx)  # Var-register => eax
 7588       3d/compare-eax-and 0/imm32/false
 7589       75/jump-if-!= $same-register-spilled-before?:end/disp8
 7590 $same-register-spilled-before?:continue:
 7591       # curr -= 4
 7592       81 5/subop/subtract %esi 4/imm32
 7593       e9/jump loop/disp32
 7594     }
 7595 $same-register-spilled-before?:false:
 7596     b8/copy-to-eax 0/imm32/false
 7597 $same-register-spilled-before?:end:
 7598     # . restore registers
 7599     5f/pop-to-edi
 7600     5e/pop-to-esi
 7601     5b/pop-to-ebx
 7602     5a/pop-to-edx
 7603     59/pop-to-ecx
 7604     # . epilogue
 7605     89/<- %esp 5/r32/ebp
 7606     5d/pop-to-ebp
 7607     c3/return
 7608 
 7609 # clean up global state for 'vars' until some block depth
 7610 clean-up-blocks:  # vars: (addr stack (handle var)), until-block-depth: int
 7611     # . prologue
 7612     55/push-ebp
 7613     89/<- %ebp 4/r32/esp
 7614     # . save registers
 7615     50/push-eax
 7616     51/push-ecx
 7617     56/push-esi
 7618     # esi = vars
 7619     8b/-> *(ebp+8) 6/r32/esi
 7620     # ecx = until-block-depth
 7621     8b/-> *(ebp+0xc) 1/r32/ecx
 7622     {
 7623 $clean-up-blocks:reclaim-loop:
 7624       # if (vars->top <= 0) break
 7625       81 7/subop/compare *esi 0/imm32  # Stack-top
 7626       7e/jump-if-<= break/disp8
 7627       # var v/eax: (handle var) = top(vars)
 7628       (top %esi)  # => eax
 7629       # if (v->block-depth < until-block-depth) break
 7630       39/compare *(eax+8) 1/r32/ecx  # Var-block-depth
 7631       7c/jump-if-< break/disp8
 7632       # if v is on the stack, update Curr-local-stack-offset
 7633       81 7/subop/compare *(eax+0x10) 0/imm32  # Var-register
 7634       {
 7635         75/jump-if-!= break/disp8
 7636 $clean-up-blocks:reclaim-var-on-stack:
 7637         (size-of %eax)  # => eax
 7638         01/add-to *Curr-local-stack-offset 0/r32/eax
 7639       }
 7640       (pop %esi)
 7641       e9/jump loop/disp32
 7642     }
 7643 $clean-up-blocks:end:
 7644     # . restore registers
 7645     5e/pop-to-esi
 7646     59/pop-to-ecx
 7647     58/pop-to-eax
 7648     # . epilogue
 7649     89/<- %esp 5/r32/ebp
 7650     5d/pop-to-ebp
 7651     c3/return
 7652 
 7653 emit-subx-var-def:  # out: (addr buffered-file), stmt: (handle stmt)
 7654     # . prologue
 7655     55/push-ebp
 7656     89/<- %ebp 4/r32/esp
 7657     # . save registers
 7658     50/push-eax
 7659     51/push-ecx
 7660     52/push-edx
 7661     # eax = stmt
 7662     8b/-> *(ebp+0xc) 0/r32/eax
 7663     # var v/ecx: (handle var)
 7664     8b/-> *(eax+4) 1/r32/ecx  # Vardef-var
 7665     # v->block-depth = *Curr-block-depth
 7666     8b/-> *Curr-block-depth 0/r32/eax
 7667     89/<- *(ecx+8) 0/r32/eax  # Var-block-depth
 7668     # var n/edx: int = size-of(stmt->var)
 7669     (size-of %ecx)  # => eax
 7670     89/<- %edx 0/r32/eax
 7671     # *Curr-local-stack-offset -= n
 7672     29/subtract-from *Curr-local-stack-offset 2/r32/edx
 7673     # v->offset = *Curr-local-stack-offset
 7674     8b/-> *Curr-local-stack-offset 0/r32/eax
 7675     89/<- *(ecx+0xc) 0/r32/eax  # Var-offset
 7676     # if v is an array, do something special
 7677     {
 7678       (is-mu-array? *(ecx+4))  # Var-type => eax
 7679       3d/compare-eax-and 0/imm32/false
 7680       0f 84/jump-if-= break/disp32
 7681       # var array-size-without-size/edx: int = n-4
 7682       81 5/subop/subtract %edx 4/imm32
 7683       (emit-indent *(ebp+8) *Curr-block-depth)
 7684       (write-buffered *(ebp+8) "(push-n-zero-bytes ")
 7685       (print-int32-buffered *(ebp+8) %edx)
 7686       (write-buffered *(ebp+8) ")\n")
 7687       (emit-indent *(ebp+8) *Curr-block-depth)
 7688       (write-buffered *(ebp+8) "68/push ")
 7689       (print-int32-buffered *(ebp+8) %edx)
 7690       (write-buffered *(ebp+8) "/imm32\n")
 7691       eb/jump $emit-subx-var-def:end/disp8
 7692     }
 7693     # while n > 0
 7694     {
 7695       81 7/subop/compare %edx 0/imm32
 7696       7e/jump-if-<= break/disp8
 7697       (emit-indent *(ebp+8) *Curr-block-depth)
 7698       (write-buffered *(ebp+8) "68/push 0/imm32\n")
 7699       # n -= 4
 7700       81 5/subop/subtract %edx 4/imm32
 7701       #
 7702       eb/jump loop/disp8
 7703     }
 7704 $emit-subx-var-def:end:
 7705     # . restore registers
 7706     5a/pop-to-edx
 7707     59/pop-to-ecx
 7708     58/pop-to-eax
 7709     # . epilogue
 7710     89/<- %esp 5/r32/ebp
 7711     5d/pop-to-ebp
 7712     c3/return
 7713 
 7714 emit-subx-stmt:  # out: (addr buffered-file), stmt: (handle stmt), primitives: (handle primitive)
 7715     # . prologue
 7716     55/push-ebp
 7717     89/<- %ebp 4/r32/esp
 7718     # . save registers
 7719     50/push-eax
 7720     51/push-ecx
 7721     # - some special-case primitives that don't actually use the 'primitives' data structure
 7722     # ecx = stmt
 7723     8b/-> *(ebp+0xc) 1/r32/ecx
 7724     # array size
 7725     {
 7726       # if (!string-equal?(stmt->operation, "length")) break
 7727       (string-equal? *(ecx+4) "length")  # Stmt1-operation => eax
 7728       3d/compare-eax-and 0/imm32
 7729       0f 84/jump-if-= break/disp32
 7730       (translate-mu-length-stmt *(ebp+8) *(ebp+0xc))
 7731       e9/jump $emit-subx-stmt:end/disp32
 7732     }
 7733     # index into array
 7734     {
 7735       # if (!string-equal?(var->operation, "index")) break
 7736       (string-equal? *(ecx+4) "index")  # Stmt1-operation => eax
 7737       3d/compare-eax-and 0/imm32
 7738       0f 84/jump-if-= break/disp32
 7739       (translate-mu-index-stmt *(ebp+8) *(ebp+0xc))
 7740       e9/jump $emit-subx-stmt:end/disp32
 7741     }
 7742     # compute-offset for index into array
 7743     {
 7744       # if (!string-equal?(var->operation, "compute-offset")) break
 7745       (string-equal? *(ecx+4) "compute-offset")  # Stmt1-operation => eax
 7746       3d/compare-eax-and 0/imm32
 7747       0f 84/jump-if-= break/disp32
 7748       (translate-mu-compute-index-stmt *(ebp+8) *(ebp+0xc))
 7749       e9/jump $emit-subx-stmt:end/disp32
 7750     }
 7751     # get field from record
 7752     {
 7753       # if (!string-equal?(var->operation, "get")) break
 7754       (string-equal? *(ecx+4) "get")  # Stmt1-operation => eax
 7755       3d/compare-eax-and 0/imm32
 7756       0f 84/jump-if-= break/disp32
 7757       (translate-mu-get-stmt *(ebp+8) *(ebp+0xc))
 7758       e9/jump $emit-subx-stmt:end/disp32
 7759     }
 7760     # - if stmt matches a primitive, emit it
 7761     {
 7762 $emit-subx-stmt:check-for-primitive:
 7763       (find-matching-primitive *(ebp+0x10) *(ebp+0xc))  # primitives, stmt => curr/eax
 7764       3d/compare-eax-and 0/imm32
 7765       74/jump-if-= break/disp8
 7766 $emit-subx-stmt:primitive:
 7767       (emit-subx-primitive *(ebp+8) *(ebp+0xc) %eax)  # out, stmt, curr
 7768       e9/jump $emit-subx-stmt:end/disp32
 7769     }
 7770     # - otherwise emit a call
 7771     # TODO: type-checking
 7772 $emit-subx-stmt:call:
 7773     (emit-call *(ebp+8) *(ebp+0xc))
 7774 $emit-subx-stmt:end:
 7775     # . restore registers
 7776     59/pop-to-ecx
 7777     58/pop-to-eax
 7778     # . epilogue
 7779     89/<- %esp 5/r32/ebp
 7780     5d/pop-to-ebp
 7781     c3/return
 7782 
 7783 translate-mu-length-stmt:  # out: (address buffered-file), stmt: (handle stmt)
 7784     # . prologue
 7785     55/push-ebp
 7786     89/<- %ebp 4/r32/esp
 7787     # . save registers
 7788     50/push-eax
 7789     51/push-ecx
 7790     # ecx = stmt
 7791     8b/-> *(ebp+0xc) 1/r32/ecx
 7792     #
 7793     (emit-indent *(ebp+8) *Curr-block-depth)
 7794     (write-buffered *(ebp+8) "8b/-> *")
 7795     # var base/eax: (handle var) = inouts[0]
 7796     8b/-> *(ecx+8) 0/r32/eax  # Stmt1-inouts or Regvardef-inouts
 7797     8b/-> *eax 0/r32/eax  # Stmt-var-value
 7798     # if base is an (address array ...) in a register
 7799     {
 7800       81 7/subop/compare *(eax+0x10)) 0/imm32  # Var-register
 7801       74/jump-if-= break/disp8
 7802       (write-buffered *(ebp+8) *(eax+0x10))  # Var-register
 7803       eb/jump $translate-mu-length-stmt:emit-output/disp8
 7804     }
 7805     # otherwise if base is an (array ...) on the stack
 7806     {
 7807       81 7/subop/compare *(eax+0xc)) 0/imm32  # Var-offset
 7808       74/jump-if-= break/disp8
 7809       (write-buffered *(ebp+8) "(ebp+")
 7810       (print-int32-buffered *(ebp+8) *(eax+0xc))  # Var-offset
 7811       (write-buffered *(ebp+8) ")")
 7812     }
 7813 $translate-mu-length-stmt:emit-output:
 7814     (write-buffered *(ebp+8) " ")
 7815     # outputs[0] "/r32"
 7816     8b/-> *(ecx+0xc) 0/r32/eax  # Stmt1-outputs
 7817     8b/-> *eax 0/r32/eax  # Stmt-var-value
 7818     (get Registers *(eax+0x10) 8 "Registers")  # Var-register => eax
 7819     (print-int32-buffered *(ebp+8) *eax)
 7820     (write-buffered *(ebp+8) "/r32\n")
 7821 $translate-mu-length-stmt:end:
 7822     # . restore registers
 7823     59/pop-to-ecx
 7824     58/pop-to-eax
 7825     # . epilogue
 7826     89/<- %esp 5/r32/ebp
 7827     5d/pop-to-ebp
 7828     c3/return
 7829 
 7830 translate-mu-index-stmt:  # out: (address buffered-file), stmt: (handle stmt)
 7831     # . prologue
 7832     55/push-ebp
 7833     89/<- %ebp 4/r32/esp
 7834     # . save registers
 7835     51/push-ecx
 7836     # var base/ecx: (handle var) = stmt->inouts[0]
 7837     8b/-> *(ebp+0xc) 1/r32/ecx
 7838     8b/-> *(ecx+8) 1/r32/ecx  # Stmt1-inouts
 7839     8b/-> *ecx 1/r32/ecx  # Stmt-var-value
 7840     # if (var->register) do one thing
 7841     {
 7842       81 7/subop/compare *(ecx+0x10) 0/imm32  # Var-register
 7843       74/jump-if-= break/disp8
 7844       # TODO: ensure there's no dereference
 7845       (translate-mu-index-stmt-with-array-in-register *(ebp+8) *(ebp+0xc))
 7846       eb/jump $translate-mu-index-stmt:end/disp8
 7847     }
 7848     # if (var->offset) do a different thing
 7849     {
 7850       81 7/subop/compare *(ecx+0xc) 0/imm32  # Var-offset
 7851       74/jump-if-= break/disp8
 7852       # TODO: ensure there's no dereference
 7853       (translate-mu-index-stmt-with-array-on-stack *(ebp+8) *(ebp+0xc))
 7854       eb/jump $translate-mu-index-stmt:end/disp8
 7855     }
 7856 $translate-mu-index-stmt:end:
 7857     # . restore registers
 7858     59/pop-to-ecx
 7859     # . epilogue
 7860     89/<- %esp 5/r32/ebp
 7861     5d/pop-to-ebp
 7862     c3/return
 7863 
 7864 $translate-mu-index-stmt-with-array:error1:
 7865     (write-buffered Stderr "couldn't translate an index instruction. second (index) input must either lie in a register or be a literal\n")
 7866     (flush Stderr)
 7867     # . syscall(exit, 1)
 7868     bb/copy-to-ebx  1/imm32
 7869     b8/copy-to-eax  1/imm32/exit
 7870     cd/syscall  0x80/imm8
 7871     # never gets here
 7872 
 7873 $translate-mu-index-stmt-with-array:error2:
 7874     (write-buffered Stderr "couldn't translate an index instruction. second (index) input when in a register must be an int or offset\n")
 7875     (flush Stderr)
 7876     # . syscall(exit, 1)
 7877     bb/copy-to-ebx  1/imm32
 7878     b8/copy-to-eax  1/imm32/exit
 7879     cd/syscall  0x80/imm8
 7880     # never gets here
 7881 
 7882 translate-mu-index-stmt-with-array-in-register:  # out: (address buffered-file), stmt: (handle stmt)
 7883     # . prologue
 7884     55/push-ebp
 7885     89/<- %ebp 4/r32/esp
 7886     # . save registers
 7887     50/push-eax
 7888     51/push-ecx
 7889     52/push-edx
 7890     53/push-ebx
 7891     #
 7892     (emit-indent *(ebp+8) *Curr-block-depth)
 7893     (write-buffered *(ebp+8) "8d/copy-address *(")
 7894     # TODO: ensure inouts[0] is in a register and not dereferenced
 7895 $translate-mu-index-stmt-with-array-in-register:emit-base:
 7896     # ecx = stmt
 7897     8b/-> *(ebp+0xc) 1/r32/ecx
 7898     # var base/ebx: (handle var) = inouts[0]
 7899     8b/-> *(ecx+8) 3/r32/ebx  # Stmt1-inouts
 7900     8b/-> *ebx 3/r32/ebx  # Stmt-var-value
 7901     # print base->register " + "
 7902     (write-buffered *(ebp+8) *(ebx+0x10))  # Var-register
 7903     #
 7904     (write-buffered *(ebp+8) " + ")
 7905     # var index/edx: (handle var) = inouts[1]
 7906     8b/-> *(ecx+8) 2/r32/edx  # Stmt1-inouts
 7907     8b/-> *(edx+4) 2/r32/edx  # Stmt-var-next
 7908     8b/-> *edx 2/r32/edx  # Stmt-var-value
 7909     # if index->register
 7910     81 7/subop/compare *(edx+0x10) 0/imm32  # Var-register
 7911     {
 7912       0f 84/jump-if-= break/disp32
 7913 $translate-mu-index-stmt-with-array-in-register:emit-register-index:
 7914       # if index is an int
 7915       (is-simple-mu-type? *(edx+4) 1)  # Var-type, int => eax
 7916       3d/compare-eax-and 0/imm32/false
 7917       {
 7918         0f 84/jump-if-= break/disp32
 7919 $translate-mu-index-stmt-with-array-in-register:emit-int-register-index:
 7920         # print index->register "<<" log2(size-of(element(base->type))) " + 4) "
 7921         # . index->register "<<"
 7922         (write-buffered *(ebp+8) *(edx+0x10))  # Var-register
 7923         (write-buffered *(ebp+8) "<<")
 7924         # . log2(size-of(element(base->type)))
 7925         # TODO: ensure size is a power of 2
 7926         (array-element-type-id %ebx)  # => eax
 7927         (size-of-type-id %eax)  # => eax
 7928         (num-shift-rights %eax)  # => eax
 7929         (print-int32-buffered *(ebp+8) %eax)
 7930         e9/jump $translate-mu-index-stmt-with-array-in-register:emit-register-index-done/disp32
 7931       }
 7932       # if index->type is any other atom, abort
 7933       8b/-> *(edx+4) 0/r32/eax  # Var-type
 7934       81 7/subop/compare *eax 0/imm32/false  # Tree-is-atom
 7935       0f 85/jump-if-!= $translate-mu-index-stmt-with-array:error2/disp32
 7936       # if index has type (offset ...)
 7937       (is-simple-mu-type? *(eax+4) 7)  # Tree-left, offset => eax
 7938       3d/compare-eax-and 0/imm32/false
 7939       {
 7940         0f 84/jump-if-= break/disp32
 7941         # print index->register " + 4) "
 7942 $translate-mu-index-stmt-with-array-in-register:emit-offset-register-index:
 7943         (write-buffered *(ebp+8) *(edx+0x10))  # Var-register
 7944       }
 7945 $translate-mu-index-stmt-with-array-in-register:emit-register-index-done:
 7946       (write-buffered *(ebp+8) " + 4) ")
 7947       e9/jump $translate-mu-index-stmt-with-array-in-register:emit-output/disp32
 7948     }
 7949     # otherwise if index is a literal
 7950     (is-simple-mu-type? *(edx+4) 0)  # Var-type => eax
 7951     3d/compare-eax-and 0/imm32/false
 7952     {
 7953       0f 84/jump-if-= break/disp32
 7954 $translate-mu-index-stmt-with-array-in-register:emit-literal-index:
 7955       # var index-value/edx: int = parse-hex-int(index->name)
 7956       (parse-hex-int *edx)  # Var-name => eax
 7957       89/<- %edx 0/r32/eax
 7958       # offset = idx-value * size-of(element(base->type))
 7959       (array-element-type-id %ebx)  # => eax
 7960       (size-of-type-id %eax)  # => eax
 7961       f7 4/subop/multiply-into-eax %edx  # clobbers edx
 7962       # offset += 4 for array size
 7963       05/add-to-eax 4/imm32
 7964       # TODO: check edx for overflow
 7965       # print offset
 7966       (print-int32-buffered *(ebp+8) %eax)
 7967       (write-buffered *(ebp+8) ") ")
 7968       e9/jump $translate-mu-index-stmt-with-array-in-register:emit-output/disp32
 7969     }
 7970     # otherwise abort
 7971     e9/jump $translate-mu-index-stmt-with-array:error1/disp32
 7972 $translate-mu-index-stmt-with-array-in-register:emit-output:
 7973     # outputs[0] "/r32"
 7974     8b/-> *(ebp+0xc) 1/r32/ecx
 7975     8b/-> *(ecx+0xc) 0/r32/eax  # Stmt1-outputs
 7976     8b/-> *eax 0/r32/eax  # Stmt-var-value
 7977     (get Registers *(eax+0x10) 8 "Registers")  # Var-register => eax
 7978     (print-int32-buffered *(ebp+8) *eax)
 7979     (write-buffered *(ebp+8) "/r32\n")
 7980 $translate-mu-index-stmt-with-array-in-register:end:
 7981     # . restore registers
 7982     5b/pop-to-ebx
 7983     5a/pop-to-edx
 7984     59/pop-to-ecx
 7985     58/pop-to-eax
 7986     # . epilogue
 7987     89/<- %esp 5/r32/ebp
 7988     5d/pop-to-ebp
 7989     c3/return
 7990 
 7991 translate-mu-index-stmt-with-array-on-stack:  # out: (address buffered-file), stmt: (handle stmt)
 7992     # . prologue
 7993     55/push-ebp
 7994     89/<- %ebp 4/r32/esp
 7995     # . save registers
 7996     50/push-eax
 7997     51/push-ecx
 7998     52/push-edx
 7999     53/push-ebx
 8000     #
 8001     (emit-indent *(ebp+8) *Curr-block-depth)
 8002     (write-buffered *(ebp+8) "8d/copy-address *(ebp + ")
 8003     # var curr/eax = stmt->inouts
 8004     8b/-> *(ebp+0xc) 0/r32/eax
 8005     # var base/ecx: (handle var) = stmt->inouts[0]
 8006     8b/-> *(eax+8) 0/r32/eax  # Stmt1-inouts
 8007     8b/-> *eax 1/r32/ecx  # Stmt-var-value
 8008     # curr = curr->next
 8009     8b/-> *(eax+4) 0/r32/eax  # Stmt-var-next
 8010     # var index/edx: (handle var) = stmt->inouts[1]
 8011     8b/-> *eax 2/r32/edx  # Stmt-var-value
 8012     # if index->register
 8013     81 7/subop/compare *(edx+0x10) 0/imm32  # Var-register
 8014     {
 8015       0f 84/jump-if-= break/disp32
 8016 $translate-mu-index-stmt-with-array-on-stack:emit-register-index:
 8017       # if index is an int
 8018       (is-simple-mu-type? *(edx+4) 1)  # Var-type, int => eax
 8019       3d/compare-eax-and 0/imm32/false
 8020       {
 8021         0f 84/jump-if-= break/disp32
 8022 $translate-mu-index-stmt-with-array-on-stack:emit-int-register-index:
 8023         # print index->register "<<" log2(size-of(element-type(base))) " + " base->offset+4
 8024         # . inouts[1]->register "<<"
 8025         (write-buffered *(ebp+8) *(edx+0x10))  # Var-register
 8026         (write-buffered *(ebp+8) "<<")
 8027         # . log2(size-of(element(base)))
 8028         # TODO: ensure size is a power of 2
 8029         (array-element-type-id %ecx)  # => eax
 8030         (size-of-type-id %eax)  # => eax
 8031         (num-shift-rights %eax)  # => eax
 8032         (print-int32-buffered *(ebp+8) %eax)
 8033         #
 8034         (write-buffered *(ebp+8) " + ")
 8035         #
 8036         8b/-> *(ecx+0xc) 0/r32/eax  # Var-offset
 8037         05/add-to-eax 4/imm32
 8038         (print-int32-buffered *(ebp+8) %eax)
 8039         e9/jump $translate-mu-index-stmt-with-array-on-stack:emit-register-index-done/disp32
 8040       }
 8041       # if index->type is any other atom, abort
 8042       8b/-> *(edx+4) 0/r32/eax  # Var-type
 8043       81 7/subop/compare *eax 0/imm32/false  # Tree-is-atom
 8044       0f 85/jump-if-!= $translate-mu-index-stmt-with-array:error2/disp32
 8045       # if index has type (offset ...)
 8046       (is-simple-mu-type? *(eax+4) 7)  # Tree-left, offset => eax
 8047       3d/compare-eax-and 0/imm32/false
 8048       {
 8049         0f 84/jump-if-= break/disp32
 8050         # print index->register
 8051 $translate-mu-index-stmt-with-array-on-stack:emit-offset-register-index:
 8052         (write-buffered *(ebp+8) *(edx+0x10))  # Var-register
 8053       }
 8054 $translate-mu-index-stmt-with-array-on-stack:emit-register-index-done:
 8055       (write-buffered *(ebp+8) ") ")
 8056       e9/jump $translate-mu-index-stmt-with-array-on-stack:emit-output/disp32
 8057     }
 8058     # otherwise if index is a literal
 8059     (is-simple-mu-type? *(edx+4) 0)  # Var-type => eax
 8060     3d/compare-eax-and 0/imm32/false
 8061     {
 8062       0f 84/jump-if-= break/disp32
 8063 $translate-mu-index-stmt-with-array-on-stack:emit-literal-index:
 8064       # var idx-value/edx: int = parse-hex-int(index->name)
 8065       (parse-hex-int *edx)  # Var-name => eax
 8066       89/<- %ebx 0/r32/eax
 8067       # offset = idx-value * size-of(element-type(base->type))
 8068       (array-element-type-id %ecx)  # => eax
 8069       (size-of-type-id %eax)  # => eax
 8070       f7 4/subop/multiply-into-eax %ebx  # clobbers edx
 8071       # offset += base->offset
 8072       03/add *(ecx+0xc) 0/r32/eax  # Var-offset
 8073       # offset += 4 for array size
 8074       05/add-to-eax 4/imm32
 8075       # TODO: check edx for overflow
 8076       # print offset
 8077       (print-int32-buffered *(ebp+8) %eax)
 8078       (write-buffered *(ebp+8) ") ")
 8079       e9/jump $translate-mu-index-stmt-with-array-on-stack:emit-output/disp32
 8080     }
 8081     # otherwise abort
 8082     e9/jump $translate-mu-index-stmt-with-array:error1/disp32
 8083 $translate-mu-index-stmt-with-array-on-stack:emit-output:
 8084     # outputs[0] "/r32"
 8085     8b/-> *(ebp+0xc) 0/r32/eax
 8086     8b/-> *(eax+0xc) 0/r32/eax  # Stmt1-outputs
 8087     8b/-> *eax 0/r32/eax  # Stmt-var-value
 8088     (get Registers *(eax+0x10) 8 "Registers")  # Var-register => eax
 8089     (print-int32-buffered *(ebp+8) *eax)
 8090     (write-buffered *(ebp+8) "/r32\n")
 8091 $translate-mu-index-stmt-with-array-on-stack:end:
 8092     # . restore registers
 8093     5b/pop-to-ebx
 8094     5a/pop-to-edx
 8095     59/pop-to-ecx
 8096     58/pop-to-eax
 8097     # . epilogue
 8098     89/<- %esp 5/r32/ebp
 8099     5d/pop-to-ebp
 8100     c3/return
 8101 
 8102 translate-mu-compute-index-stmt:  # out: (address buffered-file), stmt: (handle stmt)
 8103     # . prologue
 8104     55/push-ebp
 8105     89/<- %ebp 4/r32/esp
 8106     # . save registers
 8107     50/push-eax
 8108     51/push-ecx
 8109     52/push-edx
 8110     53/push-ebx
 8111     #
 8112     (emit-indent *(ebp+8) *Curr-block-depth)
 8113     (write-buffered *(ebp+8) "69/multiply")
 8114     # ecx = stmt
 8115     8b/-> *(ebp+0xc) 1/r32/ecx
 8116     # var first-inout/edx: (handle stmt-var) = stmt->inouts[0]
 8117     8b/-> *(ecx+8) 2/r32/edx  # Stmt1-inouts
 8118 $translate-mu-compute-index-stmt:emit-index:
 8119     (emit-subx-var-as-rm32 *(ebp+8) *(edx+4))  # Stmt-var-next
 8120     (write-buffered *(ebp+8) Space)
 8121 $translate-mu-compute-index-stmt:emit-elem-size:
 8122     # var base/ebx: (handle var)
 8123     8b/-> *edx 3/r32/ebx  # Stmt-var-value
 8124     # print size-of(element(base->type))
 8125     (array-element-type-id %ebx)  # => eax
 8126     (size-of-type-id %eax)  # => eax
 8127     (print-int32-buffered *(ebp+8) %eax)
 8128     (write-buffered *(ebp+8) "/imm32 ")
 8129 $translate-mu-compute-index-stmt:emit-output:
 8130     # outputs[0] "/r32"
 8131     8b/-> *(ecx+0xc) 0/r32/eax  # Stmt1-outputs
 8132     8b/-> *eax 0/r32/eax  # Stmt-var-value
 8133     (get Registers *(eax+0x10) 8 "Registers")  # Var-register => eax
 8134     (print-int32-buffered *(ebp+8) *eax)
 8135     (write-buffered *(ebp+8) "/r32\n")
 8136 $translate-mu-compute-index-stmt:end:
 8137     # . restore registers
 8138     5b/pop-to-ebx
 8139     5a/pop-to-edx
 8140     59/pop-to-ecx
 8141     58/pop-to-eax
 8142     # . epilogue
 8143     89/<- %esp 5/r32/ebp
 8144     5d/pop-to-ebp
 8145     c3/return
 8146 
 8147 translate-mu-get-stmt:  # out: (address buffered-file), stmt: (handle stmt)
 8148     # . prologue
 8149     55/push-ebp
 8150     89/<- %ebp 4/r32/esp
 8151     # . save registers
 8152     50/push-eax
 8153     51/push-ecx
 8154     52/push-edx
 8155     #
 8156     (emit-indent *(ebp+8) *Curr-block-depth)
 8157     (write-buffered *(ebp+8) "8d/copy-address ")
 8158     # ecx = stmt
 8159     8b/-> *(ebp+0xc) 1/r32/ecx
 8160     # var offset/edx: int = get offset of stmt
 8161     (mu-get-offset %ecx)  # => eax
 8162     89/<- %edx 0/r32/eax
 8163     # var base/eax: (handle var) = stmt->inouts[0]
 8164     8b/-> *(ecx+8) 0/r32/eax  # Stmt1-inouts
 8165     8b/-> *eax 0/r32/eax  # Stmt-var-value
 8166     # if base is in a register
 8167     81 7/subop/compare *(eax+0x10) 0/imm32
 8168     {
 8169       0f 84/jump-if-= break/disp32
 8170 $translate-mu-get-stmt:emit-register-input:
 8171       # "*(" inouts[0]->register " + " offset ")"
 8172       (write-buffered *(ebp+8) "*(")
 8173       (write-buffered *(ebp+8) *(eax+0x10))  # Var-register
 8174       (write-buffered *(ebp+8) " + ")
 8175       (print-int32-buffered *(ebp+8) %edx)
 8176       (write-buffered *(ebp+8) ") ")
 8177       e9/jump $translate-mu-get-stmt:emit-output/disp32
 8178     }
 8179     # otherwise base is on the stack
 8180     {
 8181 $translate-mu-get-stmt:emit-stack-input:
 8182       # "*(ebp + " inouts[0]->offset + offset ")"
 8183       (write-buffered *(ebp+8) "*(ebp+")
 8184       03/add *(eax+0xc) 2/r32/edx  # Var-offset
 8185       (print-int32-buffered *(ebp+8) %edx)
 8186       (write-buffered *(ebp+8) ") ")
 8187       eb/jump $translate-mu-get-stmt:emit-output/disp8
 8188     }
 8189 $translate-mu-get-stmt:emit-output:
 8190     # outputs[0] "/r32"
 8191     8b/-> *(ecx+0xc) 0/r32/eax  # Stmt1-outputs
 8192     8b/-> *eax 0/r32/eax  # Stmt-var-value
 8193     (get Registers *(eax+0x10) 8 "Registers")  # Var-register => eax
 8194     (print-int32-buffered *(ebp+8) *eax)
 8195     (write-buffered *(ebp+8) "/r32\n")
 8196 $translate-mu-get-stmt:end:
 8197     # . restore registers
 8198     5a/pop-to-edx
 8199     59/pop-to-ecx
 8200     58/pop-to-eax
 8201     # . epilogue
 8202     89/<- %esp 5/r32/ebp
 8203     5d/pop-to-ebp
 8204     c3/return
 8205 
 8206 array-element-type-id:  # v: (handle var) -> result/eax: type-id
 8207     # precondition: n is positive
 8208     # . prologue
 8209     55/push-ebp
 8210     89/<- %ebp 4/r32/esp
 8211     #
 8212     8b/-> *(ebp+8) 0/r32/eax
 8213     8b/-> *(eax+4) 0/r32/eax  # Var-type
 8214     # TODO: ensure type->left is 'addr'
 8215     8b/-> *(eax+8) 0/r32/eax  # Tree-right
 8216     # TODO: ensure that type->right is non-null
 8217     # TODO: ensure that type->right->left is 'array'
 8218     8b/-> *(eax+8) 0/r32/eax  # Tree-right
 8219     # TODO: ensure that type->right->right is non-null
 8220     8b/-> *(eax+4) 0/r32/eax  # Tree-left
 8221     8b/-> *(eax+4) 0/r32/eax  # Tree-value
 8222 $array-element-type-id:end:
 8223     # . epilogue
 8224     89/<- %esp 5/r32/ebp
 8225     5d/pop-to-ebp
 8226     c3/return
 8227 
 8228 num-shift-rights:  # n: int -> result/eax: int
 8229     # precondition: n is a positive power of 2
 8230     # . prologue
 8231     55/push-ebp
 8232     89/<- %ebp 4/r32/esp
 8233     # . save registers
 8234     51/push-ecx
 8235     # var curr/ecx: int = n
 8236     8b/-> *(ebp+8) 1/r32/ecx
 8237     # result = 0
 8238     b8/copy-to-eax 0/imm32
 8239     {
 8240       # if (curr <= 1) break
 8241       81 7/subop/compare %ecx 1/imm32
 8242       7e/jump-if-<= break/disp8
 8243       40/increment-eax
 8244       c1/shift 5/subop/arithmetic-right %ecx 1/imm8
 8245       eb/jump loop/disp8
 8246     }
 8247 $num-shift-rights:end:
 8248     # . restore registers
 8249     59/pop-to-ecx
 8250     # . epilogue
 8251     89/<- %esp 5/r32/ebp
 8252     5d/pop-to-ebp
 8253     c3/return
 8254 
 8255 mu-get-offset:  # stmt: (handle stmt) -> result/eax: int
 8256     # . prologue
 8257     55/push-ebp
 8258     89/<- %ebp 4/r32/esp
 8259     # var second-inout/eax: (handle stmt-var) = stmt->inouts->next
 8260     8b/-> *(ebp+8) 0/r32/eax
 8261     8b/-> *(eax+8) 0/r32/eax  # Stmt1-inouts
 8262     8b/-> *(eax+4) 0/r32/eax  # Stmt-var-next
 8263     # var output-var/eax: (handle var) = second-inout->value
 8264     8b/-> *eax 0/r32/eax  # Stmt-var-value
 8265     # return output-var->offset
 8266     8b/-> *(eax+0xc) 0/r32/eax  # Var-offset
 8267 $emit-get-offset:end:
 8268     # . epilogue
 8269     89/<- %esp 5/r32/ebp
 8270     5d/pop-to-ebp
 8271     c3/return
 8272 
 8273 emit-subx-block:  # out: (addr buffered-file), block: (handle block), vars: (addr stack (handle var))
 8274     # . prologue
 8275     55/push-ebp
 8276     89/<- %ebp 4/r32/esp
 8277     # . save registers
 8278     50/push-eax
 8279     51/push-ecx
 8280     56/push-esi
 8281     # esi = block
 8282     8b/-> *(ebp+0xc) 6/r32/esi
 8283     # block->var->block-depth = *Curr-block-depth
 8284     8b/-> *(esi+8) 0/r32/eax  # Block-var
 8285     8b/-> *Curr-block-depth 1/r32/ecx
 8286     89/<- *(eax+8) 1/r32/ecx  # Var-block-depth
 8287     # var stmts/eax: (handle list stmt) = block->statements
 8288     8b/-> *(esi+4) 0/r32/eax  # Block-stmts
 8289     #
 8290     {
 8291 $emit-subx-block:check-empty:
 8292       3d/compare-eax-and 0/imm32
 8293       0f 84/jump-if-= break/disp32
 8294       (emit-indent *(ebp+8) *Curr-block-depth)
 8295       (write-buffered *(ebp+8) "{\n")
 8296       # var v/ecx: (handle var)
 8297       8b/-> *(esi+8) 1/r32/ecx  # Block-var
 8298       #
 8299       (write-buffered *(ebp+8) *ecx)  # Var-name
 8300       (write-buffered *(ebp+8) ":loop:\n")
 8301       ff 0/subop/increment *Curr-block-depth
 8302       (push *(ebp+0x10) %ecx)
 8303       (emit-subx-stmt-list *(ebp+8) %eax *(ebp+0x10))
 8304       (pop *(ebp+0x10))  # => eax
 8305       ff 1/subop/decrement *Curr-block-depth
 8306       (emit-indent *(ebp+8) *Curr-block-depth)
 8307       (write-buffered *(ebp+8) "}\n")
 8308       (write-buffered *(ebp+8) *ecx)  # Var-name
 8309       (write-buffered *(ebp+8) ":break:\n")
 8310     }
 8311 $emit-subx-block:end:
 8312     # . restore registers
 8313     5e/pop-to-esi
 8314     59/pop-to-ecx
 8315     58/pop-to-eax
 8316     # . epilogue
 8317     89/<- %esp 5/r32/ebp
 8318     5d/pop-to-ebp
 8319     c3/return
 8320 
 8321 # Primitives supported
 8322 # For each operation, put variants with hard-coded registers before flexible ones.
 8323 == data
 8324 Primitives:
 8325 # - increment/decrement
 8326 _Primitive-inc-eax:
 8327     # var/eax <- increment => 40/increment-eax
 8328     "increment"/imm32/name
 8329     0/imm32/no-inouts
 8330     Single-int-var-in-eax/imm32/outputs
 8331     "40/increment-eax"/imm32/subx-name
 8332     0/imm32/no-rm32
 8333     0/imm32/no-r32
 8334     0/imm32/no-imm32
 8335     0/imm32/no-disp32
 8336     0/imm32/output-is-write-only
 8337     _Primitive-inc-ecx/imm32/next
 8338 _Primitive-inc-ecx:
 8339     # var/ecx <- increment => 41/increment-ecx
 8340     "increment"/imm32/name
 8341     0/imm32/no-inouts
 8342     Single-int-var-in-ecx/imm32/outputs
 8343     "41/increment-ecx"/imm32/subx-name
 8344     0/imm32/no-rm32
 8345     0/imm32/no-r32
 8346     0/imm32/no-imm32
 8347     0/imm32/no-disp32
 8348     0/imm32/output-is-write-only
 8349     _Primitive-inc-edx/imm32/next
 8350 _Primitive-inc-edx:
 8351     # var/edx <- increment => 42/increment-edx
 8352     "increment"/imm32/name
 8353     0/imm32/no-inouts
 8354     Single-int-var-in-edx/imm32/outputs
 8355     "42/increment-edx"/imm32/subx-name
 8356     0/imm32/no-rm32
 8357     0/imm32/no-r32
 8358     0/imm32/no-imm32
 8359     0/imm32/no-disp32
 8360     0/imm32/output-is-write-only
 8361     _Primitive-inc-ebx/imm32/next
 8362 _Primitive-inc-ebx:
 8363     # var/ebx <- increment => 43/increment-ebx
 8364     "increment"/imm32/name
 8365     0/imm32/no-inouts
 8366     Single-int-var-in-ebx/imm32/outputs
 8367     "43/increment-ebx"/imm32/subx-name
 8368     0/imm32/no-rm32
 8369     0/imm32/no-r32
 8370     0/imm32/no-imm32
 8371     0/imm32/no-disp32
 8372     0/imm32/output-is-write-only
 8373     _Primitive-inc-esi/imm32/next
 8374 _Primitive-inc-esi:
 8375     # var/esi <- increment => 46/increment-esi
 8376     "increment"/imm32/name
 8377     0/imm32/no-inouts
 8378     Single-int-var-in-esi/imm32/outputs
 8379     "46/increment-esi"/imm32/subx-name
 8380     0/imm32/no-rm32
 8381     0/imm32/no-r32
 8382     0/imm32/no-imm32
 8383     0/imm32/no-disp32
 8384     0/imm32/output-is-write-only
 8385     _Primitive-inc-edi/imm32/next
 8386 _Primitive-inc-edi:
 8387     # var/edi <- increment => 47/increment-edi
 8388     "increment"/imm32/name
 8389     0/imm32/no-inouts
 8390     Single-int-var-in-edi/imm32/outputs
 8391     "47/increment-edi"/imm32/subx-name
 8392     0/imm32/no-rm32
 8393     0/imm32/no-r32
 8394     0/imm32/no-imm32
 8395     0/imm32/no-disp32
 8396     0/imm32/output-is-write-only
 8397     _Primitive-dec-eax/imm32/next
 8398 _Primitive-dec-eax:
 8399     # var/eax <- decrement => 48/decrement-eax
 8400     "decrement"/imm32/name
 8401     0/imm32/no-inouts
 8402     Single-int-var-in-eax/imm32/outputs
 8403     "48/decrement-eax"/imm32/subx-name
 8404     0/imm32/no-rm32
 8405     0/imm32/no-r32
 8406     0/imm32/no-imm32
 8407     0/imm32/no-disp32
 8408     0/imm32/output-is-write-only
 8409     _Primitive-dec-ecx/imm32/next
 8410 _Primitive-dec-ecx:
 8411     # var/ecx <- decrement => 49/decrement-ecx
 8412     "decrement"/imm32/name
 8413     0/imm32/no-inouts
 8414     Single-int-var-in-ecx/imm32/outputs
 8415     "49/decrement-ecx"/imm32/subx-name
 8416     0/imm32/no-rm32
 8417     0/imm32/no-r32
 8418     0/imm32/no-imm32
 8419     0/imm32/no-disp32
 8420     0/imm32/output-is-write-only
 8421     _Primitive-dec-edx/imm32/next
 8422 _Primitive-dec-edx:
 8423     # var/edx <- decrement => 4a/decrement-edx
 8424     "decrement"/imm32/name
 8425     0/imm32/no-inouts
 8426     Single-int-var-in-edx/imm32/outputs
 8427     "4a/decrement-edx"/imm32/subx-name
 8428     0/imm32/no-rm32
 8429     0/imm32/no-r32
 8430     0/imm32/no-imm32
 8431     0/imm32/no-disp32
 8432     0/imm32/output-is-write-only
 8433     _Primitive-dec-ebx/imm32/next
 8434 _Primitive-dec-ebx:
 8435     # var/ebx <- decrement => 4b/decrement-ebx
 8436     "decrement"/imm32/name
 8437     0/imm32/no-inouts
 8438     Single-int-var-in-ebx/imm32/outputs
 8439     "4b/decrement-ebx"/imm32/subx-name
 8440     0/imm32/no-rm32
 8441     0/imm32/no-r32
 8442     0/imm32/no-imm32
 8443     0/imm32/no-disp32
 8444     0/imm32/output-is-write-only
 8445     _Primitive-dec-esi/imm32/next
 8446 _Primitive-dec-esi:
 8447     # var/esi <- decrement => 4e/decrement-esi
 8448     "decrement"/imm32/name
 8449     0/imm32/no-inouts
 8450     Single-int-var-in-esi/imm32/outputs
 8451     "4e/decrement-esi"/imm32/subx-name
 8452     0/imm32/no-rm32
 8453     0/imm32/no-r32
 8454     0/imm32/no-imm32
 8455     0/imm32/no-disp32
 8456     0/imm32/output-is-write-only
 8457     _Primitive-dec-edi/imm32/next
 8458 _Primitive-dec-edi:
 8459     # var/edi <- decrement => 4f/decrement-edi
 8460     "decrement"/imm32/name
 8461     0/imm32/no-inouts
 8462     Single-int-var-in-edi/imm32/outputs
 8463     "4f/decrement-edi"/imm32/subx-name
 8464     0/imm32/no-rm32
 8465     0/imm32/no-r32
 8466     0/imm32/no-imm32
 8467     0/imm32/no-disp32
 8468     0/imm32/output-is-write-only
 8469     _Primitive-inc-mem/imm32/next
 8470 _Primitive-inc-mem:
 8471     # increment var => ff 0/subop/increment *(ebp+__)
 8472     "increment"/imm32/name
 8473     Single-int-var-in-mem/imm32/inouts
 8474     0/imm32/no-outputs
 8475     "ff 0/subop/increment"/imm32/subx-name
 8476     1/imm32/rm32-is-first-inout
 8477     0/imm32/no-r32
 8478     0/imm32/no-imm32
 8479     0/imm32/no-disp32
 8480     0/imm32/output-is-write-only
 8481     _Primitive-inc-reg/imm32/next
 8482 _Primitive-inc-reg:
 8483     # var/reg <- increment => ff 0/subop/increment %__
 8484     "increment"/imm32/name
 8485     0/imm32/no-inouts
 8486     Single-int-var-in-some-register/imm32/outputs
 8487     "ff 0/subop/increment"/imm32/subx-name
 8488     3/imm32/rm32-is-first-output
 8489     0/imm32/no-r32
 8490     0/imm32/no-imm32
 8491     0/imm32/no-disp32
 8492     0/imm32/output-is-write-only
 8493     _Primitive-dec-mem/imm32/next
 8494 _Primitive-dec-mem:
 8495     # decrement var => ff 1/subop/decrement *(ebp+__)
 8496     "decrement"/imm32/name
 8497     Single-int-var-in-mem/imm32/inouts
 8498     0/imm32/no-outputs
 8499     "ff 1/subop/decrement"/imm32/subx-name
 8500     1/imm32/rm32-is-first-inout
 8501     0/imm32/no-r32
 8502     0/imm32/no-imm32
 8503     0/imm32/no-disp32
 8504     0/imm32/output-is-write-only
 8505     _Primitive-dec-reg/imm32/next
 8506 _Primitive-dec-reg:
 8507     # var/reg <- decrement => ff 1/subop/decrement %__
 8508     "decrement"/imm32/name
 8509     0/imm32/no-inouts
 8510     Single-int-var-in-some-register/imm32/outputs
 8511     "ff 1/subop/decrement"/imm32/subx-name
 8512     3/imm32/rm32-is-first-output
 8513     0/imm32/no-r32
 8514     0/imm32/no-imm32
 8515     0/imm32/no-disp32
 8516     0/imm32/output-is-write-only
 8517     _Primitive-add-to-eax/imm32/next
 8518 # - add
 8519 _Primitive-add-to-eax:
 8520     # var/eax <- add lit => 05/add-to-eax lit/imm32
 8521     "add"/imm32/name
 8522     Single-lit-var/imm32/inouts
 8523     Single-int-var-in-eax/imm32/outputs
 8524     "05/add-to-eax"/imm32/subx-name
 8525     0/imm32/no-rm32
 8526     0/imm32/no-r32
 8527     1/imm32/imm32-is-first-inout
 8528     0/imm32/no-disp32
 8529     0/imm32/output-is-write-only
 8530     _Primitive-add-reg-to-reg/imm32/next
 8531 _Primitive-add-reg-to-reg:
 8532     # var1/reg <- add var2/reg => 01/add-to var1/rm32 var2/r32
 8533     "add"/imm32/name
 8534     Single-int-var-in-some-register/imm32/inouts
 8535     Single-int-var-in-some-register/imm32/outputs
 8536     "01/add-to"/imm32/subx-name
 8537     3/imm32/rm32-is-first-output
 8538     1/imm32/r32-is-first-inout
 8539     0/imm32/no-imm32
 8540     0/imm32/no-disp32
 8541     0/imm32/output-is-write-only
 8542     _Primitive-add-reg-to-mem/imm32/next
 8543 _Primitive-add-reg-to-mem:
 8544     # add-to var1 var2/reg => 01/add-to var1 var2/r32
 8545     "add-to"/imm32/name
 8546     Two-args-int-stack-int-reg/imm32/inouts
 8547     0/imm32/outputs
 8548     "01/add-to"/imm32/subx-name
 8549     1/imm32/rm32-is-first-inout
 8550     2/imm32/r32-is-second-inout
 8551     0/imm32/no-imm32
 8552     0/imm32/no-disp32
 8553     0/imm32/output-is-write-only
 8554     _Primitive-add-mem-to-reg/imm32/next
 8555 _Primitive-add-mem-to-reg:
 8556     # var1/reg <- add var2 => 03/add var2/rm32 var1/r32
 8557     "add"/imm32/name
 8558     Single-int-var-in-mem/imm32/inouts
 8559     Single-int-var-in-some-register/imm32/outputs
 8560     "03/add"/imm32/subx-name
 8561     1/imm32/rm32-is-first-inout
 8562     3/imm32/r32-is-first-output
 8563     0/imm32/no-imm32
 8564     0/imm32/no-disp32
 8565     0/imm32/output-is-write-only
 8566     _Primitive-add-lit-to-reg/imm32/next
 8567 _Primitive-add-lit-to-reg:
 8568     # var1/reg <- add lit => 81 0/subop/add var1/rm32 lit/imm32
 8569     "add"/imm32/name
 8570     Single-lit-var/imm32/inouts
 8571     Single-int-var-in-some-register/imm32/outputs
 8572     "81 0/subop/add"/imm32/subx-name
 8573     3/imm32/rm32-is-first-output
 8574     0/imm32/no-r32
 8575     1/imm32/imm32-is-first-inout
 8576     0/imm32/no-disp32
 8577     0/imm32/output-is-write-only
 8578     _Primitive-add-lit-to-mem/imm32/next
 8579 _Primitive-add-lit-to-mem:
 8580     # add-to var1, lit => 81 0/subop/add var1/rm32 lit/imm32
 8581     "add-to"/imm32/name
 8582     Int-var-and-literal/imm32/inouts
 8583     0/imm32/outputs
 8584     "81 0/subop/add"/imm32/subx-name
 8585     1/imm32/rm32-is-first-inout
 8586     0/imm32/no-r32
 8587     2/imm32/imm32-is-second-inout
 8588     0/imm32/no-disp32
 8589     0/imm32/output-is-write-only
 8590     _Primitive-subtract-from-eax/imm32/next
 8591 # - subtract
 8592 _Primitive-subtract-from-eax:
 8593     # var/eax <- subtract lit => 2d/subtract-from-eax lit/imm32
 8594     "subtract"/imm32/name
 8595     Single-lit-var/imm32/inouts
 8596     Single-int-var-in-eax/imm32/outputs
 8597     "2d/subtract-from-eax"/imm32/subx-name
 8598     0/imm32/no-rm32
 8599     0/imm32/no-r32
 8600     1/imm32/imm32-is-first-inout
 8601     0/imm32/no-disp32
 8602     0/imm32/output-is-write-only
 8603     _Primitive-subtract-reg-from-reg/imm32/next
 8604 _Primitive-subtract-reg-from-reg:
 8605     # var1/reg <- subtract var2/reg => 29/subtract-from var1/rm32 var2/r32
 8606     "subtract"/imm32/name
 8607     Single-int-var-in-some-register/imm32/inouts
 8608     Single-int-var-in-some-register/imm32/outputs
 8609     "29/subtract-from"/imm32/subx-name
 8610     3/imm32/rm32-is-first-output
 8611     1/imm32/r32-is-first-inout
 8612     0/imm32/no-imm32
 8613     0/imm32/no-disp32
 8614     0/imm32/output-is-write-only
 8615     _Primitive-subtract-reg-from-mem/imm32/next
 8616 _Primitive-subtract-reg-from-mem:
 8617     # subtract-from var1 var2/reg => 29/subtract-from var1 var2/r32
 8618     "subtract-from"/imm32/name
 8619     Two-args-int-stack-int-reg/imm32/inouts
 8620     0/imm32/outputs
 8621     "29/subtract-from"/imm32/subx-name
 8622     1/imm32/rm32-is-first-inout
 8623     2/imm32/r32-is-second-inout
 8624     0/imm32/no-imm32
 8625     0/imm32/no-disp32
 8626     0/imm32/output-is-write-only
 8627     _Primitive-subtract-mem-from-reg/imm32/next
 8628 _Primitive-subtract-mem-from-reg:
 8629     # var1/reg <- subtract var2 => 2b/subtract var2/rm32 var1/r32
 8630     "subtract"/imm32/name
 8631     Single-int-var-in-mem/imm32/inouts
 8632     Single-int-var-in-some-register/imm32/outputs
 8633     "2b/subtract"/imm32/subx-name
 8634     1/imm32/rm32-is-first-inout
 8635     3/imm32/r32-is-first-output
 8636     0/imm32/no-imm32
 8637     0/imm32/no-disp32
 8638     0/imm32/output-is-write-only
 8639     _Primitive-subtract-lit-from-reg/imm32/next
 8640 _Primitive-subtract-lit-from-reg:
 8641     # var1/reg <- subtract lit => 81 5/subop/subtract var1/rm32 lit/imm32
 8642     "subtract"/imm32/name
 8643     Single-lit-var/imm32/inouts
 8644     Single-int-var-in-some-register/imm32/outputs
 8645     "81 5/subop/subtract"/imm32/subx-name
 8646     3/imm32/rm32-is-first-output
 8647     0/imm32/no-r32
 8648     1/imm32/imm32-is-first-inout
 8649     0/imm32/no-disp32
 8650     0/imm32/output-is-write-only
 8651     _Primitive-subtract-lit-from-mem/imm32/next
 8652 _Primitive-subtract-lit-from-mem:
 8653     # subtract-from var1, lit => 81 5/subop/subtract var1/rm32 lit/imm32
 8654     "subtract-from"/imm32/name
 8655     Int-var-and-literal/imm32/inouts
 8656     0/imm32/outputs
 8657     "81 5/subop/subtract"/imm32/subx-name
 8658     1/imm32/rm32-is-first-inout
 8659     0/imm32/no-r32
 8660     2/imm32/imm32-is-first-inout
 8661     0/imm32/no-disp32
 8662     0/imm32/output-is-write-only
 8663     _Primitive-and-with-eax/imm32/next
 8664 # - and
 8665 _Primitive-and-with-eax:
 8666     # var/eax <- and lit => 25/and-with-eax lit/imm32
 8667     "and"/imm32/name
 8668     Single-lit-var/imm32/inouts
 8669     Single-int-var-in-eax/imm32/outputs
 8670     "25/and-with-eax"/imm32/subx-name
 8671     0/imm32/no-rm32
 8672     0/imm32/no-r32
 8673     1/imm32/imm32-is-first-inout
 8674     0/imm32/no-disp32
 8675     0/imm32/output-is-write-only
 8676     _Primitive-and-reg-with-reg/imm32/next
 8677 _Primitive-and-reg-with-reg:
 8678     # var1/reg <- and var2/reg => 21/and-with var1/rm32 var2/r32
 8679     "and"/imm32/name
 8680     Single-int-var-in-some-register/imm32/inouts
 8681     Single-int-var-in-some-register/imm32/outputs
 8682     "21/and-with"/imm32/subx-name
 8683     3/imm32/rm32-is-first-output
 8684     1/imm32/r32-is-first-inout
 8685     0/imm32/no-imm32
 8686     0/imm32/no-disp32
 8687     0/imm32/output-is-write-only
 8688     _Primitive-and-reg-with-mem/imm32/next
 8689 _Primitive-and-reg-with-mem:
 8690     # and-with var1 var2/reg => 21/and-with var1 var2/r32
 8691     "and-with"/imm32/name
 8692     Two-args-int-stack-int-reg/imm32/inouts
 8693     0/imm32/outputs
 8694     "21/and-with"/imm32/subx-name
 8695     1/imm32/rm32-is-first-inout
 8696     2/imm32/r32-is-second-inout
 8697     0/imm32/no-imm32
 8698     0/imm32/no-disp32
 8699     0/imm32/output-is-write-only
 8700     _Primitive-and-mem-with-reg/imm32/next
 8701 _Primitive-and-mem-with-reg:
 8702     # var1/reg <- and var2 => 23/and var2/rm32 var1/r32
 8703     "and"/imm32/name
 8704     Single-int-var-in-mem/imm32/inouts
 8705     Single-int-var-in-some-register/imm32/outputs
 8706     "23/and"/imm32/subx-name
 8707     1/imm32/rm32-is-first-inout
 8708     3/imm32/r32-is-first-output
 8709     0/imm32/no-imm32
 8710     0/imm32/no-disp32
 8711     0/imm32/output-is-write-only
 8712     _Primitive-and-lit-with-reg/imm32/next
 8713 _Primitive-and-lit-with-reg:
 8714     # var1/reg <- and lit => 81 4/subop/and var1/rm32 lit/imm32
 8715     "and"/imm32/name
 8716     Single-lit-var/imm32/inouts
 8717     Single-int-var-in-some-register/imm32/outputs
 8718     "81 4/subop/and"/imm32/subx-name
 8719     3/imm32/rm32-is-first-output
 8720     0/imm32/no-r32
 8721     1/imm32/imm32-is-first-inout
 8722     0/imm32/no-disp32
 8723     0/imm32/output-is-write-only
 8724     _Primitive-and-lit-with-mem/imm32/next
 8725 _Primitive-and-lit-with-mem:
 8726     # and-with var1, lit => 81 4/subop/and var1/rm32 lit/imm32
 8727     "and-with"/imm32/name
 8728     Int-var-and-literal/imm32/inouts
 8729     0/imm32/outputs
 8730     "81 4/subop/and"/imm32/subx-name
 8731     1/imm32/rm32-is-first-inout
 8732     0/imm32/no-r32
 8733     2/imm32/imm32-is-first-inout
 8734     0/imm32/no-disp32
 8735     0/imm32/output-is-write-only
 8736     _Primitive-or-with-eax/imm32/next
 8737 # - or
 8738 _Primitive-or-with-eax:
 8739     # var/eax <- or lit => 0d/or-with-eax lit/imm32
 8740     "or"/imm32/name
 8741     Single-lit-var/imm32/inouts
 8742     Single-int-var-in-eax/imm32/outputs
 8743     "0d/or-with-eax"/imm32/subx-name
 8744     0/imm32/no-rm32
 8745     0/imm32/no-r32
 8746     1/imm32/imm32-is-first-inout
 8747     0/imm32/no-disp32
 8748     0/imm32/output-is-write-only
 8749     _Primitive-or-reg-with-reg/imm32/next
 8750 _Primitive-or-reg-with-reg:
 8751     # var1/reg <- or var2/reg => 09/or-with var1/rm32 var2/r32
 8752     "or"/imm32/name
 8753     Single-int-var-in-some-register/imm32/inouts
 8754     Single-int-var-in-some-register/imm32/outputs
 8755     "09/or-with"/imm32/subx-name
 8756     3/imm32/rm32-is-first-output
 8757     1/imm32/r32-is-first-inout
 8758     0/imm32/no-imm32
 8759     0/imm32/no-disp32
 8760     0/imm32/output-is-write-only
 8761     _Primitive-or-reg-with-mem/imm32/next
 8762 _Primitive-or-reg-with-mem:
 8763     # or-with var1 var2/reg => 09/or-with var1 var2/r32
 8764     "or-with"/imm32/name
 8765     Two-args-int-stack-int-reg/imm32/inouts
 8766     0/imm32/outputs
 8767     "09/or-with"/imm32/subx-name
 8768     1/imm32/rm32-is-first-inout
 8769     2/imm32/r32-is-second-inout
 8770     0/imm32/no-imm32
 8771     0/imm32/no-disp32
 8772     0/imm32/output-is-write-only
 8773     _Primitive-or-mem-with-reg/imm32/next
 8774 _Primitive-or-mem-with-reg:
 8775     # var1/reg <- or var2 => 0b/or var2/rm32 var1/r32
 8776     "or"/imm32/name
 8777     Single-int-var-in-mem/imm32/inouts
 8778     Single-int-var-in-some-register/imm32/outputs
 8779     "0b/or"/imm32/subx-name
 8780     1/imm32/rm32-is-first-inout
 8781     3/imm32/r32-is-first-output
 8782     0/imm32/no-imm32
 8783     0/imm32/no-disp32
 8784     0/imm32/output-is-write-only
 8785     _Primitive-or-lit-with-reg/imm32/next
 8786 _Primitive-or-lit-with-reg:
 8787     # var1/reg <- or lit => 81 1/subop/or var1/rm32 lit/imm32
 8788     "or"/imm32/name
 8789     Single-lit-var/imm32/inouts
 8790     Single-int-var-in-some-register/imm32/outputs
 8791     "81 1/subop/or"/imm32/subx-name
 8792     3/imm32/rm32-is-first-output
 8793     0/imm32/no-r32
 8794     1/imm32/imm32-is-first-inout
 8795     0/imm32/no-disp32
 8796     0/imm32/output-is-write-only
 8797     _Primitive-or-lit-with-mem/imm32/next
 8798 _Primitive-or-lit-with-mem:
 8799     # or-with var1, lit => 81 1/subop/or var1/rm32 lit/imm32
 8800     "or-with"/imm32/name
 8801     Int-var-and-literal/imm32/inouts
 8802     0/imm32/outputs
 8803     "81 1/subop/or"/imm32/subx-name
 8804     1/imm32/rm32-is-first-inout
 8805     0/imm32/no-r32
 8806     2/imm32/imm32-is-second-inout
 8807     0/imm32/no-disp32
 8808     0/imm32/output-is-write-only
 8809     _Primitive-xor-with-eax/imm32/next
 8810 # - xor
 8811 _Primitive-xor-with-eax:
 8812     # var/eax <- xor lit => 35/xor-with-eax lit/imm32
 8813     "xor"/imm32/name
 8814     Single-lit-var/imm32/inouts
 8815     Single-int-var-in-eax/imm32/outputs
 8816     "35/xor-with-eax"/imm32/subx-name
 8817     0/imm32/no-rm32
 8818     0/imm32/no-r32
 8819     1/imm32/imm32-is-first-inout
 8820     0/imm32/no-disp32
 8821     0/imm32/output-is-write-only
 8822     _Primitive-xor-reg-with-reg/imm32/next
 8823 _Primitive-xor-reg-with-reg:
 8824     # var1/reg <- xor var2/reg => 31/xor-with var1/rm32 var2/r32
 8825     "xor"/imm32/name
 8826     Single-int-var-in-some-register/imm32/inouts
 8827     Single-int-var-in-some-register/imm32/outputs
 8828     "31/xor-with"/imm32/subx-name
 8829     3/imm32/rm32-is-first-output
 8830     1/imm32/r32-is-first-inout
 8831     0/imm32/no-imm32
 8832     0/imm32/no-disp32
 8833     0/imm32/output-is-write-only
 8834     _Primitive-xor-reg-with-mem/imm32/next
 8835 _Primitive-xor-reg-with-mem:
 8836     # xor-with var1 var2/reg => 31/xor-with var1 var2/r32
 8837     "xor-with"/imm32/name
 8838     Two-args-int-stack-int-reg/imm32/inouts
 8839     0/imm32/outputs
 8840     "31/xor-with"/imm32/subx-name
 8841     1/imm32/rm32-is-first-inout
 8842     2/imm32/r32-is-second-inout
 8843     0/imm32/no-imm32
 8844     0/imm32/no-disp32
 8845     0/imm32/output-is-write-only
 8846     _Primitive-xor-mem-with-reg/imm32/next
 8847 _Primitive-xor-mem-with-reg:
 8848     # var1/reg <- xor var2 => 33/xor var2/rm32 var1/r32
 8849     "xor"/imm32/name
 8850     Single-int-var-in-mem/imm32/inouts
 8851     Single-int-var-in-some-register/imm32/outputs
 8852     "33/xor"/imm32/subx-name
 8853     1/imm32/rm32-is-first-inout
 8854     3/imm32/r32-is-first-output
 8855     0/imm32/no-imm32
 8856     0/imm32/no-disp32
 8857     0/imm32/output-is-write-only
 8858     _Primitive-xor-lit-with-reg/imm32/next
 8859 _Primitive-xor-lit-with-reg:
 8860     # var1/reg <- xor lit => 81 6/subop/xor var1/rm32 lit/imm32
 8861     "xor"/imm32/name
 8862     Single-lit-var/imm32/inouts
 8863     Single-int-var-in-some-register/imm32/outputs
 8864     "81 6/subop/xor"/imm32/subx-name
 8865     3/imm32/rm32-is-first-output
 8866     0/imm32/no-r32
 8867     1/imm32/imm32-is-first-inout
 8868     0/imm32/no-disp32
 8869     0/imm32/output-is-write-only
 8870     _Primitive-xor-lit-with-mem/imm32/next
 8871 _Primitive-xor-lit-with-mem:
 8872     # xor-with var1, lit => 81 6/subop/xor var1/rm32 lit/imm32
 8873     "xor-with"/imm32/name
 8874     Int-var-and-literal/imm32/inouts
 8875     0/imm32/outputs
 8876     "81 6/subop/xor"/imm32/subx-name
 8877     1/imm32/rm32-is-first-inout
 8878     0/imm32/no-r32
 8879     2/imm32/imm32-is-first-inout
 8880     0/imm32/no-disp32
 8881     0/imm32/output-is-write-only
 8882     _Primitive-copy-to-eax/imm32/next
 8883 # - copy
 8884 _Primitive-copy-to-eax:
 8885     # var/eax <- copy lit => b8/copy-to-eax lit/imm32
 8886     "copy"/imm32/name
 8887     Single-lit-var/imm32/inouts
 8888     Single-int-var-in-eax/imm32/outputs
 8889     "b8/copy-to-eax"/imm32/subx-name
 8890     0/imm32/no-rm32
 8891     0/imm32/no-r32
 8892     1/imm32/imm32-is-first-inout
 8893     0/imm32/no-disp32
 8894     1/imm32/output-is-write-only
 8895     _Primitive-copy-to-ecx/imm32/next
 8896 _Primitive-copy-to-ecx:
 8897     # var/ecx <- copy lit => b9/copy-to-ecx lit/imm32
 8898     "copy"/imm32/name
 8899     Single-lit-var/imm32/inouts
 8900     Single-int-var-in-ecx/imm32/outputs
 8901     "b9/copy-to-ecx"/imm32/subx-name
 8902     0/imm32/no-rm32
 8903     0/imm32/no-r32
 8904     1/imm32/imm32-is-first-inout
 8905     0/imm32/no-disp32
 8906     1/imm32/output-is-write-only
 8907     _Primitive-copy-to-edx/imm32/next
 8908 _Primitive-copy-to-edx:
 8909     # var/edx <- copy lit => ba/copy-to-edx lit/imm32
 8910     "copy"/imm32/name
 8911     Single-lit-var/imm32/inouts
 8912     Single-int-var-in-edx/imm32/outputs
 8913     "ba/copy-to-edx"/imm32/subx-name
 8914     0/imm32/no-rm32
 8915     0/imm32/no-r32
 8916     1/imm32/imm32-is-first-inout
 8917     0/imm32/no-disp32
 8918     1/imm32/output-is-write-only
 8919     _Primitive-copy-to-ebx/imm32/next
 8920 _Primitive-copy-to-ebx:
 8921     # var/ebx <- copy lit => bb/copy-to-ebx lit/imm32
 8922     "copy"/imm32/name
 8923     Single-lit-var/imm32/inouts
 8924     Single-int-var-in-ebx/imm32/outputs
 8925     "bb/copy-to-ebx"/imm32/subx-name
 8926     0/imm32/no-rm32
 8927     0/imm32/no-r32
 8928     1/imm32/imm32-is-first-inout
 8929     0/imm32/no-disp32
 8930     1/imm32/output-is-write-only
 8931     _Primitive-copy-to-esi/imm32/next
 8932 _Primitive-copy-to-esi:
 8933     # var/esi <- copy lit => be/copy-to-esi lit/imm32
 8934     "copy"/imm32/name
 8935     Single-lit-var/imm32/inouts
 8936     Single-int-var-in-esi/imm32/outputs
 8937     "be/copy-to-esi"/imm32/subx-name
 8938     0/imm32/no-rm32
 8939     0/imm32/no-r32
 8940     1/imm32/imm32-is-first-inout
 8941     0/imm32/no-disp32
 8942     1/imm32/output-is-write-only
 8943     _Primitive-copy-to-edi/imm32/next
 8944 _Primitive-copy-to-edi:
 8945     # var/edi <- copy lit => bf/copy-to-edi lit/imm32
 8946     "copy"/imm32/name
 8947     Single-lit-var/imm32/inouts
 8948     Single-int-var-in-edi/imm32/outputs
 8949     "bf/copy-to-edi"/imm32/subx-name
 8950     0/imm32/no-rm32
 8951     0/imm32/no-r32
 8952     1/imm32/imm32-is-first-inout
 8953     0/imm32/no-disp32
 8954     1/imm32/output-is-write-only
 8955     _Primitive-copy-reg-to-reg/imm32/next
 8956 _Primitive-copy-reg-to-reg:
 8957     # var1/reg <- copy var2/reg => 89/<- var1/rm32 var2/r32
 8958     "copy"/imm32/name
 8959     Single-int-var-in-some-register/imm32/inouts
 8960     Single-int-var-in-some-register/imm32/outputs
 8961     "89/<-"/imm32/subx-name
 8962     3/imm32/rm32-is-first-output
 8963     1/imm32/r32-is-first-inout
 8964     0/imm32/no-imm32
 8965     0/imm32/no-disp32
 8966     1/imm32/output-is-write-only
 8967     _Primitive-copy-reg-to-mem/imm32/next
 8968 _Primitive-copy-reg-to-mem:
 8969     # copy-to var1 var2/reg => 89/<- var1 var2/r32
 8970     "copy-to"/imm32/name
 8971     Two-args-int-stack-int-reg/imm32/inouts
 8972     0/imm32/outputs
 8973     "89/<-"/imm32/subx-name
 8974     1/imm32/rm32-is-first-inout
 8975     2/imm32/r32-is-second-inout
 8976     0/imm32/no-imm32
 8977     0/imm32/no-disp32
 8978     1/imm32/output-is-write-only
 8979     _Primitive-copy-mem-to-reg/imm32/next
 8980 _Primitive-copy-mem-to-reg:
 8981     # var1/reg <- copy var2 => 8b/-> var2/rm32 var1/r32
 8982     "copy"/imm32/name
 8983     Single-int-var-in-mem/imm32/inouts
 8984     Single-int-var-in-some-register/imm32/outputs
 8985     "8b/->"/imm32/subx-name
 8986     1/imm32/rm32-is-first-inout
 8987     3/imm32/r32-is-first-output
 8988     0/imm32/no-imm32
 8989     0/imm32/no-disp32
 8990     1/imm32/output-is-write-only
 8991     _Primitive-copy-lit-to-reg/imm32/next
 8992 _Primitive-copy-lit-to-reg:
 8993     # var1/reg <- copy lit => c7 0/subop/copy var1/rm32 lit/imm32
 8994     "copy"/imm32/name
 8995     Single-lit-var/imm32/inouts
 8996     Single-int-var-in-some-register/imm32/outputs
 8997     "c7 0/subop/copy"/imm32/subx-name
 8998     3/imm32/rm32-is-first-output
 8999     0/imm32/no-r32
 9000     1/imm32/imm32-is-first-inout
 9001     0/imm32/no-disp32
 9002     1/imm32/output-is-write-only
 9003     _Primitive-copy-lit-to-mem/imm32/next
 9004 _Primitive-copy-lit-to-mem:
 9005     # copy-to var1, lit => c7 0/subop/copy var1/rm32 lit/imm32
 9006     "copy-to"/imm32/name
 9007     Int-var-and-literal/imm32/inouts
 9008     0/imm32/outputs
 9009     "c7 0/subop/copy"/imm32/subx-name
 9010     1/imm32/rm32-is-first-inout
 9011     0/imm32/no-r32
 9012     2/imm32/imm32-is-first-inout
 9013     0/imm32/no-disp32
 9014     1/imm32/output-is-write-only
 9015     _Primitive-address/imm32/next
 9016 # - address
 9017 _Primitive-address:
 9018     # var1/reg <- address var2 => 8d/copy-address var2/rm32 var1/r32
 9019     "address"/imm32/name
 9020     Single-int-var-in-mem/imm32/inouts
 9021     Single-addr-var-in-some-register/imm32/outputs
 9022     "8d/copy-address"/imm32/subx-name
 9023     1/imm32/rm32-is-first-inout
 9024     3/imm32/r32-is-first-output
 9025     0/imm32/no-imm32
 9026     0/imm32/no-disp32
 9027     1/imm32/output-is-write-only
 9028     _Primitive-compare-mem-with-reg/imm32/next
 9029 # - compare
 9030 _Primitive-compare-mem-with-reg:
 9031     # compare var1 var2/reg => 39/compare var1/rm32 var2/r32
 9032     "compare"/imm32/name
 9033     Two-args-int-stack-int-reg/imm32/inouts
 9034     0/imm32/outputs
 9035     "39/compare->"/imm32/subx-name
 9036     1/imm32/rm32-is-first-inout
 9037     2/imm32/r32-is-second-inout
 9038     0/imm32/no-imm32
 9039     0/imm32/no-disp32
 9040     0/imm32/output-is-write-only
 9041     _Primitive-compare-reg-with-mem/imm32/next
 9042 _Primitive-compare-reg-with-mem:
 9043     # compare var1/reg var2 => 3b/compare<- var2/rm32 var1/r32
 9044     "compare"/imm32/name
 9045     Two-args-int-reg-int-stack/imm32/inouts
 9046     0/imm32/outputs
 9047     "3b/compare<-"/imm32/subx-name
 9048     2/imm32/rm32-is-second-inout
 9049     1/imm32/r32-is-first-inout
 9050     0/imm32/no-imm32
 9051     0/imm32/no-disp32
 9052     0/imm32/output-is-write-only
 9053     _Primitive-compare-eax-with-literal/imm32/next
 9054 _Primitive-compare-eax-with-literal:
 9055     # compare var1/eax n => 3d/compare-eax-with n/imm32
 9056     "compare"/imm32/name
 9057     Two-args-int-eax-int-literal/imm32/inouts
 9058     0/imm32/outputs
 9059     "3d/compare-eax-with"/imm32/subx-name
 9060     0/imm32/no-rm32
 9061     0/imm32/no-r32
 9062     2/imm32/imm32-is-second-inout
 9063     0/imm32/no-disp32
 9064     0/imm32/output-is-write-only
 9065     _Primitive-compare-reg-with-literal/imm32/next
 9066 _Primitive-compare-reg-with-literal:
 9067     # compare var1/reg n => 81 7/subop/compare %reg n/imm32
 9068     "compare"/imm32/name
 9069     Int-var-in-register-and-literal/imm32/inouts
 9070     0/imm32/outputs
 9071     "81 7/subop/compare"/imm32/subx-name
 9072     1/imm32/rm32-is-first-inout
 9073     0/imm32/no-r32
 9074     2/imm32/imm32-is-second-inout
 9075     0/imm32/no-disp32
 9076     0/imm32/output-is-write-only
 9077     _Primitive-compare-mem-with-literal/imm32/next
 9078 _Primitive-compare-mem-with-literal:
 9079     # compare var1 n => 81 7/subop/compare *(ebp+___) n/imm32
 9080     "compare"/imm32/name
 9081     Int-var-and-literal/imm32/inouts
 9082     0/imm32/outputs
 9083     "81 7/subop/compare"/imm32/subx-name
 9084     1/imm32/rm32-is-first-inout
 9085     0/imm32/no-r32
 9086     2/imm32/imm32-is-second-inout
 9087     0/imm32/no-disp32
 9088     0/imm32/output-is-write-only
 9089     _Primitive-multiply-reg-by-mem/imm32/next
 9090 # - multiply
 9091 _Primitive-multiply-reg-by-mem:
 9092     # var1/reg <- multiply var2 => 0f af/multiply var2/rm32 var1/r32
 9093     "multiply"/imm32/name
 9094     Single-int-var-in-mem/imm32/inouts
 9095     Single-int-var-in-some-register/imm32/outputs
 9096     "0f af/multiply"/imm32/subx-name
 9097     1/imm32/rm32-is-first-inout
 9098     3/imm32/r32-is-first-output
 9099     0/imm32/no-imm32
 9100     0/imm32/no-disp32
 9101     0/imm32/output-is-write-only
 9102     _Primitive-break-if-addr</imm32/next
 9103 # - branches
 9104 _Primitive-break-if-addr<:
 9105     "break-if-addr<"/imm32/name
 9106     0/imm32/inouts
 9107     0/imm32/outputs
 9108     "0f 82/jump-if-addr< break/disp32"/imm32/subx-name
 9109     0/imm32/no-rm32
 9110     0/imm32/no-r32
 9111     0/imm32/no-imm32
 9112     0/imm32/no-disp32
 9113     0/imm32/no-output
 9114     _Primitive-break-if-addr>=/imm32/next
 9115 _Primitive-break-if-addr>=:
 9116     "break-if-addr>="/imm32/name
 9117     0/imm32/inouts
 9118     0/imm32/outputs
 9119     "0f 83/jump-if-addr>= break/disp32"/imm32/subx-name
 9120     0/imm32/no-rm32
 9121     0/imm32/no-r32
 9122     0/imm32/no-imm32
 9123     0/imm32/no-disp32
 9124     0/imm32/no-output
 9125     _Primitive-break-if-=/imm32/next
 9126 _Primitive-break-if-=:
 9127     "break-if-="/imm32/name
 9128     0/imm32/inouts
 9129     0/imm32/outputs
 9130     "0f 84/jump-if-= break/disp32"/imm32/subx-name
 9131     0/imm32/no-rm32
 9132     0/imm32/no-r32
 9133     0/imm32/no-imm32
 9134     0/imm32/no-disp32
 9135     0/imm32/no-output
 9136     _Primitive-break-if-!=/imm32/next
 9137 _Primitive-break-if-!=:
 9138     "break-if-!="/imm32/name
 9139     0/imm32/inouts
 9140     0/imm32/outputs
 9141     "0f 85/jump-if-!= break/disp32"/imm32/subx-name
 9142     0/imm32/no-rm32
 9143     0/imm32/no-r32
 9144     0/imm32/no-imm32
 9145     0/imm32/no-disp32
 9146     0/imm32/no-output
 9147     _Primitive-break-if-addr<=/imm32/next
 9148 _Primitive-break-if-addr<=:
 9149     "break-if-addr<="/imm32/name
 9150     0/imm32/inouts
 9151     0/imm32/outputs
 9152     "0f 86/jump-if-addr<= break/disp32"/imm32/subx-name
 9153     0/imm32/no-rm32
 9154     0/imm32/no-r32
 9155     0/imm32/no-imm32
 9156     0/imm32/no-disp32
 9157     0/imm32/no-output
 9158     _Primitive-break-if-addr>/imm32/next
 9159 _Primitive-break-if-addr>:
 9160     "break-if-addr>"/imm32/name
 9161     0/imm32/inouts
 9162     0/imm32/outputs
 9163     "0f 87/jump-if-addr> break/disp32"/imm32/subx-name
 9164     0/imm32/no-rm32
 9165     0/imm32/no-r32
 9166     0/imm32/no-imm32
 9167     0/imm32/no-disp32
 9168     0/imm32/no-output
 9169     _Primitive-break-if-</imm32/next
 9170 _Primitive-break-if-<:
 9171     "break-if-<"/imm32/name
 9172     0/imm32/inouts
 9173     0/imm32/outputs
 9174     "0f 8c/jump-if-< break/disp32"/imm32/subx-name
 9175     0/imm32/no-rm32
 9176     0/imm32/no-r32
 9177     0/imm32/no-imm32
 9178     0/imm32/no-disp32
 9179     0/imm32/no-output
 9180     _Primitive-break-if->=/imm32/next
 9181 _Primitive-break-if->=:
 9182     "break-if->="/imm32/name
 9183     0/imm32/inouts
 9184     0/imm32/outputs
 9185     "0f 8d/jump-if->= break/disp32"/imm32/subx-name
 9186     0/imm32/no-rm32
 9187     0/imm32/no-r32
 9188     0/imm32/no-imm32
 9189     0/imm32/no-disp32
 9190     0/imm32/no-output
 9191     _Primitive-break-if-<=/imm32/next
 9192 _Primitive-break-if-<=:
 9193     "break-if-<="/imm32/name
 9194     0/imm32/inouts
 9195     0/imm32/outputs
 9196     "0f 8e/jump-if-<= break/disp32"/imm32/subx-name
 9197     0/imm32/no-rm32
 9198     0/imm32/no-r32
 9199     0/imm32/no-imm32
 9200     0/imm32/no-disp32
 9201     0/imm32/no-output
 9202     _Primitive-break-if->/imm32/next
 9203 _Primitive-break-if->:
 9204     "break-if->"/imm32/name
 9205     0/imm32/inouts
 9206     0/imm32/outputs
 9207     "0f 8f/jump-if-> break/disp32"/imm32/subx-name
 9208     0/imm32/no-rm32
 9209     0/imm32/no-r32
 9210     0/imm32/no-imm32
 9211     0/imm32/no-disp32
 9212     0/imm32/no-output
 9213     _Primitive-break/imm32/next
 9214 _Primitive-break:
 9215     "break"/imm32/name
 9216     0/imm32/inouts
 9217     0/imm32/outputs
 9218     "e9/jump break/disp32"/imm32/subx-name
 9219     0/imm32/no-rm32
 9220     0/imm32/no-r32
 9221     0/imm32/no-imm32
 9222     0/imm32/no-disp32
 9223     0/imm32/no-output
 9224     _Primitive-loop-if-addr</imm32/next
 9225 _Primitive-loop-if-addr<:
 9226     "loop-if-addr<"/imm32/name
 9227     0/imm32/inouts
 9228     0/imm32/outputs
 9229     "0f 82/jump-if-addr< loop/disp32"/imm32/subx-name
 9230     0/imm32/no-rm32
 9231     0/imm32/no-r32
 9232     0/imm32/no-imm32
 9233     0/imm32/no-disp32
 9234     0/imm32/no-output
 9235     _Primitive-loop-if-addr>=/imm32/next
 9236 _Primitive-loop-if-addr>=:
 9237     "loop-if-addr>="/imm32/name
 9238     0/imm32/inouts
 9239     0/imm32/outputs
 9240     "0f 83/jump-if-addr>= loop/disp32"/imm32/subx-name
 9241     0/imm32/no-rm32
 9242     0/imm32/no-r32
 9243     0/imm32/no-imm32
 9244     0/imm32/no-disp32
 9245     0/imm32/no-output
 9246     _Primitive-loop-if-=/imm32/next
 9247 _Primitive-loop-if-=:
 9248     "loop-if-="/imm32/name
 9249     0/imm32/inouts
 9250     0/imm32/outputs
 9251     "0f 84/jump-if-= loop/disp32"/imm32/subx-name
 9252     0/imm32/no-rm32
 9253     0/imm32/no-r32
 9254     0/imm32/no-imm32
 9255     0/imm32/no-disp32
 9256     0/imm32/no-output
 9257     _Primitive-loop-if-!=/imm32/next
 9258 _Primitive-loop-if-!=:
 9259     "loop-if-!="/imm32/name
 9260     0/imm32/inouts
 9261     0/imm32/outputs
 9262     "0f 85/jump-if-!= loop/disp32"/imm32/subx-name
 9263     0/imm32/no-rm32
 9264     0/imm32/no-r32
 9265     0/imm32/no-imm32
 9266     0/imm32/no-disp32
 9267     0/imm32/no-output
 9268     _Primitive-loop-if-addr<=/imm32/next
 9269 _Primitive-loop-if-addr<=:
 9270     "loop-if-addr<="/imm32/name
 9271     0/imm32/inouts
 9272     0/imm32/outputs
 9273     "0f 86/jump-if-addr<= loop/disp32"/imm32/subx-name
 9274     0/imm32/no-rm32
 9275     0/imm32/no-r32
 9276     0/imm32/no-imm32
 9277     0/imm32/no-disp32
 9278     0/imm32/no-output
 9279     _Primitive-loop-if-addr>/imm32/next
 9280 _Primitive-loop-if-addr>:
 9281     "loop-if-addr>"/imm32/name
 9282     0/imm32/inouts
 9283     0/imm32/outputs
 9284     "0f 87/jump-if-addr> loop/disp32"/imm32/subx-name
 9285     0/imm32/no-rm32
 9286     0/imm32/no-r32
 9287     0/imm32/no-imm32
 9288     0/imm32/no-disp32
 9289     0/imm32/no-output
 9290     _Primitive-loop-if-</imm32/next
 9291 _Primitive-loop-if-<:
 9292     "loop-if-<"/imm32/name
 9293     0/imm32/inouts
 9294     0/imm32/outputs
 9295     "0f 8c/jump-if-< loop/disp32"/imm32/subx-name
 9296     0/imm32/no-rm32
 9297     0/imm32/no-r32
 9298     0/imm32/no-imm32
 9299     0/imm32/no-disp32
 9300     0/imm32/no-output
 9301     _Primitive-loop-if->=/imm32/next
 9302 _Primitive-loop-if->=:
 9303     "loop-if->="/imm32/name
 9304     0/imm32/inouts
 9305     0/imm32/outputs
 9306     "0f 8d/jump-if->= loop/disp32"/imm32/subx-name
 9307     0/imm32/no-rm32
 9308     0/imm32/no-r32
 9309     0/imm32/no-imm32
 9310     0/imm32/no-disp32
 9311     0/imm32/no-output
 9312     _Primitive-loop-if-<=/imm32/next
 9313 _Primitive-loop-if-<=:
 9314     "loop-if-<="/imm32/name
 9315     0/imm32/inouts
 9316     0/imm32/outputs
 9317     "0f 8e/jump-if-<= loop/disp32"/imm32/subx-name
 9318     0/imm32/no-rm32
 9319     0/imm32/no-r32
 9320     0/imm32/no-imm32
 9321     0/imm32/no-disp32
 9322     0/imm32/no-output
 9323     _Primitive-loop-if->/imm32/next
 9324 _Primitive-loop-if->:
 9325     "loop-if->"/imm32/name
 9326     0/imm32/inouts
 9327     0/imm32/outputs
 9328     "0f 8f/jump-if-> loop/disp32"/imm32/subx-name
 9329     0/imm32/no-rm32
 9330     0/imm32/no-r32
 9331     0/imm32/no-imm32
 9332     0/imm32/no-disp32
 9333     0/imm32/no-output
 9334     _Primitive-loop/imm32/next  # we probably don't need an unconditional break
 9335 _Primitive-loop:
 9336     "loop"/imm32/name
 9337     0/imm32/inouts
 9338     0/imm32/outputs
 9339     "e9/jump loop/disp32"/imm32/subx-name
 9340     0/imm32/no-rm32
 9341     0/imm32/no-r32
 9342     0/imm32/no-imm32
 9343     0/imm32/no-disp32
 9344     0/imm32/no-output
 9345     _Primitive-break-if-addr<-named/imm32/next
 9346 # - branches to named blocks
 9347 _Primitive-break-if-addr<-named:
 9348     "break-if-addr<"/imm32/name
 9349     Single-lit-var/imm32/inouts
 9350     0/imm32/outputs
 9351     "0f 82/jump-if-addr<"/imm32/subx-name
 9352     0/imm32/no-rm32
 9353     0/imm32/no-r32
 9354     0/imm32/no-imm32
 9355     1/imm32/disp32-is-first-inout
 9356     0/imm32/no-output
 9357     _Primitive-break-if-addr>=-named/imm32/next
 9358 _Primitive-break-if-addr>=-named:
 9359     "break-if-addr>="/imm32/name
 9360     Single-lit-var/imm32/inouts
 9361     0/imm32/outputs
 9362     "0f 83/jump-if-addr>="/imm32/subx-name
 9363     0/imm32/no-rm32
 9364     0/imm32/no-r32
 9365     0/imm32/no-imm32
 9366     1/imm32/disp32-is-first-inout
 9367     0/imm32/no-output
 9368     _Primitive-break-if-=-named/imm32/next
 9369 _Primitive-break-if-=-named:
 9370     "break-if-="/imm32/name
 9371     Single-lit-var/imm32/inouts
 9372     0/imm32/outputs
 9373     "0f 84/jump-if-="/imm32/subx-name
 9374     0/imm32/no-rm32
 9375     0/imm32/no-r32
 9376     0/imm32/no-imm32
 9377     1/imm32/disp32-is-first-inout
 9378     0/imm32/no-output
 9379     _Primitive-break-if-!=-named/imm32/next
 9380 _Primitive-break-if-!=-named:
 9381     "break-if-!="/imm32/name
 9382     Single-lit-var/imm32/inouts
 9383     0/imm32/outputs
 9384     "0f 85/jump-if-!="/imm32/subx-name
 9385     0/imm32/no-rm32
 9386     0/imm32/no-r32
 9387     0/imm32/no-imm32
 9388     1/imm32/disp32-is-first-inout
 9389     0/imm32/no-output
 9390     _Primitive-break-if-addr<=-named/imm32/next
 9391 _Primitive-break-if-addr<=-named:
 9392     "break-if-addr<="/imm32/name
 9393     Single-lit-var/imm32/inouts
 9394     0/imm32/outputs
 9395     "0f 86/jump-if-addr<="/imm32/subx-name
 9396     0/imm32/no-rm32
 9397     0/imm32/no-r32
 9398     0/imm32/no-imm32
 9399     1/imm32/disp32-is-first-inout
 9400     0/imm32/no-output
 9401     _Primitive-break-if-addr>-named/imm32/next
 9402 _Primitive-break-if-addr>-named:
 9403     "break-if-addr>"/imm32/name
 9404     Single-lit-var/imm32/inouts
 9405     0/imm32/outputs
 9406     "0f 87/jump-if-addr>"/imm32/subx-name
 9407     0/imm32/no-rm32
 9408     0/imm32/no-r32
 9409     0/imm32/no-imm32
 9410     1/imm32/disp32-is-first-inout
 9411     0/imm32/no-output
 9412     _Primitive-break-if-<-named/imm32/next
 9413 _Primitive-break-if-<-named:
 9414     "break-if-<"/imm32/name
 9415     Single-lit-var/imm32/inouts
 9416     0/imm32/outputs
 9417     "0f 8c/jump-if-<"/imm32/subx-name
 9418     0/imm32/no-rm32
 9419     0/imm32/no-r32
 9420     0/imm32/no-imm32
 9421     1/imm32/disp32-is-first-inout
 9422     0/imm32/no-output
 9423     _Primitive-break-if->=-named/imm32/next
 9424 _Primitive-break-if->=-named:
 9425     "break-if->="/imm32/name
 9426     Single-lit-var/imm32/inouts
 9427     0/imm32/outputs
 9428     "0f 8d/jump-if->="/imm32/subx-name
 9429     0/imm32/no-rm32
 9430     0/imm32/no-r32
 9431     0/imm32/no-imm32
 9432     1/imm32/disp32-is-first-inout
 9433     0/imm32/no-output
 9434     _Primitive-break-if-<=-named/imm32/next
 9435 _Primitive-break-if-<=-named:
 9436     "break-if-<="/imm32/name
 9437     Single-lit-var/imm32/inouts
 9438     0/imm32/outputs
 9439     "0f 8e/jump-if-<="/imm32/subx-name
 9440     0/imm32/no-rm32
 9441     0/imm32/no-r32
 9442     0/imm32/no-imm32
 9443     1/imm32/disp32-is-first-inout
 9444     0/imm32/no-output
 9445     _Primitive-break-if->-named/imm32/next
 9446 _Primitive-break-if->-named:
 9447     "break-if->"/imm32/name
 9448     Single-lit-var/imm32/inouts
 9449     0/imm32/outputs
 9450     "0f 8f/jump-if->"/imm32/subx-name
 9451     0/imm32/no-rm32
 9452     0/imm32/no-r32
 9453     0/imm32/no-imm32
 9454     1/imm32/disp32-is-first-inout
 9455     0/imm32/no-output
 9456     _Primitive-break-named/imm32/next
 9457 _Primitive-break-named:
 9458     "break"/imm32/name
 9459     Single-lit-var/imm32/inouts
 9460     0/imm32/outputs
 9461     "e9/jump"/imm32/subx-name
 9462     0/imm32/no-rm32
 9463     0/imm32/no-r32
 9464     0/imm32/no-imm32
 9465     1/imm32/disp32-is-first-inout
 9466     0/imm32/no-output
 9467     _Primitive-loop-if-addr<-named/imm32/next
 9468 _Primitive-loop-if-addr<-named:
 9469     "loop-if-addr<"/imm32/name
 9470     Single-lit-var/imm32/inouts
 9471     0/imm32/outputs
 9472     "0f 82/jump-if-addr<"/imm32/subx-name
 9473     0/imm32/no-rm32
 9474     0/imm32/no-r32
 9475     0/imm32/no-imm32
 9476     1/imm32/disp32-is-first-inout
 9477     0/imm32/no-output
 9478     _Primitive-loop-if-addr>=-named/imm32/next
 9479 _Primitive-loop-if-addr>=-named:
 9480     "loop-if-addr>="/imm32/name
 9481     Single-lit-var/imm32/inouts
 9482     0/imm32/outputs
 9483     "0f 83/jump-if-addr>="/imm32/subx-name
 9484     0/imm32/no-rm32
 9485     0/imm32/no-r32
 9486     0/imm32/no-imm32
 9487     1/imm32/disp32-is-first-inout
 9488     0/imm32/no-output
 9489     _Primitive-loop-if-=-named/imm32/next
 9490 _Primitive-loop-if-=-named:
 9491     "loop-if-="/imm32/name
 9492     Single-lit-var/imm32/inouts
 9493     0/imm32/outputs
 9494     "0f 84/jump-if-="/imm32/subx-name
 9495     0/imm32/no-rm32
 9496     0/imm32/no-r32
 9497     0/imm32/no-imm32
 9498     1/imm32/disp32-is-first-inout
 9499     0/imm32/no-output
 9500     _Primitive-loop-if-!=-named/imm32/next
 9501 _Primitive-loop-if-!=-named:
 9502     "loop-if-!="/imm32/name
 9503     Single-lit-var/imm32/inouts
 9504     0/imm32/outputs
 9505     "0f 85/jump-if-!="/imm32/subx-name
 9506     0/imm32/no-rm32
 9507     0/imm32/no-r32
 9508     0/imm32/no-imm32
 9509     1/imm32/disp32-is-first-inout
 9510     0/imm32/no-output
 9511     _Primitive-loop-if-addr<=-named/imm32/next
 9512 _Primitive-loop-if-addr<=-named:
 9513     "loop-if-addr<="/imm32/name
 9514     Single-lit-var/imm32/inouts
 9515     0/imm32/outputs
 9516     "0f 86/jump-if-addr<="/imm32/subx-name
 9517     0/imm32/no-rm32
 9518     0/imm32/no-r32
 9519     0/imm32/no-imm32
 9520     1/imm32/disp32-is-first-inout
 9521     0/imm32/no-output
 9522     _Primitive-loop-if-addr>-named/imm32/next
 9523 _Primitive-loop-if-addr>-named:
 9524     "loop-if-addr>"/imm32/name
 9525     Single-lit-var/imm32/inouts
 9526     0/imm32/outputs
 9527     "0f 87/jump-if-addr>"/imm32/subx-name
 9528     0/imm32/no-rm32
 9529     0/imm32/no-r32
 9530     0/imm32/no-imm32
 9531     1/imm32/disp32-is-first-inout
 9532     0/imm32/no-output
 9533     _Primitive-loop-if-<-named/imm32/next
 9534 _Primitive-loop-if-<-named:
 9535     "loop-if-<"/imm32/name
 9536     Single-lit-var/imm32/inouts
 9537     0/imm32/outputs
 9538     "0f 8c/jump-if-<"/imm32/subx-name
 9539     0/imm32/no-rm32
 9540     0/imm32/no-r32
 9541     0/imm32/no-imm32
 9542     1/imm32/disp32-is-first-inout
 9543     0/imm32/no-output
 9544     _Primitive-loop-if->=-named/imm32/next
 9545 _Primitive-loop-if->=-named:
 9546     "loop-if->="/imm32/name
 9547     Single-lit-var/imm32/inouts
 9548     0/imm32/outputs
 9549     "0f 8d/jump-if->="/imm32/subx-name
 9550     0/imm32/no-rm32
 9551     0/imm32/no-r32
 9552     0/imm32/no-imm32
 9553     1/imm32/disp32-is-first-inout
 9554     0/imm32/no-output
 9555     _Primitive-loop-if-<=-named/imm32/next
 9556 _Primitive-loop-if-<=-named:
 9557     "loop-if-<="/imm32/name
 9558     Single-lit-var/imm32/inouts
 9559     0/imm32/outputs
 9560     "0f 8e/jump-if-<="/imm32/subx-name
 9561     0/imm32/no-rm32
 9562     0/imm32/no-r32
 9563     0/imm32/no-imm32
 9564     1/imm32/disp32-is-first-inout
 9565     0/imm32/no-output
 9566     _Primitive-loop-if->-named/imm32/next
 9567 _Primitive-loop-if->-named:
 9568     "loop-if->"/imm32/name
 9569     Single-lit-var/imm32/inouts
 9570     0/imm32/outputs
 9571     "0f 8f/jump-if->"/imm32/subx-name
 9572     0/imm32/no-rm32
 9573     0/imm32/no-r32
 9574     0/imm32/no-imm32
 9575     1/imm32/disp32-is-first-inout
 9576     0/imm32/no-output
 9577     _Primitive-loop-named/imm32/next  # we probably don't need an unconditional break
 9578 _Primitive-loop-named:
 9579     "loop"/imm32/name
 9580     Single-lit-var/imm32/inouts
 9581     0/imm32/outputs
 9582     "e9/jump"/imm32/subx-name
 9583     0/imm32/no-rm32
 9584     0/imm32/no-r32
 9585     0/imm32/no-imm32
 9586     1/imm32/disp32-is-first-inout
 9587     0/imm32/no-output
 9588     0/imm32/next
 9589 
 9590 Single-int-var-in-mem:
 9591     Int-var-in-mem/imm32
 9592     0/imm32/next
 9593 
 9594 Int-var-in-mem:
 9595     "arg1"/imm32/name
 9596     Type-int/imm32
 9597     1/imm32/some-block-depth
 9598     1/imm32/some-stack-offset
 9599     0/imm32/no-register
 9600 
 9601 Two-args-int-stack-int-reg:
 9602     Int-var-in-mem/imm32
 9603     Single-int-var-in-some-register/imm32/next
 9604 
 9605 Two-args-int-reg-int-stack:
 9606     Int-var-in-some-register/imm32
 9607     Single-int-var-in-mem/imm32/next
 9608 
 9609 Two-args-int-eax-int-literal:
 9610     Int-var-in-eax/imm32
 9611     Single-lit-var/imm32/next
 9612 
 9613 Int-var-and-literal:
 9614     Int-var-in-mem/imm32
 9615     Single-lit-var/imm32/next
 9616 
 9617 Int-var-in-register-and-literal:
 9618     Int-var-in-some-register/imm32
 9619     Single-lit-var/imm32/next
 9620 
 9621 Single-int-var-in-some-register:
 9622     Int-var-in-some-register/imm32
 9623     0/imm32/next
 9624 
 9625 Single-addr-var-in-some-register:
 9626     Addr-var-in-some-register/imm32
 9627     0/imm32/next
 9628 
 9629 Int-var-in-some-register:
 9630     "arg1"/imm32/name
 9631     Type-int/imm32
 9632     1/imm32/some-block-depth
 9633     0/imm32/no-stack-offset
 9634     Any-register/imm32
 9635 
 9636 Addr-var-in-some-register:
 9637     "arg1"/imm32/name
 9638     Type-addr/imm32
 9639     1/imm32/some-block-depth
 9640     0/imm32/no-stack-offset
 9641     Any-register/imm32
 9642 
 9643 Single-int-var-in-eax:
 9644     Int-var-in-eax/imm32
 9645     0/imm32/next
 9646 
 9647 Int-var-in-eax:
 9648     "arg1"/imm32/name
 9649     Type-int/imm32
 9650     1/imm32/some-block-depth
 9651     0/imm32/no-stack-offset
 9652     "eax"/imm32/register
 9653 
 9654 Single-int-var-in-ecx:
 9655     Int-var-in-ecx/imm32
 9656     0/imm32/next
 9657 
 9658 Int-var-in-ecx:
 9659     "arg1"/imm32/name
 9660     Type-int/imm32
 9661     1/imm32/some-block-depth
 9662     0/imm32/no-stack-offset
 9663     "ecx"/imm32/register
 9664 
 9665 Single-int-var-in-edx:
 9666     Int-var-in-edx/imm32
 9667     0/imm32/next
 9668 
 9669 Int-var-in-edx:
 9670     "arg1"/imm32/name
 9671     Type-int/imm32
 9672     1/imm32/some-block-depth
 9673     0/imm32/no-stack-offset
 9674     "edx"/imm32/register
 9675 
 9676 Single-int-var-in-ebx:
 9677     Int-var-in-ebx/imm32
 9678     0/imm32/next
 9679 
 9680 Int-var-in-ebx:
 9681     "arg1"/imm32/name
 9682     Type-int/imm32
 9683     1/imm32/some-block-depth
 9684     0/imm32/no-stack-offset
 9685     "ebx"/imm32/register
 9686 
 9687 Single-int-var-in-esi:
 9688     Int-var-in-esi/imm32
 9689     0/imm32/next
 9690 
 9691 Int-var-in-esi:
 9692     "arg1"/imm32/name
 9693     Type-int/imm32
 9694     1/imm32/some-block-depth
 9695     0/imm32/no-stack-offset
 9696     "esi"/imm32/register
 9697 
 9698 Single-int-var-in-edi:
 9699     Int-var-in-edi/imm32
 9700     0/imm32/next
 9701 
 9702 Int-var-in-edi:
 9703     "arg1"/imm32/name
 9704     Type-int/imm32
 9705     1/imm32/some-block-depth
 9706     0/imm32/no-stack-offset
 9707     "edi"/imm32/register
 9708 
 9709 Single-lit-var:
 9710     Lit-var/imm32
 9711     0/imm32/next
 9712 
 9713 Lit-var:
 9714     "literal"/imm32/name
 9715     Type-literal/imm32
 9716     1/imm32/some-block-depth
 9717     0/imm32/no-stack-offset
 9718     0/imm32/no-register
 9719 
 9720 Type-int:
 9721     1/imm32/left-is-atom
 9722     1/imm32/left/int
 9723     0/imm32/right/null
 9724 
 9725 Type-literal:
 9726     1/imm32/left-is-atom
 9727     0/imm32/left/literal
 9728     0/imm32/right/null
 9729 
 9730 Type-addr:
 9731     1/imm32/left-is-atom
 9732     2/imm32/left/addr
 9733     0/imm32/right/null
 9734 
 9735 == code
 9736 emit-subx-primitive:  # out: (addr buffered-file), stmt: (handle stmt), primitive: (handle function)
 9737     # . prologue
 9738     55/push-ebp
 9739     89/<- %ebp 4/r32/esp
 9740     # . save registers
 9741     50/push-eax
 9742     51/push-ecx
 9743     # ecx = primitive
 9744     8b/-> *(ebp+0x10) 1/r32/ecx
 9745     # emit primitive name
 9746     (emit-indent *(ebp+8) *Curr-block-depth)
 9747     (write-buffered *(ebp+8) *(ecx+0xc))  # Primitive-subx-name
 9748     # emit rm32 if necessary
 9749     (emit-subx-rm32 *(ebp+8) *(ecx+0x10) *(ebp+0xc))  # out, Primitive-subx-rm32, stmt
 9750     # emit r32 if necessary
 9751     (emit-subx-r32 *(ebp+8) *(ecx+0x14) *(ebp+0xc))  # out, Primitive-subx-r32, stmt
 9752     # emit imm32 if necessary
 9753     (emit-subx-imm32 *(ebp+8) *(ecx+0x18) *(ebp+0xc))  # out, Primitive-subx-imm32, stmt
 9754     # emit disp32 if necessary
 9755     (emit-subx-disp32 *(ebp+8) *(ecx+0x1c) *(ebp+0xc))  # out, Primitive-subx-disp32, stmt
 9756     (write-buffered *(ebp+8) Newline)
 9757 $emit-subx-primitive:end:
 9758     # . restore registers
 9759     59/pop-to-ecx
 9760     58/pop-to-eax
 9761     # . epilogue
 9762     89/<- %esp 5/r32/ebp
 9763     5d/pop-to-ebp
 9764     c3/return
 9765 
 9766 emit-subx-rm32:  # out: (addr buffered-file), l: arg-location, stmt: (handle stmt)
 9767     # . prologue
 9768     55/push-ebp
 9769     89/<- %ebp 4/r32/esp
 9770     # . save registers
 9771     50/push-eax
 9772     # if (l == 0) return
 9773     81 7/subop/compare *(ebp+0xc) 0/imm32
 9774     74/jump-if-= $emit-subx-rm32:end/disp8
 9775     # var v/eax: (handle var)
 9776     (get-stmt-operand-from-arg-location *(ebp+0x10) *(ebp+0xc))  # => eax
 9777     (emit-subx-var-as-rm32 *(ebp+8) %eax)
 9778 $emit-subx-rm32:end:
 9779     # . restore registers
 9780     58/pop-to-eax
 9781     # . epilogue
 9782     89/<- %esp 5/r32/ebp
 9783     5d/pop-to-ebp
 9784     c3/return
 9785 
 9786 get-stmt-operand-from-arg-location:  # stmt: (handle stmt), l: arg-location -> var/eax: (handle stmt-var)
 9787     # . prologue
 9788     55/push-ebp
 9789     89/<- %ebp 4/r32/esp
 9790     # . save registers
 9791     51/push-ecx
 9792     # eax = l
 9793     8b/-> *(ebp+0xc) 0/r32/eax
 9794     # ecx = stmt
 9795     8b/-> *(ebp+8) 1/r32/ecx
 9796     # if (l == 1) return stmt->inouts
 9797     {
 9798       3d/compare-eax-and 1/imm32
 9799       75/jump-if-!= break/disp8
 9800 $get-stmt-operand-from-arg-location:1:
 9801       8b/-> *(ecx+8) 0/r32/eax  # Stmt1-inouts or Regvardef-inouts
 9802       eb/jump $get-stmt-operand-from-arg-location:end/disp8
 9803     }
 9804     # if (l == 2) return stmt->inouts->next
 9805     {
 9806       3d/compare-eax-and 2/imm32
 9807       75/jump-if-!= break/disp8
 9808 $get-stmt-operand-from-arg-location:2:
 9809       8b/-> *(ecx+8) 0/r32/eax  # Stmt1-inouts or Regvardef-inouts
 9810       8b/-> *(eax+4) 0/r32/eax  # Stmt-var-next
 9811       eb/jump $get-stmt-operand-from-arg-location:end/disp8
 9812     }
 9813     # if (l == 3) return stmt->outputs
 9814     {
 9815       3d/compare-eax-and 3/imm32
 9816       75/jump-if-!= break/disp8
 9817 $get-stmt-operand-from-arg-location:3:
 9818       8b/-> *(ecx+0xc) 0/r32/eax  # Stmt1-outputs
 9819       eb/jump $get-stmt-operand-from-arg-location:end/disp8
 9820     }
 9821     # abort
 9822     e9/jump $get-stmt-operand-from-arg-location:abort/disp32
 9823 $get-stmt-operand-from-arg-location:end:
 9824     # . restore registers
 9825     59/pop-to-ecx
 9826     # . epilogue
 9827     89/<- %esp 5/r32/ebp
 9828     5d/pop-to-ebp
 9829     c3/return
 9830 
 9831 $get-stmt-operand-from-arg-location:abort:
 9832     # error("invalid arg-location " eax)
 9833     (write-buffered Stderr "invalid arg-location ")
 9834     (print-int32-buffered Stderr %eax)
 9835     (write-buffered Stderr Newline)
 9836     (flush Stderr)
 9837     # . syscall(exit, 1)
 9838     bb/copy-to-ebx  1/imm32
 9839     b8/copy-to-eax  1/imm32/exit
 9840     cd/syscall  0x80/imm8
 9841     # never gets here
 9842 
 9843 emit-subx-r32:  # out: (addr buffered-file), l: arg-location, stmt: (handle stmt)
 9844     # . prologue
 9845     55/push-ebp
 9846     89/<- %ebp 4/r32/esp
 9847     # . save registers
 9848     50/push-eax
 9849     51/push-ecx
 9850     # if (location == 0) return
 9851     81 7/subop/compare *(ebp+0xc) 0/imm32
 9852     0f 84/jump-if-= $emit-subx-r32:end/disp32
 9853     # var v/eax: (handle var)
 9854     (get-stmt-operand-from-arg-location *(ebp+0x10) *(ebp+0xc))  # => eax
 9855     8b/-> *eax 0/r32/eax  # Stmt-var-value
 9856     (maybe-get Registers *(eax+0x10) 8)  # Var-register => eax: (addr register-index)
 9857     (write-buffered *(ebp+8) Space)
 9858     (print-int32-buffered *(ebp+8) *eax)
 9859     (write-buffered *(ebp+8) "/r32")
 9860 $emit-subx-r32:end:
 9861     # . restore registers
 9862     59/pop-to-ecx
 9863     58/pop-to-eax
 9864     # . epilogue
 9865     89/<- %esp 5/r32/ebp
 9866     5d/pop-to-ebp
 9867     c3/return
 9868 
 9869 emit-subx-imm32:  # out: (addr buffered-file), l: arg-location, stmt: (handle stmt)
 9870     # . prologue
 9871     55/push-ebp
 9872     89/<- %ebp 4/r32/esp
 9873     # . save registers
 9874     50/push-eax
 9875     51/push-ecx
 9876     # if (location == 0) return
 9877     81 7/subop/compare *(ebp+0xc) 0/imm32
 9878     74/jump-if-= $emit-subx-imm32:end/disp8
 9879     # var v/eax: (handle var)
 9880     (get-stmt-operand-from-arg-location *(ebp+0x10) *(ebp+0xc))  # => eax
 9881     8b/-> *eax 0/r32/eax  # Stmt-var-value
 9882     (write-buffered *(ebp+8) Space)
 9883     (write-buffered *(ebp+8) *eax)  # Var-name
 9884     (write-buffered *(ebp+8) "/imm32")
 9885 $emit-subx-imm32:end:
 9886     # . restore registers
 9887     59/pop-to-ecx
 9888     58/pop-to-eax
 9889     # . epilogue
 9890     89/<- %esp 5/r32/ebp
 9891     5d/pop-to-ebp
 9892     c3/return
 9893 
 9894 emit-subx-disp32:  # out: (addr buffered-file), l: arg-location, stmt: (handle stmt)
 9895     # . prologue
 9896     55/push-ebp
 9897     89/<- %ebp 4/r32/esp
 9898     # . save registers
 9899     50/push-eax
 9900     51/push-ecx
 9901     # if (location == 0) return
 9902     81 7/subop/compare *(ebp+0xc) 0/imm32
 9903     0f 84/jump-if-= $emit-subx-disp32:end/disp32
 9904     # var v/eax: (handle var)
 9905     (get-stmt-operand-from-arg-location *(ebp+0x10) *(ebp+0xc))  # => eax
 9906     8b/-> *eax 0/r32/eax  # Stmt-var-value
 9907     (write-buffered *(ebp+8) Space)
 9908     (write-buffered *(ebp+8) *eax)  # Var-name
 9909     # hack: if instruction operation starts with "break", emit ":break"
 9910     # var name/ecx: (addr array byte) = stmt->operation
 9911     8b/-> *(ebp+0x10) 0/r32/eax
 9912     8b/-> *(eax+4) 1/r32/ecx
 9913     {
 9914       (string-starts-with? %ecx "break")  # => eax
 9915       3d/compare-eax-and 0/imm32/false
 9916       74/jump-if-= break/disp8
 9917       (write-buffered *(ebp+8) ":break")
 9918     }
 9919     # hack: if instruction operation starts with "loop", emit ":loop"
 9920     {
 9921       (string-starts-with? %ecx "loop")  # => eax
 9922       3d/compare-eax-and 0/imm32/false
 9923       74/jump-if-= break/disp8
 9924       (write-buffered *(ebp+8) ":loop")
 9925     }
 9926     (write-buffered *(ebp+8) "/disp32")
 9927 $emit-subx-disp32:end:
 9928     # . restore registers
 9929     59/pop-to-ecx
 9930     58/pop-to-eax
 9931     # . epilogue
 9932     89/<- %esp 5/r32/ebp
 9933     5d/pop-to-ebp
 9934     c3/return
 9935 
 9936 emit-call:  # out: (addr buffered-file), stmt: (handle stmt)
 9937     # . prologue
 9938     55/push-ebp
 9939     89/<- %ebp 4/r32/esp
 9940     # . save registers
 9941     51/push-ecx
 9942     #
 9943     (emit-indent *(ebp+8) *Curr-block-depth)
 9944     (write-buffered *(ebp+8) "(")
 9945     # ecx = stmt
 9946     8b/-> *(ebp+0xc) 1/r32/ecx
 9947     # - emit function name
 9948     (write-buffered *(ebp+8) *(ecx+4))  # Stmt1-operation
 9949     # - emit arguments
 9950     # var curr/ecx: (handle stmt-var) = stmt->inouts
 9951     8b/-> *(ecx+8) 1/r32/ecx  # Stmt1-inouts
 9952     {
 9953       # if (curr == null) break
 9954       81 7/subop/compare %ecx 0/imm32
 9955       74/jump-if-= break/disp8
 9956       #
 9957       (emit-subx-call-operand *(ebp+8) %ecx)
 9958       # curr = curr->next
 9959       8b/-> *(ecx+4) 1/r32/ecx  # Stmt-var-next
 9960       eb/jump loop/disp8
 9961     }
 9962     #
 9963     (write-buffered *(ebp+8) ")\n")
 9964 $emit-call:end:
 9965     # . restore registers
 9966     59/pop-to-ecx
 9967     # . epilogue
 9968     89/<- %esp 5/r32/ebp
 9969     5d/pop-to-ebp
 9970     c3/return
 9971 
 9972 emit-subx-call-operand:  # out: (addr buffered-file), s: (handle stmt-var)
 9973     # shares code with emit-subx-var-as-rm32
 9974     # . prologue
 9975     55/push-ebp
 9976     89/<- %ebp 4/r32/esp
 9977     # . save registers
 9978     50/push-eax
 9979     51/push-ecx
 9980     56/push-esi
 9981     # ecx = s
 9982     8b/-> *(ebp+0xc) 1/r32/ecx
 9983     # var operand/esi: (handle var) = s->value
 9984     8b/-> *ecx 6/r32/esi  # Stmt-var-value
 9985     # if (operand->register && !s->is-deref?) emit "%__"
 9986     {
 9987 $emit-subx-call-operand:check-for-register-direct:
 9988       81 7/subop/compare *(esi+0x10) 0/imm32  # Var-register
 9989       74/jump-if-= break/disp8
 9990       81 7/subop/compare *(ecx+8) 0/imm32/false  # Stmt-var-is-deref
 9991       75/jump-if-!= break/disp8
 9992 $emit-subx-call-operand:register-direct:
 9993       (write-buffered *(ebp+8) " %")
 9994       (write-buffered *(ebp+8) *(esi+0x10))  # Var-register
 9995       e9/jump $emit-subx-call-operand:end/disp32
 9996     }
 9997     # else if (operand->register && s->is-deref?) emit "*__"
 9998     {
 9999 $emit-subx-call-operand:check-for-register-indirect:
10000       81 7/subop/compare *(esi+0x10) 0/imm32  # Var-register
10001       74/jump-if-= break/disp8
10002       81 7/subop/compare *(ecx+8) 0/imm32/false  # Stmt-var-is-deref
10003       74/jump-if-= break/disp8
10004 $emit-subx-call-operand:register-indirect:
10005       (emit-subx-call-operand-register-indirect *(ebp+8) %esi)
10006       e9/jump $emit-subx-call-operand:end/disp32
10007     }
10008     # else if (operand->stack-offset) emit "*(ebp+__)"
10009     {
10010       81 7/subop/compare *(esi+0xc) 0/imm32  # Var-offset
10011       74/jump-if-= break/disp8
10012 $emit-subx-call-operand:stack:
10013       (emit-subx-call-operand-stack *(ebp+8) %esi)
10014       e9/jump $emit-subx-call-operand:end/disp32
10015     }
10016     # else if (operand->type == literal) emit "__"
10017     {
10018       8b/-> *(esi+4) 0/r32/eax  # Var-type
10019       81 7/subop/compare *(eax+4) 0/imm32  # Tree-left
10020       75/jump-if-!= break/disp8
10021 $emit-subx-call-operand:literal:
10022       (write-buffered *(ebp+8) Space)
10023       (write-buffered *(ebp+8) *esi)
10024     }
10025 $emit-subx-call-operand:end:
10026     # . restore registers
10027     5e/pop-to-esi
10028     59/pop-to-ecx
10029     58/pop-to-eax
10030     # . epilogue
10031     89/<- %esp 5/r32/ebp
10032     5d/pop-to-ebp
10033     c3/return
10034 
10035 emit-subx-call-operand-register-indirect:  # out: (addr buffered-file), v: (handle var)
10036     # . prologue
10037     55/push-ebp
10038     89/<- %ebp 4/r32/esp
10039     # . save registers
10040     50/push-eax
10041     51/push-ecx
10042     56/push-esi
10043     # esi = v
10044     8b/-> *(ebp+0xc) 6/r32/esi
10045     # var size/ecx: int = size-of-deref(v)
10046     (size-of-deref %esi)  # => eax
10047     89/<- %ecx 0/r32/eax
10048     # TODO: assert size is a multiple of 4
10049     # var i/eax: int = 0
10050     b8/copy-to-eax 0/imm32
10051     {
10052 $emit-subx-call-operand-register-indirect:loop:
10053       # if (i >= size) break
10054       39/compare %eax 1/r32/ecx
10055       7d/jump-if->= break/disp8
10056       # emit " *(" v->register "+" i ")"
10057       (write-buffered *(ebp+8) " *(")
10058       (write-buffered *(ebp+8) *(esi+0x10))  # Var-register
10059       (write-buffered *(ebp+8) "+")
10060       (print-int32-buffered *(ebp+8) %eax)
10061       (write-buffered *(ebp+8) ")")
10062       # i += 4
10063       05/add-to-eax 4/imm32
10064       #
10065       eb/jump loop/disp8
10066     }
10067 $emit-subx-call-operand-register-indirect:end:
10068     # . restore registers
10069     5e/pop-to-esi
10070     59/pop-to-ecx
10071     58/pop-to-eax
10072     # . epilogue
10073     89/<- %esp 5/r32/ebp
10074     5d/pop-to-ebp
10075     c3/return
10076 
10077 emit-subx-call-operand-stack:  # out: (addr buffered-file), v: (handle var)
10078     # . prologue
10079     55/push-ebp
10080     89/<- %ebp 4/r32/esp
10081     # . save registers
10082     50/push-eax
10083     51/push-ecx
10084     56/push-esi
10085     # esi = v
10086     8b/-> *(ebp+0xc) 6/r32/esi
10087     # var curr/ecx: int = v->offset
10088     8b/-> *(esi+0xc) 1/r32/ecx  # Var-offset
10089     # var max/eax: int = v->offset + size-of(v)
10090     (size-of %esi)  # => eax
10091     # TODO: assert size is a multiple of 4
10092     01/add-to %eax 1/r32/ecx
10093     {
10094 $emit-subx-call-operand-stack:loop:
10095       # if (curr >= max) break
10096       39/compare %ecx 0/r32/eax
10097       7d/jump-if->= break/disp8
10098       # emit " *(ebp+" curr ")"
10099       (write-buffered *(ebp+8) " *(ebp+")
10100       (print-int32-buffered *(ebp+8) %ecx)
10101       (write-buffered *(ebp+8) ")")
10102       # i += 4
10103       81 0/subop/add %ecx 4/imm32
10104       #
10105       eb/jump loop/disp8
10106     }
10107 $emit-subx-call-operand-stack:end:
10108     # . restore registers
10109     5e/pop-to-esi
10110     59/pop-to-ecx
10111     58/pop-to-eax
10112     # . epilogue
10113     89/<- %esp 5/r32/ebp
10114     5d/pop-to-ebp
10115     c3/return
10116 
10117 emit-subx-var-as-rm32:  # out: (addr buffered-file), s: (handle stmt-var)
10118     # . prologue
10119     55/push-ebp
10120     89/<- %ebp 4/r32/esp
10121     # . save registers
10122     50/push-eax
10123     51/push-ecx
10124     56/push-esi
10125     # ecx = s
10126     8b/-> *(ebp+0xc) 1/r32/ecx
10127     # var operand/esi: (handle var) = s->value
10128     8b/-> *ecx 6/r32/esi  # Stmt-var-value
10129     # if (operand->register && s->is-deref?) emit "*__"
10130     {
10131 $emit-subx-var-as-rm32:check-for-register-indirect:
10132       81 7/subop/compare *(esi+0x10) 0/imm32  # Var-register
10133       74/jump-if-= break/disp8
10134       81 7/subop/compare *(ecx+8) 0/imm32/false  # Stmt-var-is-deref
10135       74/jump-if-= break/disp8
10136 $emit-subx-var-as-rm32:register-indirect:
10137       (write-buffered *(ebp+8) " *")
10138       (write-buffered *(ebp+8) *(esi+0x10))  # Var-register
10139     }
10140     # if (operand->register && !s->is-deref?) emit "%__"
10141     {
10142 $emit-subx-var-as-rm32:check-for-register-direct:
10143       81 7/subop/compare *(esi+0x10) 0/imm32  # Var-register
10144       74/jump-if-= break/disp8
10145       81 7/subop/compare *(ecx+8) 0/imm32/false  # Stmt-var-is-deref
10146       75/jump-if-!= break/disp8
10147 $emit-subx-var-as-rm32:register-direct:
10148       (write-buffered *(ebp+8) " %")
10149       (write-buffered *(ebp+8) *(esi+0x10))  # Var-register
10150     }
10151     # else if (operand->stack-offset) emit "*(ebp+__)"
10152     {
10153       81 7/subop/compare *(esi+0xc) 0/imm32  # Var-offset
10154       74/jump-if-= break/disp8
10155 $emit-subx-var-as-rm32:stack:
10156       (write-buffered *(ebp+8) Space)
10157       (write-buffered *(ebp+8) "*(ebp+")
10158       (print-int32-buffered *(ebp+8) *(esi+0xc))  # Var-offset
10159       (write-buffered *(ebp+8) ")")
10160     }
10161 $emit-subx-var-as-rm32:end:
10162     # . restore registers
10163     5e/pop-to-esi
10164     59/pop-to-ecx
10165     58/pop-to-eax
10166     # . epilogue
10167     89/<- %esp 5/r32/ebp
10168     5d/pop-to-ebp
10169     c3/return
10170 
10171 find-matching-primitive:  # primitives: (handle primitive), stmt: (handle stmt) -> result/eax: (handle primitive)
10172     # . prologue
10173     55/push-ebp
10174     89/<- %ebp 4/r32/esp
10175     # . save registers
10176     51/push-ecx
10177     # var curr/ecx: (handle primitive) = primitives
10178     8b/-> *(ebp+8) 1/r32/ecx
10179     {
10180 $find-matching-primitive:loop:
10181       # if (curr == null) break
10182       81 7/subop/compare %ecx 0/imm32
10183       0f 84/jump-if-= break/disp32
10184       # if match(curr, stmt) return curr
10185       {
10186         (mu-stmt-matches-primitive? *(ebp+0xc) %ecx)  # => eax
10187         3d/compare-eax-and 0/imm32/false
10188         74/jump-if-= break/disp8
10189         89/<- %eax 1/r32/ecx
10190         eb/jump $find-matching-primitive:end/disp8
10191       }
10192 $find-matching-primitive:next-primitive:
10193       # curr = curr->next
10194       8b/-> *(ecx+0x24) 1/r32/ecx  # Primitive-next
10195       e9/jump loop/disp32
10196     }
10197     # return null
10198     b8/copy-to-eax 0/imm32
10199 $find-matching-primitive:end:
10200     # . restore registers
10201     59/pop-to-ecx
10202     # . epilogue
10203     89/<- %esp 5/r32/ebp
10204     5d/pop-to-ebp
10205     c3/return
10206 
10207 mu-stmt-matches-primitive?:  # stmt: (handle stmt), primitive: (handle primitive) -> result/eax: boolean
10208     # A mu stmt matches a primitive if the name matches, all the inout vars
10209     # match, and all the output vars match.
10210     # Vars match if types match and registers match.
10211     # In addition, a stmt output matches a primitive's output if types match
10212     # and the primitive has a wildcard register.
10213     # . prologue
10214     55/push-ebp
10215     89/<- %ebp 4/r32/esp
10216     # . save registers
10217     51/push-ecx
10218     52/push-edx
10219     53/push-ebx
10220     56/push-esi
10221     57/push-edi
10222     # ecx = stmt
10223     8b/-> *(ebp+8) 1/r32/ecx
10224     # edx = primitive
10225     8b/-> *(ebp+0xc) 2/r32/edx
10226     {
10227 $mu-stmt-matches-primitive?:check-name:
10228       # if (primitive->name != stmt->operation) return false
10229       (string-equal? *(ecx+4) *edx)  # Stmt1-operation, Primitive-name => eax
10230       3d/compare-eax-and 0/imm32/false
10231       75/jump-if-!= break/disp8
10232       b8/copy-to-eax 0/imm32
10233       e9/jump $mu-stmt-matches-primitive?:end/disp32
10234     }
10235 $mu-stmt-matches-primitive?:check-inouts:
10236     # for (curr/esi in stmt->inouts, curr2/edi in primitive->inouts)
10237     8b/-> *(ecx+8) 6/r32/esi  # Stmt1-inouts or Regvardef-inouts
10238     8b/-> *(edx+4) 7/r32/edi  # Primitive-inouts
10239     {
10240       # if (curr == 0 && curr2 == 0) move on to check outputs
10241       {
10242         81 7/subop/compare %esi 0/imm32
10243         75/jump-if-!= break/disp8
10244 $mu-stmt-matches-primitive?:stmt-inout-is-null:
10245         {
10246           81 7/subop/compare %edi 0/imm32
10247           75/jump-if-!= break/disp8
10248           #
10249           e9/jump $mu-stmt-matches-primitive?:check-outputs/disp32
10250         }
10251         # return false
10252         b8/copy-to-eax 0/imm32/false
10253         e9/jump $mu-stmt-matches-primitive?:end/disp32
10254       }
10255       # if (curr2 == 0) return false
10256       {
10257         81 7/subop/compare %edi 0/imm32
10258         75/jump-if-!= break/disp8
10259 $mu-stmt-matches-primitive?:prim-inout-is-null:
10260         b8/copy-to-eax 0/imm32/false
10261         e9/jump $mu-stmt-matches-primitive?:end/disp32
10262       }
10263       # if (curr != curr2) return false
10264       {
10265         (operand-matches-primitive? %esi *edi)  # List-value => eax
10266         3d/compare-eax-and 0/imm32/false
10267         75/jump-if-!= break/disp8
10268         b8/copy-to-eax 0/imm32/false
10269         e9/jump $mu-stmt-matches-primitive?:end/disp32
10270       }
10271       # curr=curr->next
10272       8b/-> *(esi+4) 6/r32/esi  # Stmt-var-next
10273       # curr2=curr2->next
10274       8b/-> *(edi+4) 7/r32/edi  # Stmt-var-next
10275       eb/jump loop/disp8
10276     }
10277 $mu-stmt-matches-primitive?:check-outputs:
10278     # for (curr/esi in stmt->outputs, curr2/edi in primitive->outputs)
10279     8b/-> *(ecx+0xc) 6/r32/esi  # Stmt1-outputs
10280     8b/-> *(edx+8) 7/r32/edi  # Primitive-outputs
10281     {
10282       # if (curr == 0) return (curr2 == 0)
10283       {
10284 $mu-stmt-matches-primitive?:check-output:
10285         81 7/subop/compare %esi 0/imm32
10286         75/jump-if-!= break/disp8
10287         {
10288           81 7/subop/compare %edi 0/imm32
10289           75/jump-if-!= break/disp8
10290           # return true
10291           b8/copy-to-eax 1/imm32
10292           e9/jump $mu-stmt-matches-primitive?:end/disp32
10293         }
10294         # return false
10295         b8/copy-to-eax 0/imm32
10296         e9/jump $mu-stmt-matches-primitive?:end/disp32
10297       }
10298       # if (curr2 == 0) return false
10299       {
10300         81 7/subop/compare %edi 0/imm32
10301         75/jump-if-!= break/disp8
10302         b8/copy-to-eax 0/imm32
10303         e9/jump $mu-stmt-matches-primitive?:end/disp32
10304       }
10305       # if (curr != curr2) return false
10306       {
10307         (operand-matches-primitive? %esi *edi)  # List-value => eax
10308         3d/compare-eax-and 0/imm32/false
10309         75/jump-if-!= break/disp8
10310         b8/copy-to-eax 0/imm32
10311         e9/jump $mu-stmt-matches-primitive?:end/disp32
10312       }
10313       # curr=curr->next
10314       8b/-> *(esi+4) 6/r32/esi  # Stmt-var-next
10315       # curr2=curr2->next
10316       8b/-> *(edi+4) 7/r32/edi  # Stmt-var-next
10317       eb/jump loop/disp8
10318     }
10319 $mu-stmt-matches-primitive?:return-true:
10320     b8/copy-to-eax 1/imm32
10321 $mu-stmt-matches-primitive?:end:
10322     # . restore registers
10323     5f/pop-to-edi
10324     5e/pop-to-esi
10325     5b/pop-to-ebx
10326     5a/pop-to-edx
10327     59/pop-to-ecx
10328     # . epilogue
10329     89/<- %esp 5/r32/ebp
10330     5d/pop-to-ebp
10331     c3/return
10332 
10333 operand-matches-primitive?:  # s: (handle stmt-var), prim-var: (handle var) -> result/eax: boolean
10334     # . prologue
10335     55/push-ebp
10336     89/<- %ebp 4/r32/esp
10337     # . save registers
10338     51/push-ecx
10339     56/push-esi
10340     57/push-edi
10341     # ecx = s
10342     8b/-> *(ebp+8) 1/r32/ecx
10343     # var var/esi : (handle var) = s->value
10344     8b/-> *ecx 6/r32/esi  # Stmt-var-value
10345     # edi = prim-var
10346     8b/-> *(ebp+0xc) 7/r32/edi
10347 $operand-matches-primitive?:check-type:
10348     # if (var->type != prim-var->type) return false
10349     (subx-type-equal? *(esi+4) *(edi+4))  # Var-type, Var-type => eax
10350     3d/compare-eax-and 0/imm32/false
10351     b8/copy-to-eax 0/imm32/false
10352     0f 84/jump-if-= $operand-matches-primitive?:end/disp32
10353     {
10354 $operand-matches-primitive?:check-register:
10355       # if prim-var is in memory and var is in register but dereference, match
10356       {
10357         81 7/subop/compare *(edi+0x10) 0/imm32  # Var-register
10358         0f 85/jump-if-!= break/disp32
10359         81 7/subop/compare *(esi+0x10) 0/imm32  # Var-register
10360         74/jump-if-= break/disp8
10361         81 7/subop/compare *(ecx+8) 0/imm32/false  # Stmt-var-is-deref
10362         74/jump-if-= break/disp8
10363         e9/jump $operand-matches-primitive?:return-true/disp32
10364       }
10365       # if prim-var is in register and var is in register but dereference, no match
10366       {
10367         81 7/subop/compare *(edi+0x10) 0/imm32  # Var-register
10368         0f 84/jump-if-= break/disp32
10369         81 7/subop/compare *(esi+0x10) 0/imm32  # Var-register
10370         0f 84/jump-if-= break/disp32
10371         81 7/subop/compare *(ecx+8) 0/imm32/false  # Stmt-var-is-deref
10372         74/jump-if-= break/disp8
10373         e9/jump $operand-matches-primitive?:return-false/disp32
10374       }
10375       # return false if var->register doesn't match prim-var->register
10376       {
10377         # if register addresses are equal, it's a match
10378         8b/-> *(esi+0x10) 0/r32/eax  # Var-register
10379         39/compare *(edi+0x10) 0/r32/eax  # Var-register
10380         74/jump-if-= break/disp8
10381         # if either address is 0, return false
10382         3d/compare-eax-and 0/imm32
10383         74/jump-if-=  $operand-matches-primitive?:end/disp8  # eax goes from meaning var->register to result
10384         81 7/subop/compare *(edi+0x10) 0/imm32  # Var-register
10385         74/jump-if-=  $operand-matches-primitive?:return-false/disp8
10386         # if prim-var->register is wildcard, it's a match
10387         (string-equal? *(edi+0x10) Any-register)  # Var-register => eax
10388         3d/compare-eax-and 0/imm32/false
10389         75/jump-if-!= break/disp8
10390         # if string contents aren't equal, return false
10391         (string-equal? *(esi+0x10) *(edi+0x10))  # Var-register Var-register => eax
10392         3d/compare-eax-and 0/imm32/false
10393         74/jump-if-= $operand-matches-primitive?:return-false/disp8
10394       }
10395     }
10396 $operand-matches-primitive?:return-true:
10397     b8/copy-to-eax 1/imm32/true
10398     eb/jump $operand-matches-primitive?:end/disp8
10399 $operand-matches-primitive?:return-false:
10400     b8/copy-to-eax 0/imm32/false
10401 $operand-matches-primitive?:end:
10402     # . restore registers
10403     5f/pop-to-edi
10404     5e/pop-to-esi
10405     59/pop-to-ecx
10406     # . epilogue
10407     89/<- %esp 5/r32/ebp
10408     5d/pop-to-ebp
10409     c3/return
10410 
10411 subx-type-equal?:  # a: (handle tree type-id), b: (handle tree type-id) -> result/eax: boolean
10412     # . prologue
10413     55/push-ebp
10414     89/<- %ebp 4/r32/esp
10415     # . save registers
10416     51/push-ecx
10417     # var alit/ecx: boolean = is-literal-type?(a)
10418     (is-simple-mu-type? *(ebp+8) 0)  # => eax
10419     89/<- %ecx 0/r32/eax
10420     # var blit/eax: boolean = is-literal-type?(b)
10421     (is-simple-mu-type? *(ebp+0xc) 0)  # => eax
10422     # return alit == blit
10423     39/compare %eax 1/r32/ecx
10424     0f 94/set-byte-if-= %al
10425     81 4/subop/and %eax 0xff/imm32
10426 $subx-type-equal?:end:
10427     # . restore registers
10428     59/pop-to-ecx
10429     # . epilogue
10430     89/<- %esp 5/r32/ebp
10431     5d/pop-to-ebp
10432     c3/return
10433 
10434 is-simple-mu-type?:  # a: (handle tree type-id), n: type-id -> result/eax: boolean
10435     # . prologue
10436     55/push-ebp
10437     89/<- %ebp 4/r32/esp
10438     # . save registers
10439     51/push-ecx
10440     # ecx = n
10441     8b/-> *(ebp+0xc) 1/r32/ecx
10442     # return (a->value == n)
10443     8b/-> *(ebp+8) 0/r32/eax
10444     39/compare *(eax+4) 1/r32/ecx  # Tree-value
10445     0f 94/set-byte-if-= %al
10446     81 4/subop/and %eax 0xff/imm32
10447 $is-simple-mu-type?:end:
10448     # . restore registers
10449     59/pop-to-ecx
10450     # . epilogue
10451     89/<- %esp 5/r32/ebp
10452     5d/pop-to-ebp
10453     c3/return
10454 
10455 test-emit-subx-stmt-primitive:
10456     # Primitive operation on a variable on the stack.
10457     #   increment foo
10458     # =>
10459     #   ff 0/subop/increment *(ebp-8)
10460     #
10461     # There's a variable on the var stack as follows:
10462     #   name: 'foo'
10463     #   type: int
10464     #   stack-offset: -8
10465     #
10466     # There's a primitive with this info:
10467     #   name: 'increment'
10468     #   inouts: int/mem
10469     #   value: 'ff 0/subop/increment'
10470     #
10471     # There's nothing in functions.
10472     #
10473     # . prologue
10474     55/push-ebp
10475     89/<- %ebp 4/r32/esp
10476     # setup
10477     (clear-stream _test-output-stream)
10478     (clear-stream $_test-output-buffered-file->buffer)
10479     # var type/ecx: (handle tree type-id) = int
10480     68/push 0/imm32/right/null
10481     68/push 1/imm32/left/int
10482     89/<- %ecx 4/r32/esp
10483     # var var-foo/ecx: var
10484     68/push 0/imm32/no-register
10485     68/push -8/imm32/stack-offset
10486     68/push 1/imm32/block-depth
10487     51/push-ecx
10488     68/push "foo"/imm32
10489     89/<- %ecx 4/r32/esp
10490     # var operand/ebx: (handle stmt-var)
10491     68/push 0/imm32/is-deref:false
10492     68/push 0/imm32/next
10493     51/push-ecx/var-foo
10494     89/<- %ebx 4/r32/esp
10495     # var stmt/esi: statement
10496     68/push 0/imm32/next
10497     68/push 0/imm32/outputs
10498     53/push-ebx/operands
10499     68/push "increment"/imm32/operation
10500     68/push 1/imm32
10501     89/<- %esi 4/r32/esp
10502     # var primitives/ebx: primitive
10503     68/push 0/imm32/next
10504     68/push 0/imm32/output-is-write-only
10505     68/push 0/imm32/no-disp32
10506     68/push 0/imm32/no-imm32
10507     68/push 0/imm32/no-r32
10508     68/push 1/imm32/rm32-is-first-inout
10509     68/push "ff 0/subop/increment"/imm32/subx-name
10510     68/push 0/imm32/outputs
10511     53/push-ebx/inouts  # hack; in practice we won't have the same var in function definition and call
10512     68/push "increment"/imm32/name
10513     89/<- %ebx 4/r32/esp
10514     # convert
10515     c7 0/subop/copy *Curr-block-depth 0/imm32
10516     (emit-subx-stmt _test-output-buffered-file %esi %ebx)
10517     (flush _test-output-buffered-file)
10518 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
10524     # check output
10525     (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment *(ebp+0xfffffff8)" "F - test-emit-subx-stmt-primitive")
10526     # . epilogue
10527     89/<- %esp 5/r32/ebp
10528     5d/pop-to-ebp
10529     c3/return
10530 
10531 test-emit-subx-stmt-primitive-register:
10532     # Primitive operation on a variable in a register.
10533     #   foo <- increment
10534     # =>
10535     #   ff 0/subop/increment %eax  # sub-optimal, but should suffice
10536     #
10537     # There's a variable on the var stack as follows:
10538     #   name: 'foo'
10539     #   type: int
10540     #   register: 'eax'
10541     #
10542     # There's a primitive with this info:
10543     #   name: 'increment'
10544     #   out: int/reg
10545     #   value: 'ff 0/subop/increment'
10546     #
10547     # There's nothing in functions.
10548     #
10549     # . prologue
10550     55/push-ebp
10551     89/<- %ebp 4/r32/esp
10552     # setup
10553     (clear-stream _test-output-stream)
10554     (clear-stream $_test-output-buffered-file->buffer)
10555     # var type/ecx: (handle tree type-id) = int
10556     68/push 0/imm32/right/null
10557     68/push 1/imm32/left/int
10558     89/<- %ecx 4/r32/esp
10559     # var var-foo/ecx: var in eax
10560     68/push "eax"/imm32/register
10561     68/push 0/imm32/no-stack-offset
10562     68/push 1/imm32/block-depth
10563     51/push-ecx
10564     68/push "foo"/imm32
10565     89/<- %ecx 4/r32/esp
10566     # var operand/ebx: (handle stmt-var)
10567     68/push 0/imm32/is-deref:false
10568     68/push 0/imm32/next
10569     51/push-ecx/var-foo
10570     89/<- %ebx 4/r32/esp
10571     # var stmt/esi: statement
10572     68/push 0/imm32/next
10573     53/push-ebx/outputs
10574     68/push 0/imm32/inouts
10575     68/push "increment"/imm32/operation
10576     68/push 1/imm32
10577     89/<- %esi 4/r32/esp
10578     # var formal-var/ebx: var in any register
10579     68/push Any-register/imm32
10580     68/push 0/imm32/no-stack-offset
10581     68/push 1/imm32/block-depth
10582     ff 6/subop/push *(ecx+4)  # Var-type
10583     68/push "dummy"/imm32
10584     89/<- %ebx 4/r32/esp
10585     # var operand/ebx: (handle stmt-var)
10586     68/push 0/imm32/is-deref:false
10587     68/push 0/imm32/next
10588     53/push-ebx/formal-var
10589     89/<- %ebx 4/r32/esp
10590     # var primitives/ebx: primitive
10591     68/push 0/imm32/next
10592     68/push 0/imm32/output-is-write-only
10593     68/push 0/imm32/no-disp32
10594     68/push 0/imm32/no-imm32
10595     68/push 0/imm32/no-r32
10596     68/push 3/imm32/rm32-in-first-output
10597     68/push "ff 0/subop/increment"/imm32/subx-name
10598     53/push-ebx/outputs
10599     68/push 0/imm32/inouts
10600     68/push "increment"/imm32/name
10601     89/<- %ebx 4/r32/esp
10602     # convert
10603     c7 0/subop/copy *Curr-block-depth 0/imm32
10604     (emit-subx-stmt _test-output-buffered-file %esi %ebx)
10605     (flush _test-output-buffered-file)
10606 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
10612     # check output
10613     (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment %eax" "F - test-emit-subx-stmt-primitive-register")
10614     # . epilogue
10615     89/<- %esp 5/r32/ebp
10616     5d/pop-to-ebp
10617     c3/return
10618 
10619 test-emit-subx-stmt-select-primitive:
10620     # Select the right primitive between overloads.
10621     #   foo <- increment
10622     # =>
10623     #   ff 0/subop/increment %eax  # sub-optimal, but should suffice
10624     #
10625     # There's a variable on the var stack as follows:
10626     #   name: 'foo'
10627     #   type: int
10628     #   register: 'eax'
10629     #
10630     # There's two primitives, as follows:
10631     #   - name: 'increment'
10632     #     out: int/reg
10633     #     value: 'ff 0/subop/increment'
10634     #   - name: 'increment'
10635     #     inout: int/mem
10636     #     value: 'ff 0/subop/increment'
10637     #
10638     # There's nothing in functions.
10639     #
10640     # . prologue
10641     55/push-ebp
10642     89/<- %ebp 4/r32/esp
10643     # setup
10644     (clear-stream _test-output-stream)
10645     (clear-stream $_test-output-buffered-file->buffer)
10646     # var type/ecx: (handle tree type-id) = int
10647     68/push 0/imm32/right/null
10648     68/push 1/imm32/left/int
10649     89/<- %ecx 4/r32/esp
10650     # var var-foo/ecx: var in eax
10651     68/push "eax"/imm32/register
10652     68/push 0/imm32/no-stack-offset
10653     68/push 1/imm32/block-depth
10654     51/push-ecx
10655     68/push "foo"/imm32
10656     89/<- %ecx 4/r32/esp
10657     # var real-outputs/edi: (handle stmt-var)
10658     68/push 0/imm32/is-deref:false
10659     68/push 0/imm32/next
10660     51/push-ecx/var-foo
10661     89/<- %edi 4/r32/esp
10662     # var stmt/esi: statement
10663     68/push 0/imm32/next
10664     57/push-edi/outputs
10665     68/push 0/imm32/inouts
10666     68/push "increment"/imm32/operation
10667     68/push 1/imm32
10668     89/<- %esi 4/r32/esp
10669     # var formal-var/ebx: var in any register
10670     68/push Any-register/imm32
10671     68/push 0/imm32/no-stack-offset
10672     68/push 1/imm32/block-depth
10673     ff 6/subop/push *(ecx+4)  # Var-type
10674     68/push "dummy"/imm32
10675     89/<- %ebx 4/r32/esp
10676     # var formal-outputs/ebx: (handle stmt-var)
10677     68/push 0/imm32/is-deref:false
10678     68/push 0/imm32/next
10679     53/push-ebx/formal-var
10680     89/<- %ebx 4/r32/esp
10681     # var primitive1/ebx: primitive
10682     68/push 0/imm32/next
10683     68/push 0/imm32/output-is-write-only
10684     68/push 0/imm32/no-disp32
10685     68/push 0/imm32/no-imm32
10686     68/push 0/imm32/no-r32
10687     68/push 3/imm32/rm32-in-first-output
10688     68/push "ff 0/subop/increment"/imm32/subx-name
10689     53/push-ebx/outputs/formal-outputs
10690     68/push 0/imm32/inouts
10691     68/push "increment"/imm32/name
10692     89/<- %ebx 4/r32/esp
10693     # var primitives/ebx: primitive
10694     53/push-ebx/next
10695     68/push 0/imm32/output-is-write-only
10696     68/push 0/imm32/no-disp32
10697     68/push 0/imm32/no-imm32
10698     68/push 0/imm32/no-r32
10699     68/push 1/imm32/rm32-is-first-inout
10700     68/push "ff 0/subop/increment"/imm32/subx-name
10701     68/push 0/imm32/outputs
10702     57/push-edi/inouts/real-outputs  # hack; in practice we won't have the same var in function definition and call
10703     68/push "increment"/imm32/name
10704     89/<- %ebx 4/r32/esp
10705     # convert
10706     c7 0/subop/copy *Curr-block-depth 0/imm32
10707     (emit-subx-stmt _test-output-buffered-file %esi %ebx)
10708     (flush _test-output-buffered-file)
10709 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
10715     # check output
10716     (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment %eax" "F - test-emit-subx-stmt-select-primitive")
10717     # . epilogue
10718     89/<- %esp 5/r32/ebp
10719     5d/pop-to-ebp
10720     c3/return
10721 
10722 test-emit-subx-stmt-select-primitive-2:
10723     # Select the right primitive between overloads.
10724     #   foo <- increment
10725     # =>
10726     #   ff 0/subop/increment %eax  # sub-optimal, but should suffice
10727     #
10728     # There's a variable on the var stack as follows:
10729     #   name: 'foo'
10730     #   type: int
10731     #   register: 'eax'
10732     #
10733     # There's two primitives, as follows:
10734     #   - name: 'increment'
10735     #     out: int/reg
10736     #     value: 'ff 0/subop/increment'
10737     #   - name: 'increment'
10738     #     inout: int/mem
10739     #     value: 'ff 0/subop/increment'
10740     #
10741     # There's nothing in functions.
10742     #
10743     # . prologue
10744     55/push-ebp
10745     89/<- %ebp 4/r32/esp
10746     # setup
10747     (clear-stream _test-output-stream)
10748     (clear-stream $_test-output-buffered-file->buffer)
10749     # var type/ecx: (handle tree type-id) = int
10750     68/push 0/imm32/right/null
10751     68/push 1/imm32/left/int
10752     89/<- %ecx 4/r32/esp
10753     # var var-foo/ecx: var in eax
10754     68/push "eax"/imm32/register
10755     68/push 0/imm32/no-stack-offset
10756     68/push 1/imm32/block-depth
10757     51/push-ecx
10758     68/push "foo"/imm32
10759     89/<- %ecx 4/r32/esp
10760     # var inouts/edi: (handle stmt-var)
10761     68/push 0/imm32/is-deref:false
10762     68/push 0/imm32/next
10763     51/push-ecx/var-foo
10764     89/<- %edi 4/r32/esp
10765     # var stmt/esi: statement
10766     68/push 0/imm32/next
10767     68/push 0/imm32/outputs
10768     57/push-edi/inouts
10769     68/push "increment"/imm32/operation
10770     68/push 1/imm32
10771     89/<- %esi 4/r32/esp
10772     # var formal-var/ebx: var in any register
10773     68/push Any-register/imm32
10774     68/push 0/imm32/no-stack-offset
10775     68/push 1/imm32/block-depth
10776     ff 6/subop/push *(ecx+4)  # Var-type
10777     68/push "dummy"/imm32
10778     89/<- %ebx 4/r32/esp
10779     # var operand/ebx: (handle stmt-var)
10780     68/push 0/imm32/is-deref:false
10781     68/push 0/imm32/next
10782     53/push-ebx/formal-var
10783     89/<- %ebx 4/r32/esp
10784     # var primitive1/ebx: primitive
10785     68/push 0/imm32/next
10786     68/push 0/imm32/output-is-write-only
10787     68/push 0/imm32/no-disp32
10788     68/push 0/imm32/no-imm32
10789     68/push 0/imm32/no-r32
10790     68/push 3/imm32/rm32-in-first-output
10791     68/push "ff 0/subop/increment"/imm32/subx-name
10792     53/push-ebx/outputs/formal-outputs
10793     68/push 0/imm32/inouts
10794     68/push "increment"/imm32/name
10795     89/<- %ebx 4/r32/esp
10796     # var primitives/ebx: primitive
10797     53/push-ebx/next
10798     68/push 0/imm32/output-is-write-only
10799     68/push 0/imm32/no-disp32
10800     68/push 0/imm32/no-imm32
10801     68/push 0/imm32/no-r32
10802     68/push 1/imm32/rm32-is-first-inout
10803     68/push "ff 0/subop/increment"/imm32/subx-name
10804     68/push 0/imm32/outputs
10805     57/push-edi/inouts/real-outputs  # hack; in practice we won't have the same var in function definition and call
10806     68/push "increment"/imm32/name
10807     89/<- %ebx 4/r32/esp
10808     # convert
10809     c7 0/subop/copy *Curr-block-depth 0/imm32
10810     (emit-subx-stmt _test-output-buffered-file %esi %ebx)
10811     (flush _test-output-buffered-file)
10812 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
10818     # check output
10819     (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment %eax" "F - test-emit-subx-stmt-select-primitive-2")
10820     # . epilogue
10821     89/<- %esp 5/r32/ebp
10822     5d/pop-to-ebp
10823     c3/return
10824 
10825 test-increment-register:
10826     # Select the right register between overloads.
10827     #   foo <- increment
10828     # =>
10829     #   50/increment-eax
10830     #
10831     # There's a variable on the var stack as follows:
10832     #   name: 'foo'
10833     #   type: int
10834     #   register: 'eax'
10835     #
10836     # Primitives are the global definitions.
10837     #
10838     # There are no functions defined.
10839     #
10840     # . prologue
10841     55/push-ebp
10842     89/<- %ebp 4/r32/esp
10843     # setup
10844     (clear-stream _test-output-stream)
10845     (clear-stream $_test-output-buffered-file->buffer)
10846     # var type/ecx: (handle tree type-id) = int
10847     68/push 0/imm32/right/null
10848     68/push 1/imm32/left/int
10849     68/push 1/imm32/is-atom
10850     89/<- %ecx 4/r32/esp
10851     # var var-foo/ecx: var in eax
10852     68/push "eax"/imm32/register
10853     68/push 0/imm32/no-stack-offset
10854     68/push 1/imm32/block-depth
10855     51/push-ecx
10856     68/push "foo"/imm32
10857     89/<- %ecx 4/r32/esp
10858     # var real-outputs/edi: (handle stmt-var)
10859     68/push 0/imm32/is-deref:false
10860     68/push 0/imm32/next
10861     51/push-ecx/var-foo
10862     89/<- %edi 4/r32/esp
10863     # var stmt/esi: statement
10864     68/push 0/imm32/next
10865     57/push-edi/outputs
10866     68/push 0/imm32/inouts
10867     68/push "increment"/imm32/operation
10868     68/push 1/imm32/regular-stmt
10869     89/<- %esi 4/r32/esp
10870     # convert
10871     c7 0/subop/copy *Curr-block-depth 0/imm32
10872     (emit-subx-stmt _test-output-buffered-file %esi Primitives)
10873     (flush _test-output-buffered-file)
10874 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
10880     # check output
10881     (check-next-stream-line-equal _test-output-stream "40/increment-eax" "F - test-increment-register")
10882     # . epilogue
10883     89/<- %esp 5/r32/ebp
10884     5d/pop-to-ebp
10885     c3/return
10886 
10887 test-increment-var:
10888     # Select the right primitive between overloads.
10889     #   foo <- increment
10890     # =>
10891     #   ff 0/subop/increment %eax  # sub-optimal, but should suffice
10892     #
10893     # There's a variable on the var stack as follows:
10894     #   name: 'foo'
10895     #   type: int
10896     #   register: 'eax'
10897     #
10898     # Primitives are the global definitions.
10899     #
10900     # There are no functions defined.
10901     #
10902     # . prologue
10903     55/push-ebp
10904     89/<- %ebp 4/r32/esp
10905     # setup
10906     (clear-stream _test-output-stream)
10907     (clear-stream $_test-output-buffered-file->buffer)
10908     # var type/ecx: (handle tree type-id) = int
10909     68/push 0/imm32/right/null
10910     68/push 1/imm32/left/int
10911     68/push 1/imm32/is-atom
10912     89/<- %ecx 4/r32/esp
10913     # var var-foo/ecx: var in eax
10914     68/push "eax"/imm32/register
10915     68/push 0/imm32/no-stack-offset
10916     68/push 1/imm32/block-depth
10917     51/push-ecx
10918     68/push "foo"/imm32
10919     89/<- %ecx 4/r32/esp
10920     # var inouts/edi: (handle stmt-var)
10921     68/push 0/imm32/is-deref:false
10922     68/push 0/imm32/next
10923     51/push-ecx/var-foo
10924     89/<- %edi 4/r32/esp
10925     # var stmt/esi: statement
10926     68/push 0/imm32/next
10927     57/push-edi/outputs
10928     68/push 0/imm32/inouts
10929     68/push "increment"/imm32/operation
10930     68/push 1/imm32
10931     89/<- %esi 4/r32/esp
10932     # convert
10933     c7 0/subop/copy *Curr-block-depth 0/imm32
10934     (emit-subx-stmt _test-output-buffered-file %esi Primitives)
10935     (flush _test-output-buffered-file)
10936 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
10942     # check output
10943     (check-next-stream-line-equal _test-output-stream "40/increment-eax" "F - test-increment-var")
10944     # . epilogue
10945     89/<- %esp 5/r32/ebp
10946     5d/pop-to-ebp
10947     c3/return
10948 
10949 test-add-reg-to-reg:
10950     #   var1/reg <- add var2/reg
10951     # =>
10952     #   01/add-to %var1 var2
10953     #
10954     # . prologue
10955     55/push-ebp
10956     89/<- %ebp 4/r32/esp
10957     # setup
10958     (clear-stream _test-output-stream)
10959     (clear-stream $_test-output-buffered-file->buffer)
10960     # var type/ecx: (handle tree type-id) = int
10961     68/push 0/imm32/right/null
10962     68/push 1/imm32/left/int
10963     68/push 1/imm32/is-atom
10964     89/<- %ecx 4/r32/esp
10965     # var var-var1/ecx: var in eax
10966     68/push "eax"/imm32/register
10967     68/push 0/imm32/no-stack-offset
10968     68/push 1/imm32/block-depth
10969     51/push-ecx
10970     68/push "var1"/imm32
10971     89/<- %ecx 4/r32/esp
10972     # var var-var2/edx: var in ecx
10973     68/push "ecx"/imm32/register
10974     68/push 0/imm32/no-stack-offset
10975     68/push 1/imm32/block-depth
10976     ff 6/subop/push *(ecx+4)  # Var-type
10977     68/push "var2"/imm32
10978     89/<- %edx 4/r32/esp
10979     # var inouts/esi: (handle stmt-var) = [var2]
10980     68/push 0/imm32/is-deref:false
10981     68/push 0/imm32/next
10982     52/push-edx/var-var2
10983     89/<- %esi 4/r32/esp
10984     # var outputs/edi: (handle stmt-var) = [var1, var2]
10985     68/push 0/imm32/is-deref:false
10986     68/push 0/imm32/next
10987     51/push-ecx/var-var1
10988     89/<- %edi 4/r32/esp
10989     # var stmt/esi: statement
10990     68/push 0/imm32/next
10991     57/push-edi/outputs
10992     56/push-esi/inouts
10993     68/push "add"/imm32/operation
10994     68/push 1/imm32
10995     89/<- %esi 4/r32/esp
10996     # convert
10997     c7 0/subop/copy *Curr-block-depth 0/imm32
10998     (emit-subx-stmt _test-output-buffered-file %esi Primitives)
10999     (flush _test-output-buffered-file)
11000 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
11006     # check output
11007     (check-next-stream-line-equal _test-output-stream "01/add-to %eax 0x00000001/r32" "F - test-add-reg-to-reg")
11008     # . epilogue
11009     89/<- %esp 5/r32/ebp
11010     5d/pop-to-ebp
11011     c3/return
11012 
11013 test-add-reg-to-mem:
11014     #   add-to var1 var2/reg
11015     # =>
11016     #   01/add-to *(ebp+__) var2
11017     #
11018     # . prologue
11019     55/push-ebp
11020     89/<- %ebp 4/r32/esp
11021     # setup
11022     (clear-stream _test-output-stream)
11023     (clear-stream $_test-output-buffered-file->buffer)
11024     # var type/ecx: (handle tree type-id) = int
11025     68/push 0/imm32/right/null
11026     68/push 1/imm32/left/int
11027     68/push 1/imm32/is-atom
11028     89/<- %ecx 4/r32/esp
11029     # var var-var1/ecx: var
11030     68/push 0/imm32/no-register
11031     68/push 8/imm32/stack-offset
11032     68/push 1/imm32/block-depth
11033     51/push-ecx
11034     68/push "var1"/imm32
11035     89/<- %ecx 4/r32/esp
11036     # var var-var2/edx: var in ecx
11037     68/push "ecx"/imm32/register
11038     68/push 0/imm32/no-stack-offset
11039     68/push 1/imm32/block-depth
11040     ff 6/subop/push *(ecx+4)  # Var-type
11041     68/push "var2"/imm32
11042     89/<- %edx 4/r32/esp
11043     # var inouts/esi: (handle stmt-var) = [var2]
11044     68/push 0/imm32/is-deref:false
11045     68/push 0/imm32/next
11046     52/push-edx/var-var2
11047     89/<- %esi 4/r32/esp
11048     # var inouts = (handle stmt-var) = [var1, var2]
11049     56/push-esi/next
11050     51/push-ecx/var-var1
11051     89/<- %esi 4/r32/esp
11052     # var stmt/esi: statement
11053     68/push 0/imm32/next
11054     68/push 0/imm32/outputs
11055     56/push-esi/inouts
11056     68/push "add-to"/imm32/operation
11057     68/push 1/imm32
11058     89/<- %esi 4/r32/esp
11059     # convert
11060     c7 0/subop/copy *Curr-block-depth 0/imm32
11061     (emit-subx-stmt _test-output-buffered-file %esi Primitives)
11062     (flush _test-output-buffered-file)
11063 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
11069     # check output
11070     (check-next-stream-line-equal _test-output-stream "01/add-to *(ebp+0x00000008) 0x00000001/r32" "F - test-add-reg-to-mem")
11071     # . epilogue
11072     89/<- %esp 5/r32/ebp
11073     5d/pop-to-ebp
11074     c3/return
11075 
11076 test-add-mem-to-reg:
11077     #   var1/reg <- add var2
11078     # =>
11079     #   03/add *(ebp+__) var1
11080     #
11081     # . prologue
11082     55/push-ebp
11083     89/<- %ebp 4/r32/esp
11084     # setup
11085     (clear-stream _test-output-stream)
11086     (clear-stream $_test-output-buffered-file->buffer)
11087     # var type/ecx: (handle tree type-id) = int
11088     68/push 0/imm32/right/null
11089     68/push 1/imm32/left/int
11090     68/push 1/imm32/is-atom
11091     89/<- %ecx 4/r32/esp
11092     # var var-var1/ecx: var in eax
11093     68/push "eax"/imm32/register
11094     68/push 0/imm32/no-stack-offset
11095     68/push 1/imm32/block-depth
11096     51/push-ecx
11097     68/push "var1"/imm32
11098     89/<- %ecx 4/r32/esp
11099     # var var-var2/edx: var
11100     68/push 0/imm32/no-register
11101     68/push 8/imm32/stack-offset
11102     68/push 1/imm32/block-depth
11103     ff 6/subop/push *(ecx+4)  # Var-type
11104     68/push "var2"/imm32
11105     89/<- %edx 4/r32/esp
11106     # var inouts/esi: (handle stmt-var) = [var2]
11107     68/push 0/imm32/is-deref:false
11108     68/push 0/imm32/next
11109     52/push-edx/var-var2
11110     89/<- %esi 4/r32/esp
11111     # var outputs/edi = (handle stmt-var) = [var1]
11112     68/push 0/imm32/is-deref:false
11113     68/push 0/imm32/next
11114     51/push-ecx/var-var1
11115     89/<- %edi 4/r32/esp
11116     # var stmt/esi: statement
11117     68/push 0/imm32/next
11118     57/push-edi/outputs
11119     56/push-esi/inouts
11120     68/push "add"/imm32/operation
11121     68/push 1/imm32
11122     89/<- %esi 4/r32/esp
11123     # convert
11124     c7 0/subop/copy *Curr-block-depth 0/imm32
11125     (emit-subx-stmt _test-output-buffered-file %esi Primitives)
11126     (flush _test-output-buffered-file)
11127 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
11133     # check output
11134     (check-next-stream-line-equal _test-output-stream "03/add *(ebp+0x00000008) 0x00000000/r32" "F - test-add-mem-to-reg")
11135     # . epilogue
11136     89/<- %esp 5/r32/ebp
11137     5d/pop-to-ebp
11138     c3/return
11139 
11140 test-add-literal-to-eax:
11141     #   var1/eax <- add 0x34
11142     # =>
11143     #   05/add-to-eax 0x34/imm32
11144     #
11145     # . prologue
11146     55/push-ebp
11147     89/<- %ebp 4/r32/esp
11148     # setup
11149     (clear-stream _test-output-stream)
11150     (clear-stream $_test-output-buffered-file->buffer)
11151     # var type/ecx: (handle tree type-id) = int
11152     68/push 0/imm32/right/null
11153     68/push 1/imm32/left/int
11154     68/push 1/imm32/is-atom
11155     89/<- %ecx 4/r32/esp
11156     # var var-var1/ecx: var in eax
11157     68/push "eax"/imm32/register
11158     68/push 0/imm32/no-stack-offset
11159     68/push 1/imm32/block-depth
11160     51/push-ecx
11161     68/push "var1"/imm32
11162     89/<- %ecx 4/r32/esp
11163     # var type/edx: (handle tree type-id) = literal
11164     68/push 0/imm32/right/null
11165     68/push 0/imm32/left/literal
11166     89/<- %edx 4/r32/esp
11167     # var var-var2/edx: var literal
11168     68/push 0/imm32/no-register
11169     68/push 0/imm32/no-stack-offset
11170     68/push 1/imm32/block-depth
11171     52/push-edx
11172     68/push "0x34"/imm32
11173     89/<- %edx 4/r32/esp
11174     # var inouts/esi: (handle stmt-var) = [var2]
11175     68/push 0/imm32/is-deref:false
11176     68/push 0/imm32/next
11177     52/push-edx/var-var2
11178     89/<- %esi 4/r32/esp
11179     # var outputs/edi: (handle stmt-var) = [var1]
11180     68/push 0/imm32/is-deref:false
11181     68/push 0/imm32/next
11182     51/push-ecx/var-var1
11183     89/<- %edi 4/r32/esp
11184     # var stmt/esi: statement
11185     68/push 0/imm32/next
11186     57/push-edi/outputs
11187     56/push-esi/inouts
11188     68/push "add"/imm32/operation
11189     68/push 1/imm32
11190     89/<- %esi 4/r32/esp
11191     # convert
11192     c7 0/subop/copy *Curr-block-depth 0/imm32
11193     (emit-subx-stmt _test-output-buffered-file %esi Primitives)
11194     (flush _test-output-buffered-file)
11195 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
11201     # check output
11202     (check-next-stream-line-equal _test-output-stream "05/add-to-eax 0x34/imm32" "F - test-add-literal-to-eax")
11203     # . epilogue
11204     89/<- %esp 5/r32/ebp
11205     5d/pop-to-ebp
11206     c3/return
11207 
11208 test-add-literal-to-reg:
11209     #   var1/ecx <- add 0x34
11210     # =>
11211     #   81 0/subop/add %ecx 0x34/imm32
11212     #
11213     # . prologue
11214     55/push-ebp
11215     89/<- %ebp 4/r32/esp
11216     # setup
11217     (clear-stream _test-output-stream)
11218     (clear-stream $_test-output-buffered-file->buffer)
11219     # var type/ecx: (handle tree type-id) = int
11220     68/push 0/imm32/right/null
11221     68/push 1/imm32/left/int
11222     68/push 1/imm32/is-atom
11223     89/<- %ecx 4/r32/esp
11224     # var var-var1/ecx: var in ecx
11225     68/push "ecx"/imm32/register
11226     68/push 0/imm32/no-stack-offset
11227     68/push 1/imm32/block-depth
11228     51/push-ecx
11229     68/push "var1"/imm32
11230     89/<- %ecx 4/r32/esp
11231     # var type/edx: (handle tree type-id) = literal
11232     68/push 0/imm32/right/null
11233     68/push 0/imm32/left/literal
11234     89/<- %edx 4/r32/esp
11235     # var var-var2/edx: var literal
11236     68/push 0/imm32/no-register
11237     68/push 0/imm32/no-stack-offset
11238     68/push 1/imm32/block-depth
11239     52/push-edx
11240     68/push "0x34"/imm32
11241     89/<- %edx 4/r32/esp
11242     # var inouts/esi: (handle stmt-var) = [var2]
11243     68/push 0/imm32/is-deref:false
11244     68/push 0/imm32/next
11245     52/push-edx/var-var2
11246     89/<- %esi 4/r32/esp
11247     # var outputs/edi: (handle stmt-var) = [var1]
11248     68/push 0/imm32/is-deref:false
11249     68/push 0/imm32/next
11250     51/push-ecx/var-var1
11251     89/<- %edi 4/r32/esp
11252     # var stmt/esi: statement
11253     68/push 0/imm32/next
11254     57/push-edi/outputs
11255     56/push-esi/inouts
11256     68/push "add"/imm32/operation
11257     68/push 1/imm32
11258     89/<- %esi 4/r32/esp
11259     # convert
11260     c7 0/subop/copy *Curr-block-depth 0/imm32
11261     (emit-subx-stmt _test-output-buffered-file %esi Primitives)
11262     (flush _test-output-buffered-file)
11263 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
11269     # check output
11270     (check-next-stream-line-equal _test-output-stream "81 0/subop/add %ecx 0x34/imm32" "F - test-add-literal-to-reg")
11271     # . epilogue
11272     89/<- %esp 5/r32/ebp
11273     5d/pop-to-ebp
11274     c3/return
11275 
11276 test-add-literal-to-mem:
11277     #   add-to var1, 0x34
11278     # =>
11279     #   81 0/subop/add %eax 0x34/imm32
11280     #
11281     # . prologue
11282     55/push-ebp
11283     89/<- %ebp 4/r32/esp
11284     # setup
11285     (clear-stream _test-output-stream)
11286     (clear-stream $_test-output-buffered-file->buffer)
11287     # var type/ecx: (handle tree type-id) = int
11288     68/push 0/imm32/right/null
11289     68/push 1/imm32/left/int
11290     68/push 1/imm32/is-atom
11291     89/<- %ecx 4/r32/esp
11292     # var var-var1/ecx: var
11293     68/push 0/imm32/no-register
11294     68/push 8/imm32/stack-offset
11295     68/push 1/imm32/block-depth
11296     51/push-ecx
11297     68/push "var1"/imm32
11298     89/<- %ecx 4/r32/esp
11299     # var type/edx: (handle tree type-id) = literal
11300     68/push 0/imm32/right/null
11301     68/push 0/imm32/left/literal
11302     89/<- %edx 4/r32/esp
11303     # var var-var2/edx: var literal
11304     68/push 0/imm32/no-register
11305     68/push 0/imm32/no-stack-offset
11306     68/push 1/imm32/block-depth
11307     52/push-edx
11308     68/push "0x34"/imm32
11309     89/<- %edx 4/r32/esp
11310     # var inouts/esi: (handle stmt-var) = [var2]
11311     68/push 0/imm32/is-deref:false
11312     68/push 0/imm32/next
11313     52/push-edx/var-var2
11314     89/<- %esi 4/r32/esp
11315     # var inouts = (handle stmt-var) = [var1, var2]
11316     68/push 0/imm32/is-deref:false
11317     56/push-esi/next
11318     51/push-ecx/var-var1
11319     89/<- %esi 4/r32/esp
11320     # var stmt/esi: statement
11321     68/push 0/imm32/next
11322     68/push 0/imm32/outputs
11323     56/push-esi/inouts
11324     68/push "add-to"/imm32/operation
11325     68/push 1/imm32
11326     89/<- %esi 4/r32/esp
11327     # convert
11328     c7 0/subop/copy *Curr-block-depth 0/imm32
11329     (emit-subx-stmt _test-output-buffered-file %esi Primitives)
11330     (flush _test-output-buffered-file)
11331 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
11337     # check output
11338     (check-next-stream-line-equal _test-output-stream "81 0/subop/add *(ebp+0x00000008) 0x34/imm32" "F - test-add-literal-to-mem")
11339     # . epilogue
11340     89/<- %esp 5/r32/ebp
11341     5d/pop-to-ebp
11342     c3/return
11343 
11344 test-compare-mem-with-reg:
11345     #   compare var1, var2/eax
11346     # =>
11347     #   39/compare *(ebp+___) 0/r32/eax
11348     #
11349     # . prologue
11350     55/push-ebp
11351     89/<- %ebp 4/r32/esp
11352     # setup
11353     (clear-stream _test-output-stream)
11354     (clear-stream $_test-output-buffered-file->buffer)
11355     # var type/ecx: (handle tree type-id) = int
11356     68/push 0/imm32/right/null
11357     68/push 1/imm32/left/int
11358     68/push 1/imm32/is-atom
11359     89/<- %ecx 4/r32/esp
11360     # var var-var2/ecx: var in eax
11361     68/push "eax"/imm32/register
11362     68/push 0/imm32/no-stack-offset
11363     68/push 1/imm32/block-depth
11364     51/push-ecx
11365     68/push "var2"/imm32
11366     89/<- %ecx 4/r32/esp
11367     # var var-var1/edx: var
11368     68/push 0/imm32/no-register
11369     68/push 8/imm32/stack-offset
11370     68/push 1/imm32/block-depth
11371     ff 6/subop/push *(ecx+4)  # Var-type
11372     68/push "var1"/imm32
11373     89/<- %edx 4/r32/esp
11374     # var inouts/esi: (handle stmt-var) = [var2]
11375     68/push 0/imm32/is-deref:false
11376     68/push 0/imm32/next
11377     51/push-ecx/var-var2
11378     89/<- %esi 4/r32/esp
11379     # inouts = [var1, var2]
11380     68/push 0/imm32/is-deref:false
11381     56/push-esi
11382     52/push-edx/var-var1
11383     89/<- %esi 4/r32/esp
11384     # var stmt/esi: statement
11385     68/push 0/imm32/next
11386     68/push 0/imm32/outputs
11387     56/push-esi/inouts
11388     68/push "compare"/imm32/operation
11389     68/push 1/imm32
11390     89/<- %esi 4/r32/esp
11391     # convert
11392     c7 0/subop/copy *Curr-block-depth 0/imm32
11393     (emit-subx-stmt _test-output-buffered-file %esi Primitives)
11394     (flush _test-output-buffered-file)
11395 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
11401     # check output
11402     (check-next-stream-line-equal _test-output-stream "39/compare-> *(ebp+0x00000008) 0x00000000/r32" "F - test-compare-mem-with-reg")
11403     # . epilogue
11404     89/<- %esp 5/r32/ebp
11405     5d/pop-to-ebp
11406     c3/return
11407 
11408 test-compare-reg-with-mem:
11409     #   compare var1/eax, var2
11410     # =>
11411     #   3b/compare<- *(ebp+___) 0/r32/eax
11412     #
11413     # . prologue
11414     55/push-ebp
11415     89/<- %ebp 4/r32/esp
11416     # setup
11417     (clear-stream _test-output-stream)
11418     (clear-stream $_test-output-buffered-file->buffer)
11419     # var type/ecx: (handle tree type-id) = int
11420     68/push 0/imm32/right/null
11421     68/push 1/imm32/left/int
11422     68/push 1/imm32/is-atom
11423     89/<- %ecx 4/r32/esp
11424     # var var-var1/ecx: var in eax
11425     68/push "eax"/imm32/register
11426     68/push 0/imm32/no-stack-offset
11427     68/push 1/imm32/block-depth
11428     51/push-ecx
11429     68/push "var1"/imm32
11430     89/<- %ecx 4/r32/esp
11431     # var var-var2/edx: var
11432     68/push 0/imm32/no-register
11433     68/push 8/imm32/stack-offset
11434     68/push 1/imm32/block-depth
11435     ff 6/subop/push *(ecx+4)  # Var-type
11436     68/push "var2"/imm32
11437     89/<- %edx 4/r32/esp
11438     # var inouts/esi: (handle stmt-var) = [var2]
11439     68/push 0/imm32/is-deref:false
11440     68/push 0/imm32/next
11441     52/push-edx/var-var2
11442     89/<- %esi 4/r32/esp
11443     # inouts = [var1, var2]
11444     68/push 0/imm32/is-deref:false
11445     56/push-esi
11446     51/push-ecx/var-var1
11447     89/<- %esi 4/r32/esp
11448     # var stmt/esi: statement
11449     68/push 0/imm32/next
11450     68/push 0/imm32/outputs
11451     56/push-esi/inouts
11452     68/push "compare"/imm32/operation
11453     68/push 1/imm32
11454     89/<- %esi 4/r32/esp
11455     # convert
11456     c7 0/subop/copy *Curr-block-depth 0/imm32
11457     (emit-subx-stmt _test-output-buffered-file %esi Primitives)
11458     (flush _test-output-buffered-file)
11459 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
11465     # check output
11466     (check-next-stream-line-equal _test-output-stream "3b/compare<- *(ebp+0x00000008) 0x00000000/r32" "F - test-compare-reg-with-mem")
11467     # . epilogue
11468     89/<- %esp 5/r32/ebp
11469     5d/pop-to-ebp
11470     c3/return
11471 
11472 test-compare-mem-with-literal:
11473     #   compare var1, 0x34
11474     # =>
11475     #   81 7/subop/compare *(ebp+___) 0x34/imm32
11476     #
11477     # . prologue
11478     55/push-ebp
11479     89/<- %ebp 4/r32/esp
11480     # setup
11481     (clear-stream _test-output-stream)
11482     (clear-stream $_test-output-buffered-file->buffer)
11483     # var type/ecx: (handle tree type-id) = int
11484     68/push 0/imm32/right/null
11485     68/push 1/imm32/left/int
11486     68/push 1/imm32/is-atom
11487     89/<- %ecx 4/r32/esp
11488     # var var-var1/ecx: var
11489     68/push 0/imm32/no-register
11490     68/push 8/imm32/stack-offset
11491     68/push 1/imm32/block-depth
11492     51/push-ecx
11493     68/push "var1"/imm32
11494     89/<- %ecx 4/r32/esp
11495     # var type/edx: (handle tree type-id) = literal
11496     68/push 0/imm32/right/null
11497     68/push 0/imm32/left/literal
11498     89/<- %edx 4/r32/esp
11499     # var var-var2/edx: var literal
11500     68/push 0/imm32/no-register
11501     68/push 0/imm32/no-stack-offset
11502     68/push 1/imm32/block-depth
11503     52/push-edx
11504     68/push "0x34"/imm32
11505     89/<- %edx 4/r32/esp
11506     # var inouts/esi: (handle stmt-var) = [var2]
11507     68/push 0/imm32/is-deref:false
11508     68/push 0/imm32/next
11509     52/push-edx/var-var2
11510     89/<- %esi 4/r32/esp
11511     # inouts = [var1, var2]
11512     68/push 0/imm32/is-deref:false
11513     56/push-esi/next
11514     51/push-ecx/var-var1
11515     89/<- %esi 4/r32/esp
11516     # var stmt/esi: statement
11517     68/push 0/imm32/next
11518     68/push 0/imm32/outputs
11519     56/push-esi/inouts
11520     68/push "compare"/imm32/operation
11521     68/push 1/imm32
11522     89/<- %esi 4/r32/esp
11523     # convert
11524     c7 0/subop/copy *Curr-block-depth 0/imm32
11525     (emit-subx-stmt _test-output-buffered-file %esi Primitives)
11526     (flush _test-output-buffered-file)
11527 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
11533     # check output
11534     (check-next-stream-line-equal _test-output-stream "81 7/subop/compare *(ebp+0x00000008) 0x34/imm32" "F - test-compare-mem-with-literal")
11535     # . epilogue
11536     89/<- %esp 5/r32/ebp
11537     5d/pop-to-ebp
11538     c3/return
11539 
11540 test-compare-eax-with-literal:
11541     #   compare var1/eax 0x34
11542     # =>
11543     #   3d/compare-eax-with 0x34/imm32
11544     #
11545     # . prologue
11546     55/push-ebp
11547     89/<- %ebp 4/r32/esp
11548     # setup
11549     (clear-stream _test-output-stream)
11550     (clear-stream $_test-output-buffered-file->buffer)
11551     # var type/ecx: (handle tree type-id) = int
11552     68/push 0/imm32/right/null
11553     68/push 1/imm32/left/int
11554     68/push 1/imm32/is-atom
11555     89/<- %ecx 4/r32/esp
11556     # var var-var1/ecx: var in eax
11557     68/push "eax"/imm32/register
11558     68/push 0/imm32/no-stack-offset
11559     68/push 1/imm32/block-depth
11560     51/push-ecx
11561     68/push "var1"/imm32
11562     89/<- %ecx 4/r32/esp
11563     # var type/edx: (handle tree type-id) = literal
11564     68/push 0/imm32/right/null
11565     68/push 0/imm32/left/literal
11566     89/<- %edx 4/r32/esp
11567     # var var-var2/edx: var literal
11568     68/push 0/imm32/no-register
11569     68/push 0/imm32/no-stack-offset
11570     68/push 1/imm32/block-depth
11571     52/push-edx
11572     68/push "0x34"/imm32
11573     89/<- %edx 4/r32/esp
11574     # var inouts/esi: (handle stmt-var) = [var2]
11575     68/push 0/imm32/is-deref:false
11576     68/push 0/imm32/next
11577     52/push-edx/var-var2
11578     89/<- %esi 4/r32/esp
11579     # inouts = [var1, var2]
11580     68/push 0/imm32/is-deref:false
11581     56/push-esi/next
11582     51/push-ecx/var-var1
11583     89/<- %esi 4/r32/esp
11584     # var stmt/esi: statement
11585     68/push 0/imm32/next
11586     68/push 0/imm32/outputs
11587     56/push-esi/inouts
11588     68/push "compare"/imm32/operation
11589     68/push 1/imm32/regular-stmt
11590     89/<- %esi 4/r32/esp
11591     # convert
11592     c7 0/subop/copy *Curr-block-depth 0/imm32
11593     (emit-subx-stmt _test-output-buffered-file %esi Primitives)
11594     (flush _test-output-buffered-file)
11595 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
11601     # check output
11602     (check-next-stream-line-equal _test-output-stream "3d/compare-eax-with 0x34/imm32" "F - test-compare-eax-with-literal")
11603     # . epilogue
11604     89/<- %esp 5/r32/ebp
11605     5d/pop-to-ebp
11606     c3/return
11607 
11608 test-compare-reg-with-literal:
11609     #   compare var1/ecx 0x34
11610     # =>
11611     #   81 7/subop/compare %ecx 0x34/imm32
11612     #
11613     # . prologue
11614     55/push-ebp
11615     89/<- %ebp 4/r32/esp
11616     # setup
11617     (clear-stream _test-output-stream)
11618     (clear-stream $_test-output-buffered-file->buffer)
11619     # var type/ecx: (handle tree type-id) = int
11620     68/push 0/imm32/right/null
11621     68/push 1/imm32/left/int
11622     68/push 1/imm32/is-atom
11623     89/<- %ecx 4/r32/esp
11624     # var var-var1/ecx: var in ecx
11625     68/push "ecx"/imm32/register
11626     68/push 0/imm32/no-stack-offset
11627     68/push 1/imm32/block-depth
11628     51/push-ecx
11629     68/push "var1"/imm32
11630     89/<- %ecx 4/r32/esp
11631     # var type/edx: (handle tree type-id) = literal
11632     68/push 0/imm32/right/null
11633     68/push 0/imm32/left/literal
11634     89/<- %edx 4/r32/esp
11635     # var var-var2/edx: var literal
11636     68/push 0/imm32/no-register
11637     68/push 0/imm32/no-stack-offset
11638     68/push 1/imm32/block-depth
11639     52/push-edx
11640     68/push "0x34"/imm32
11641     89/<- %edx 4/r32/esp
11642     # var inouts/esi: (handle stmt-var) = [var2]
11643     68/push 0/imm32/is-deref:false
11644     68/push 0/imm32/next
11645     52/push-edx/var-var2
11646     89/<- %esi 4/r32/esp
11647     # inouts = [var1, var2]
11648     68/push 0/imm32/is-deref:false
11649     56/push-esi/next
11650     51/push-ecx/var-var1
11651     89/<- %esi 4/r32/esp
11652     # var stmt/esi: statement
11653     68/push 0/imm32/next
11654     68/push 0/imm32/outputs
11655     56/push-esi/inouts
11656     68/push "compare"/imm32/operation
11657     68/push 1/imm32/regular-stmt
11658     89/<- %esi 4/r32/esp
11659     # convert
11660     c7 0/subop/copy *Curr-block-depth 0/imm32
11661     (emit-subx-stmt _test-output-buffered-file %esi Primitives)
11662     (flush _test-output-buffered-file)
11663 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
11669     # check output
11670     (check-next-stream-line-equal _test-output-stream "81 7/subop/compare %ecx 0x34/imm32" "F - test-compare-reg-with-literal")
11671     # . epilogue
11672     89/<- %esp 5/r32/ebp
11673     5d/pop-to-ebp
11674     c3/return
11675 
11676 test-emit-subx-stmt-function-call:
11677     # Call a function on a variable on the stack.
11678     #   f foo
11679     # =>
11680     #   (f *(ebp-8))
11681     # (Changing the function name supports overloading in general, but here it
11682     # just serves to help disambiguate things.)
11683     #
11684     # There's a variable on the var stack as follows:
11685     #   name: 'foo'
11686     #   type: int
11687     #   stack-offset: -8
11688     #
11689     # There's nothing in primitives.
11690     #
11691     # We don't perform any checking here on the type of 'f'.
11692     #
11693     # . prologue
11694     55/push-ebp
11695     89/<- %ebp 4/r32/esp
11696     # setup
11697     (clear-stream _test-output-stream)
11698     (clear-stream $_test-output-buffered-file->buffer)
11699     # var type/ecx: (handle tree type-id) = int
11700     68/push 0/imm32/right/null
11701     68/push 1/imm32/left/int
11702     68/push 1/imm32/is-atom
11703     89/<- %ecx 4/r32/esp
11704     # var var-foo/ecx: var
11705     68/push 0/imm32/no-register
11706     68/push -8/imm32/stack-offset
11707     68/push 0/imm32/block-depth
11708     51/push-ecx
11709     68/push "foo"/imm32
11710     89/<- %ecx 4/r32/esp
11711     # var inouts/esi: (handle stmt-var)
11712     68/push 0/imm32/is-deref:false
11713     68/push 0/imm32/next
11714     51/push-ecx/var-foo
11715     89/<- %esi 4/r32/esp
11716     # var stmt/esi: statement
11717     68/push 0/imm32/next
11718     68/push 0/imm32/outputs
11719     56/push-esi/inouts
11720     68/push "f"/imm32/operation
11721     68/push 1/imm32
11722     89/<- %esi 4/r32/esp
11723     # convert
11724     c7 0/subop/copy *Curr-block-depth 0/imm32
11725     (emit-subx-stmt _test-output-buffered-file %esi 0)
11726     (flush _test-output-buffered-file)
11727 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
11733     # check output
11734     (check-next-stream-line-equal _test-output-stream "(f *(ebp+0xfffffff8))" "F - test-emit-subx-stmt-function-call")
11735     # . epilogue
11736     89/<- %esp 5/r32/ebp
11737     5d/pop-to-ebp
11738     c3/return
11739 
11740 test-emit-subx-stmt-function-call-with-literal-arg:
11741     # Call a function on a literal.
11742     #   f 34
11743     # =>
11744     #   (f2 34)
11745     #
11746     # . prologue
11747     55/push-ebp
11748     89/<- %ebp 4/r32/esp
11749     # setup
11750     (clear-stream _test-output-stream)
11751     (clear-stream $_test-output-buffered-file->buffer)
11752     # var type/ecx: (handle tree type-id) = literal
11753     68/push 0/imm32/right/null
11754     68/push 0/imm32/left/literal
11755     89/<- %ecx 4/r32/esp
11756     # var var-foo/ecx: var literal
11757     68/push 0/imm32/no-register
11758     68/push 0/imm32/no-stack-offset
11759     68/push 0/imm32/block-depth
11760     51/push-ecx
11761     68/push "34"/imm32
11762     89/<- %ecx 4/r32/esp
11763     # var inouts/esi: (handle stmt-var)
11764     68/push 0/imm32/is-deref:false
11765     68/push 0/imm32/next
11766     51/push-ecx/var-foo
11767     89/<- %esi 4/r32/esp
11768     # var stmt/esi: statement
11769     68/push 0/imm32/next
11770     68/push 0/imm32/outputs
11771     56/push-esi/inouts
11772     68/push "f"/imm32/operation
11773     68/push 1/imm32
11774     89/<- %esi 4/r32/esp
11775     # convert
11776     c7 0/subop/copy *Curr-block-depth 0/imm32
11777     (emit-subx-stmt _test-output-buffered-file %esi 0 %ebx)
11778     (flush _test-output-buffered-file)
11779 +--  6 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
11785     # check output
11786     (check-next-stream-line-equal _test-output-stream "(f 34)" "F - test-emit-subx-stmt-function-call-with-literal-arg")
11787     # . epilogue
11788     89/<- %esp 5/r32/ebp
11789     5d/pop-to-ebp
11790     c3/return
11791 
11792 emit-indent:  # out: (addr buffered-file), n: int
11793     # . prologue
11794     55/push-ebp
11795     89/<- %ebp 4/r32/esp
11796     # . save registers
11797     50/push-eax
11798     # var i/eax: int = n
11799     8b/-> *(ebp+0xc) 0/r32/eax
11800     {
11801       # if (i <= 0) break
11802       3d/compare-eax-with 0/imm32
11803       7e/jump-if-<= break/disp8
11804       (write-buffered *(ebp+8) "  ")
11805       48/decrement-eax
11806       eb/jump loop/disp8
11807     }
11808 $emit-indent:end:
11809     # . restore registers
11810     58/pop-to-eax
11811     # . epilogue
11812     89/<- %esp 5/r32/ebp
11813     5d/pop-to-ebp
11814     c3/return
11815 
11816 emit-subx-prologue:  # out: (addr buffered-file)
11817     # . prologue
11818     55/push-ebp
11819     89/<- %ebp 4/r32/esp
11820     #
11821     (write-buffered *(ebp+8) "  # . prologue\n")
11822     (write-buffered *(ebp+8) "  55/push-ebp\n")
11823     (write-buffered *(ebp+8) "  89/<- %ebp 4/r32/esp\n")
11824 $emit-subx-prologue:end:
11825     # . epilogue
11826     89/<- %esp 5/r32/ebp
11827     5d/pop-to-ebp
11828     c3/return
11829 
11830 emit-subx-epilogue:  # out: (addr buffered-file)
11831     # . prologue
11832     55/push-ebp
11833     89/<- %ebp 4/r32/esp
11834     #
11835     (write-buffered *(ebp+8) "  # . epilogue\n")
11836     (write-buffered *(ebp+8) "  89/<- %esp 5/r32/ebp\n")
11837     (write-buffered *(ebp+8) "  5d/pop-to-ebp\n")
11838     (write-buffered *(ebp+8) "  c3/return\n")
11839 $emit-subx-epilogue:end:
11840     # . epilogue
11841     89/<- %esp 5/r32/ebp
11842     5d/pop-to-ebp
11843     c3/return